"core/java/android/app/IInstrumentationWatcher.aidl",
"core/java/android/app/INotificationManager.aidl",
"core/java/android/app/IProcessObserver.aidl",
+ "core/java/android/app/IRequestFinishCallback.aidl",
"core/java/android/app/ISearchManager.aidl",
"core/java/android/app/ISearchManagerCallback.aidl",
"core/java/android/app/IServiceConnection.aidl",
FragmentManager fragmentManager = mFragments.getFragmentManager();
- if (fragmentManager.isStateSaved() || !fragmentManager.popBackStackImmediate()) {
+ if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) {
+ return;
+ }
+ if (!isTaskRoot()) {
+ // If the activity is not the root of the task, allow finish to proceed normally.
+ finishAfterTransition();
+ return;
+ }
+ try {
+ // Inform activity task manager that the activity received a back press
+ // while at the root of the task. This call allows ActivityTaskManager
+ // to intercept or defer finishing.
+ ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken,
+ new IRequestFinishCallback.Stub() {
+ public void requestFinish() {
+ finishAfterTransition();
+ }
+ });
+ } catch (RemoteException e) {
finishAfterTransition();
}
}
import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
+import android.app.IRequestFinishCallback;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.ITaskStackListener;
* @param activityToken The token of the target activity to restart.
*/
void restartActivityProcessIfVisible(in IBinder activityToken);
+
+ /**
+ * Reports that an Activity received a back key press when there were no additional activities
+ * on the back stack. If the Activity should be finished, the callback will be invoked. A
+ * callback is used instead of finishing the activity directly from the server such that the
+ * client may perform actions prior to finishing.
+ */
+ void onBackPressedOnTaskRoot(in IBinder activityToken, in IRequestFinishCallback callback);
}
--- /dev/null
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * This callback allows ActivityTaskManager to ask the calling Activity
+ * to finish in response to a call to onBackPressedOnTaskRoot.
+ *
+ * {@hide}
+ */
+oneway interface IRequestFinishCallback {
+ void requestFinish();
+}
* @see com.android.server.wm.AppWindowToken#inSizeCompatMode
*/
void onSizeCompatModeActivityChanged(int displayId, in IBinder activityToken);
+
+ /**
+ * Reports that an Activity received a back key press when there were no additional activities
+ * on the back stack.
+ *
+ * @param taskInfo info about the task which received the back press
+ */
+ void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
}
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
throws RemoteException {
}
+
+ @Override
+ public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo)
+ throws RemoteException {
+ }
}
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
+ public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }
+
/**
* Checks that the current user matches the process. Since
* {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
}
@Override
+ public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException {
+ mHandler.obtainMessage(H.ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
+ }
+
+ @Override
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
throws RemoteException {
mHandler.obtainMessage(H.ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 15;
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
+ private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
public H(Looper looper) {
}
break;
}
+ case ON_BACK_PRESSED_ON_TASK_ROOT: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
+ (RunningTaskInfo) msg.obj);
+ }
+ }
}
}
}
mBubbleData.setExpanded(false);
}
}
+
+ @Override
+ public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+ if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) {
+ mBubbleData.setExpanded(false);
+ }
+ }
}
private static boolean shouldAutoBubbleMessages(Context context) {
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
+import android.app.IRequestFinishCallback;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
}
}
+ @Override
+ public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
+ synchronized (mGlobalLock) {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ ActivityStack stack = r.getActivityStack();
+ if (stack != null && stack.isSingleTaskInstance()) {
+ // Single-task stacks are used for activities which are presented in floating
+ // windows above full screen activities. Instead of directly finishing the
+ // task, a task change listener is used to notify SystemUI so the action can be
+ // handled specially.
+ final TaskRecord task = r.getTaskRecord();
+ mTaskChangeNotificationController
+ .notifyBackPressedOnTaskRoot(task.getTaskInfo());
+ } else {
+ try {
+ callback.requestFinish();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke request finish callback", e);
+ }
+ }
+ }
+ }
+
/**
* TODO: Add mController hook
*/
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
+ private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
l.onTaskDescriptionChanged((RunningTaskInfo) m.obj);
};
+ private final TaskStackConsumer mNotifyBackPressedOnTaskRoot = (l, m) -> {
+ l.onBackPressedOnTaskRoot((RunningTaskInfo) m.obj);
+ };
+
private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> {
l.onActivityRequestedOrientationChanged(m.arg1, m.arg2);
};
case NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG:
forAllRemoteListeners(mOnSizeCompatModeActivityChanged, msg);
break;
+ case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
+ forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
+ break;
}
}
}
forAllLocalListeners(mOnSizeCompatModeActivityChanged, msg);
msg.sendToTarget();
}
+
+ /**
+ * Notify listeners that an activity received a back press when there are no other activities
+ * in the back stack.
+ */
+ void notifyBackPressedOnTaskRoot(TaskInfo taskInfo) {
+ final Message msg = mHandler.obtainMessage(NOTIFY_BACK_PRESSED_ON_TASK_ROOT,
+ taskInfo);
+ forAllLocalListeners(mNotifyBackPressedOnTaskRoot, msg);
+ msg.sendToTarget();
+ }
}