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

Commit def158c9 authored by Jiaming Liu's avatar Jiaming Liu
Browse files

Allow force hidden and focusable changes on TaskFragment

Bug: 284050041
Test: atest WindowOrganizerTests  TaskFragmentOrganizerControllerTest TaskFragmentOrganizerPolicyTest
Change-Id: I6001ed57483ca005270b395ad7b34271efb91289
parent 09e869c4
Loading
Loading
Loading
Loading
+5 −21
Original line number Diff line number Diff line
@@ -488,10 +488,6 @@ class Task extends TaskFragment {

    private boolean mForceShowForAllUsers;

    /** When set, will force the task to report as invisible. */
    static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
    static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
    private int mForceHiddenFlags = 0;
    private boolean mForceTranslucent = false;

    // The display category name for this task.
@@ -4492,20 +4488,13 @@ class Task extends TaskFragment {
     * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
     * @return Whether the force hidden state changed
     */
    boolean setForceHidden(int flags, boolean set) {
        int newFlags = mForceHiddenFlags;
        if (set) {
            newFlags |= flags;
        } else {
            newFlags &= ~flags;
        }
        if (mForceHiddenFlags == newFlags) {
            return false;
        }

    @Override
    boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
        final boolean wasHidden = isForceHidden();
        final boolean wasVisible = isVisible();
        mForceHiddenFlags = newFlags;
        if (!super.setForceHidden(flags, set)) {
            return false;
        }
        final boolean nowHidden = isForceHidden();
        if (wasHidden != nowHidden) {
            final String reason = "setForceHidden";
@@ -4536,11 +4525,6 @@ class Task extends TaskFragment {
        return super.isAlwaysOnTop();
    }

    @Override
    protected boolean isForceHidden() {
        return mForceHiddenFlags != 0;
    }

    boolean isForceHiddenForPinnedTask() {
        return (mForceHiddenFlags & FLAG_FORCE_HIDDEN_FOR_PINNED_TASK) != 0;
    }
+32 −1
Original line number Diff line number Diff line
@@ -342,6 +342,19 @@ class TaskFragment extends WindowContainer<WindowContainer> {
     */
    private boolean mIsolatedNav;

    /** When set, will force the task to report as invisible. */
    static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
    static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
    static final int FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG = 1 << 2;

    @IntDef(prefix = {"FLAG_FORCE_HIDDEN_"}, value = {
            FLAG_FORCE_HIDDEN_FOR_PINNED_TASK,
            FLAG_FORCE_HIDDEN_FOR_TASK_ORG,
            FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG,
    }, flag = true)
    @interface FlagForceHidden {}
    protected int mForceHiddenFlags = 0;

    final Point mLastSurfaceSize = new Point();

    private final Rect mTmpBounds = new Rect();
@@ -825,8 +838,26 @@ class TaskFragment extends WindowContainer<WindowContainer> {
     * Returns whether this TaskFragment is currently forced to be hidden for any reason.
     */
    protected boolean isForceHidden() {
        return mForceHiddenFlags != 0;
    }

    /**
     * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
     * @return Whether the force hidden state changed
     */
    boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
        int newFlags = mForceHiddenFlags;
        if (set) {
            newFlags |= flags;
        } else {
            newFlags &= ~flags;
        }
        if (mForceHiddenFlags == newFlags) {
            return false;
        }
        mForceHiddenFlags = newFlags;
        return true;
    }

    protected boolean isForceTranslucent() {
        return false;
+2 −3
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -745,9 +744,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
        }
    }

    boolean isSystemOrganizer(@NonNull TaskFragmentOrganizerToken token) {
    boolean isSystemOrganizer(@NonNull IBinder organizerToken) {
        final TaskFragmentOrganizerState state =
                mTaskFragmentOrganizerState.get(token.asBinder());
                mTaskFragmentOrganizerState.get(organizerToken);
        return state != null && state.mIsSystemOrganizer;
    }

+56 −22
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATI
import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
import static android.window.WindowContainerTransaction.Change.CHANGE_FOCUSABLE;
import static android.window.WindowContainerTransaction.Change.CHANGE_HIDDEN;
import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION;
@@ -61,6 +63,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;

@@ -821,6 +824,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
            return TRANSACT_EFFECTS_NONE;
        }

        int effects = TRANSACT_EFFECTS_NONE;
        // When the TaskFragment is resized, we may want to create a change transition for it, for
        // which we want to defer the surface update until we determine whether or not to start
        // change transition.
@@ -843,7 +847,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
            c.getConfiguration().windowConfiguration.setBounds(absBounds);
            taskFragment.setRelativeEmbeddedBounds(relBounds);
        }
        final int effects = applyChanges(taskFragment, c);
        if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
            if (taskFragment.setForceHidden(
                    FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG, c.getHidden())) {
                effects |= TRANSACT_EFFECTS_LIFECYCLE;
            }
        }
        effects |= applyChanges(taskFragment, c);

        if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
            taskFragment.initializeChangeTransition(mTmpBounds0);
        }
@@ -1920,6 +1931,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
     * For config change on {@link TaskFragment}, we only support the following operations:
     * {@link WindowContainerTransaction#setRelativeBounds(WindowContainerToken, Rect)},
     * {@link WindowContainerTransaction#setWindowingMode(WindowContainerToken, int)}.
     *
     * For a system organizer, we additionally support
     * {@link WindowContainerTransaction#setHidden(WindowContainerToken, boolean)}, and
     * {@link WindowContainerTransaction#setFocusable(WindowContainerToken, boolean)}. See
     * {@link TaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer, boolean)}
     */
    private void enforceTaskFragmentConfigChangeAllowed(@NonNull String func,
            @Nullable WindowContainer wc, @NonNull WindowContainerTransaction.Change change,
@@ -1938,32 +1954,50 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
            throw new SecurityException(msg);
        }

        final int changeMask = change.getChangeMask();
        final int configSetMask = change.getConfigSetMask();
        final int windowSetMask = change.getWindowSetMask();
        if (changeMask == 0 && configSetMask == 0 && windowSetMask == 0
                && change.getWindowingMode() >= 0) {
            // The change contains only setWindowingMode, which is allowed.
            return;
        final int originalChangeMask = change.getChangeMask();
        final int originalConfigSetMask = change.getConfigSetMask();
        final int originalWindowSetMask = change.getWindowSetMask();

        int changeMaskToBeChecked = originalChangeMask;
        int configSetMaskToBeChecked = originalConfigSetMask;
        int windowSetMaskToBeChecked = originalWindowSetMask;

        if (mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
            // System organizer is allowed to update the hidden and focusable state.
            // We unset the CHANGE_HIDDEN and CHANGE_FOCUSABLE bits because they are checked here.
            changeMaskToBeChecked &= ~CHANGE_HIDDEN;
            changeMaskToBeChecked &= ~CHANGE_FOCUSABLE;
        }
        if (changeMask != CHANGE_RELATIVE_BOUNDS
                || configSetMask != ActivityInfo.CONFIG_WINDOW_CONFIGURATION
                || windowSetMask != WindowConfiguration.WINDOW_CONFIG_BOUNDS) {
            // None of the change should be requested from a TaskFragment organizer except
            // setRelativeBounds and setWindowingMode.
            // For setRelativeBounds, we don't need to check whether it is outside of the Task

        // setRelativeBounds is allowed.
        if ((changeMaskToBeChecked & CHANGE_RELATIVE_BOUNDS) != 0
                && (configSetMaskToBeChecked & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
                && (windowSetMaskToBeChecked & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) {
            // For setRelativeBounds, we don't need to check whether it is outside the Task
            // bounds, because it is possible that the Task is also resizing, for which we don't
            // want to throw an exception. The bounds will be adjusted in
            // TaskFragment#translateRelativeBoundsToAbsoluteBounds.
            String msg = "Permission Denial: " + func + " from pid="
            changeMaskToBeChecked &= ~CHANGE_RELATIVE_BOUNDS;
            configSetMaskToBeChecked &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
            windowSetMaskToBeChecked &= ~WindowConfiguration.WINDOW_CONFIG_BOUNDS;
        }

        if (changeMaskToBeChecked == 0 && configSetMaskToBeChecked == 0
                && windowSetMaskToBeChecked == 0) {
            // All the changes have been checked.
            // Note that setWindowingMode is always allowed, so we don't need to check the mask.
            return;
        }

        final String msg = "Permission Denial: " + func + " from pid="
                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                    + " trying to apply changes of changeMask=" + changeMask
                    + " configSetMask=" + configSetMask + " windowSetMask=" + windowSetMask
                + " trying to apply changes of changeMask=" + originalChangeMask
                + " configSetMask=" + originalConfigSetMask
                + " windowSetMask=" + originalWindowSetMask
                + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
        Slog.w(TAG, msg);
        throw new SecurityException(msg);
    }
    }

    private void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
            @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller,
@@ -2019,7 +2053,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
        TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
        taskFragment.setTaskFragmentOrganizer(organizerToken,
                ownerActivity.getUid(), ownerActivity.info.processName,
                mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken));
                mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder()));
        final int position;
        if (creationParams.getPairedPrimaryFragmentToken() != null) {
            // When there is a paired primary TaskFragment, we want to place the new TaskFragment
+101 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.testing.Assert.assertThrows;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -58,6 +59,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
@@ -77,11 +79,13 @@ import android.util.Rational;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskOrganizer;
import android.window.IWindowContainerTransactionCallback;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
import android.window.TaskFragmentOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

@@ -578,6 +582,87 @@ public class WindowOrganizerTests extends WindowTestsBase {
        assertTrue(rootTask.shouldBeVisible(null));
    }

    @Test
    public void testTaskFragmentHiddenAndFocusableChanges() {
        removeGlobalMinSizeRestriction();
        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();

        final WindowContainerTransaction t = new WindowContainerTransaction();
        final TaskFragmentOrganizer organizer =
                createTaskFragmentOrganizer(t, true /* isSystemOrganizer */);

        final IBinder token = new Binder();
        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                .setParentTask(rootTask)
                .setFragmentToken(token)
                .setOrganizer(organizer)
                .createActivityCount(1)
                .build();

        // Should be visible and focusable initially.
        assertTrue(rootTask.shouldBeVisible(null));
        assertTrue(taskFragment.shouldBeVisible(null));
        assertTrue(taskFragment.isFocusable());
        assertTrue(taskFragment.isTopActivityFocusable());

        // Apply transaction to the TaskFragment hidden and not focusable.
        t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
        t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
        mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
                t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
                false /* shouldApplyIndependently */);

        // Should be not visible and not focusable after the transaction.
        assertFalse(taskFragment.shouldBeVisible(null));
        assertFalse(taskFragment.isFocusable());
        assertFalse(taskFragment.isTopActivityFocusable());

        // Apply transaction to the TaskFragment not hidden and focusable.
        t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), false);
        t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), true);
        mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
                t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
                false /* shouldApplyIndependently */);

        // Should be visible and focusable after the transaction.
        assertTrue(taskFragment.shouldBeVisible(null));
        assertTrue(taskFragment.isFocusable());
        assertTrue(taskFragment.isTopActivityFocusable());
    }

    @Test
    public void testTaskFragmentHiddenAndFocusableChanges_throwsWhenNotSystemOrganizer() {
        removeGlobalMinSizeRestriction();
        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();

        final WindowContainerTransaction t = new WindowContainerTransaction();
        final TaskFragmentOrganizer organizer =
                createTaskFragmentOrganizer(t, false /* isSystemOrganizer */);

        final IBinder token = new Binder();
        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                .setParentTask(rootTask)
                .setFragmentToken(token)
                .setOrganizer(organizer)
                .createActivityCount(1)
                .build();

        assertTrue(rootTask.shouldBeVisible(null));
        assertTrue(taskFragment.shouldBeVisible(null));

        t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
        t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);

        // Non-system organizers are not allow to update the hidden and focusable states.
        assertThrows(SecurityException.class, () ->
                mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
                        t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
                        false /* shouldApplyIndependently */)
        );
    }

    @Test
    public void testContainerTranslucentChanges() {
        removeGlobalMinSizeRestriction();
@@ -1600,4 +1685,20 @@ public class WindowOrganizerTests extends WindowTestsBase {
            assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
        }
    }

    @NonNull
    private TaskFragmentOrganizer createTaskFragmentOrganizer(
            @NonNull WindowContainerTransaction t, boolean isSystemOrganizer) {
        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
        final ITaskFragmentOrganizer organizerInterface =
                ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
        mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController
                .registerOrganizerInternal(
                        ITaskFragmentOrganizer.Stub.asInterface(
                                organizer.getOrganizerToken().asBinder()),
                        isSystemOrganizer);
        t.setTaskFragmentOrganizer(organizerInterface);

        return organizer;
    }
}