diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index d08d285beda8d..c3cd61ac8678c 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -490,9 +490,16 @@ public class ActivityStartController { return START_SUCCESS; } - void startActivityInTaskFragment(@NonNull TaskFragment taskFragment, + /** + * Starts an activity in the TaskFragment. + * @param taskFragment TaskFragment {@link TaskFragment} to start the activity in. + * @param activityIntent intent to start the activity. + * @param activityOptions ActivityOptions to start the activity with. + * @return the start result. + */ + int startActivityInTaskFragment(@NonNull TaskFragment taskFragment, @NonNull Intent activityIntent, @Nullable Bundle activityOptions) { - obtainStarter(activityIntent, "startActivityInTaskFragment") + return obtainStarter(activityIntent, "startActivityInTaskFragment") .setActivityOptions(activityOptions) .setInTaskFragment(taskFragment) .setCallingUid(Binder.getCallingUid()) diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 496ecdedcaf71..4843e5a4dc084 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.window.TaskFragmentOrganizer.putExceptionInBundle; + import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer; @@ -23,6 +25,7 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.content.res.Configuration; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; @@ -190,6 +193,16 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr Slog.e(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e); } } + + void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken, + Throwable exception) { + final Bundle exceptionBundle = putExceptionInBundle(exception); + try { + organizer.onTaskFragmentError(errorCallbackToken, exceptionBundle); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskFragmentError callback", e); + } + } } @Override @@ -289,6 +302,14 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr state.removeTaskFragment(taskFragment); } + void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken, + Throwable exception) { + validateAndGetState(organizer); + PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(organizer, + errorCallbackToken, exception, PendingTaskFragmentEvent.EVENT_ERROR); + mPendingTaskFragmentEvents.add(pendingEvent); + } + private void removeOrganizer(ITaskFragmentOrganizer organizer) { final TaskFragmentOrganizerState state = validateAndGetState(organizer); // remove all of the children of the organized TaskFragment @@ -321,25 +342,45 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr static final int EVENT_VANISHED = 1; static final int EVENT_INFO_CHANGED = 2; static final int EVENT_PARENT_INFO_CHANGED = 3; + static final int EVENT_ERROR = 4; @IntDef(prefix = "EVENT_", value = { EVENT_APPEARED, EVENT_VANISHED, EVENT_INFO_CHANGED, - EVENT_PARENT_INFO_CHANGED + EVENT_PARENT_INFO_CHANGED, + EVENT_ERROR }) @Retention(RetentionPolicy.SOURCE) public @interface EventType {} @EventType - final int mEventType; - final TaskFragment mTaskFragment; - final ITaskFragmentOrganizer mTaskFragmentOrg; + private final int mEventType; + private final ITaskFragmentOrganizer mTaskFragmentOrg; + private final TaskFragment mTaskFragment; + private final IBinder mErrorCallback; + private final Throwable mException; - PendingTaskFragmentEvent(TaskFragment taskFragment, ITaskFragmentOrganizer taskFragmentOrg, + private PendingTaskFragmentEvent(TaskFragment taskFragment, + ITaskFragmentOrganizer taskFragmentOrg, @EventType int eventType) { + this(taskFragment, taskFragmentOrg, null /* errorCallback */, + null /* exception */, eventType); + + } + + private PendingTaskFragmentEvent(ITaskFragmentOrganizer taskFragmentOrg, + IBinder errorCallback, Throwable exception, @EventType int eventType) { + this(null /* taskFragment */, taskFragmentOrg, errorCallback, exception, + eventType); + } + + private PendingTaskFragmentEvent(TaskFragment taskFragment, + ITaskFragmentOrganizer taskFragmentOrg, IBinder errorCallback, Throwable exception, @EventType int eventType) { mTaskFragment = taskFragment; mTaskFragmentOrg = taskFragmentOrg; + mErrorCallback = errorCallback; + mException = exception; mEventType = eventType; } @@ -407,6 +448,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr break; case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED: state.onTaskFragmentParentInfoChanged(taskFragmentOrg, taskFragment); + break; + case PendingTaskFragmentEvent.EVENT_ERROR: + state.onTaskFragmentError(taskFragmentOrg, event.mErrorCallback, + event.mException); } } mPendingTaskFragmentEvents.clear(); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 58f7a550020bf..e31b1302a8918 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.app.ActivityManager.isStartResultSuccessful; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; @@ -44,6 +45,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.WindowConfiguration; +import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -369,7 +371,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final boolean isInLockTaskMode = mService.isInLockTaskMode(); for (int i = 0; i < hopSize; ++i) { effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition, - isInLockTaskMode, caller); + isInLockTaskMode, caller, t.getErrorCallbackToken(), + t.getTaskFragmentOrganizer()); } } // Queue-up bounds-change transactions for tasks which are now organized. Do @@ -525,7 +528,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, int syncId, @Nullable Transition transition, boolean isInLockTaskMode, - @Nullable CallerInfo caller) { + @Nullable CallerInfo caller, @Nullable IBinder errorCallbackToken, + @Nullable ITaskFragmentOrganizer organizer) { final int type = hop.getType(); switch (type) { case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { @@ -652,7 +656,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: final TaskFragmentCreationParams taskFragmentCreationOptions = hop.getTaskFragmentCreationOptions(); - createTaskFragment(taskFragmentCreationOptions); + createTaskFragment(taskFragmentCreationOptions, errorCallbackToken); break; case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: wc = WindowContainer.fromBinder(hop.getContainer()); @@ -665,28 +669,36 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new IllegalArgumentException( "Can only delete organized TaskFragment, but not Task."); } - deleteTaskFragment(taskFragment); + deleteTaskFragment(taskFragment, errorCallbackToken); break; case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: fragmentToken = hop.getContainer(); if (!mLaunchTaskFragments.containsKey(fragmentToken)) { - throw new IllegalArgumentException( + final Throwable exception = new IllegalArgumentException( "Not allowed to operate with invalid fragment token"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; } final Intent activityIntent = hop.getActivityIntent(); final Bundle activityOptions = hop.getLaunchOptions(); - mService.getActivityStartController() - .startActivityInTaskFragment(mLaunchTaskFragments.get(fragmentToken), - activityIntent, activityOptions); - // TODO(b/189385246) : report the failure back to the organizer if the activity - // start failed + final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); + final int result = mService.getActivityStartController() + .startActivityInTaskFragment(tf, activityIntent, activityOptions); + if (!isStartResultSuccessful(result)) { + final Throwable exception = + new ActivityNotFoundException("start activity in taskFragment failed"); + sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(), + errorCallbackToken, exception); + } break; case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: fragmentToken = hop.getNewParent(); final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) { - throw new IllegalArgumentException( + final Throwable exception = new IllegalArgumentException( "Not allowed to operate with invalid fragment token or activity."); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; } activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP); break; @@ -700,7 +712,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + oldParent); break; } - reparentTaskFragment(oldParent, newParent); + reparentTaskFragment(oldParent, newParent, errorCallbackToken); break; } return effects; @@ -1076,17 +1088,25 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } - void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams) { + void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams, + @Nullable IBinder errorCallbackToken) { final ActivityRecord ownerActivity = ActivityRecord.forTokenLocked(creationParams.getOwnerToken()); if (ownerActivity == null || ownerActivity.getTask() == null) { - // TODO(b/189385246) : report the failure back to the organizer + final Throwable exception = + new IllegalArgumentException("Not allowed to operate with invalid ownerToken"); + sendTaskFragmentOperationFailure(creationParams.getOrganizer(), errorCallbackToken, + exception); return; } // The ownerActivity has to belong to the same app as the root Activity of the target Task. final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity(); if (rootActivity.getUid() != ownerActivity.getUid()) { - // TODO(b/189385246) : report the failure back to the organizer + final Throwable exception = + new IllegalArgumentException("Not allowed to operate with the ownerToken while " + + "the root activity of the target task belong to the different app"); + sendTaskFragmentOperationFailure(creationParams.getOrganizer(), errorCallbackToken, + exception); return; } final TaskFragment taskFragment = new TaskFragment(mService, @@ -1102,13 +1122,16 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } void reparentTaskFragment(@NonNull WindowContainer oldParent, - @Nullable WindowContainer newParent) { + @Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken) { WindowContainer parent = newParent; if (parent == null && oldParent.asTaskFragment() != null) { parent = oldParent.asTaskFragment().getTask(); } if (parent == null) { - // TODO(b/189385246) : report the failure back to the organizer + final Throwable exception = + new IllegalArgumentException("Not allowed to operate with invalid container"); + sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(), + errorCallbackToken, exception); return; } while (oldParent.hasChild()) { @@ -1116,11 +1139,16 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } - void deleteTaskFragment(@NonNull TaskFragment taskFragment) { + void deleteTaskFragment(@NonNull TaskFragment taskFragment, + @Nullable IBinder errorCallbackToken) { final int index = mLaunchTaskFragments.indexOfValue(taskFragment); if (index < 0) { - throw new IllegalArgumentException( - "Not allowed to operate with invalid taskFragment"); + final Throwable exception = + new IllegalArgumentException("Not allowed to operate with invalid " + + "taskFragment"); + sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(), + errorCallbackToken, exception); + return; } mLaunchTaskFragments.removeAt(index); taskFragment.removeImmediately(); @@ -1135,4 +1163,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mUid = Binder.getCallingUid(); } } + + void sendTaskFragmentOperationFailure(@NonNull ITaskFragmentOrganizer organizer, + @Nullable IBinder errorCallbackToken, @NonNull Throwable exception) { + if (organizer == null) { + throw new IllegalArgumentException("Not allowed to operate with invalid organizer"); + } + mService.mTaskFragmentOrganizerController + .onTaskFragmentError(organizer, errorCallbackToken, exception); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index ac981cf6c7cf8..3dd3e81006eff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.window.TaskFragmentOrganizer.putExceptionInBundle; - import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.testing.Assert.assertThrows; @@ -35,7 +33,6 @@ import android.content.Intent; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; -import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -198,9 +195,11 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { public void testOnTaskFragmentError() throws RemoteException { final IBinder errorCallbackToken = new Binder(); final Throwable exception = new IllegalArgumentException("Test exception"); - final Bundle exceptionBundle = putExceptionInBundle(exception); - mIOrganizer.onTaskFragmentError(errorCallbackToken, exceptionBundle); + mController.registerOrganizer(mIOrganizer); + mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(), + errorCallbackToken, exception); + mController.dispatchPendingEvents(); verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception)); }