Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5db29137 authored by Jeff Chang's avatar Jeff Chang
Browse files

Report the failure back to the organizer

 - create taskfragment
 - startActivity in taskfragment
 - reparent children
 - delete taskfragment

Bug: 189385246
Test: atest TaskFragmentOrganizerControllerTest
Change-Id: Ibddb6d9f83e707ac8a27b117777cc3ddc45bbef9
parent 290294d3
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -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())
+50 −5
Original line number Diff line number Diff line
@@ -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;

        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);
        }

        PendingTaskFragmentEvent(TaskFragment taskFragment, ITaskFragmentOrganizer taskFragmentOrg,
        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();
+57 −20
Original line number Diff line number Diff line
@@ -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);
    }
}
+4 −5
Original line number Diff line number Diff line
@@ -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));
    }