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

Commit b34a7ad1 authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Added support for docked stack

Docked stacks occupy a dedicated region on a display.
When a docked stack is present all other static stacks bounds
are restricted to the region of the screen not occupied by
the docked stack.

Change-Id: I6aec3aa19c41a7e756375002f3a925977b5533b5
parent 4a86977e
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -428,11 +428,17 @@ public class ActivityManager {
     */
    public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;

    /**
     * ID of stack that occupies a dedicated region of the screen.
     * @hide
     */
    public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;

    /**
     * Last static stack stack ID.
     * @hide
     */
    public static final int LAST_STATIC_STACK_ID = FREEFORM_WORKSPACE_STACK_ID;
    public static final int LAST_STATIC_STACK_ID = DOCKED_STACK_ID;

    /**
     * Start of ID range used by stacks that are created dynamically.
+2 −2
Original line number Diff line number Diff line
@@ -3528,12 +3528,12 @@ class ActivityManagerProxy implements IActivityManager
        reply.recycle();
    }
    @Override
    public void resizeStack(int stackBoxId, Rect r) throws RemoteException
    public void resizeStack(int stackId, Rect r) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeInt(stackBoxId);
        data.writeInt(stackId);
        r.writeToParcel(data, 0);
        mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
        reply.readException();
+13 −13
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -29,6 +30,7 @@ import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -8653,8 +8655,12 @@ public final class ActivityManagerService extends ActivityManagerNative
                    Slog.e(TAG, "setActivityBounds: No TaskRecord for the ActivityRecord r=" + r);
                    return;
                }
                if (task.stack != null && task.stack.mStackId == DOCKED_STACK_ID) {
                    mStackSupervisor.resizeStackLocked(task.stack.mStackId, bounds);
                } else {
                    mStackSupervisor.resizeTaskLocked(task, bounds);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
@@ -9010,7 +9016,7 @@ public final class ActivityManagerService extends ActivityManagerNative
    public void moveActivityToStack(IBinder token, int stackId) throws RemoteException {
        if (stackId == HOME_STACK_ID) {
            throw new IllegalArgumentException(
                    "moveTaskToStack: Attempt to move token " + token + " to home stack");
                    "moveActivityToStack: Attempt to move token " + token + " to home stack");
        }
        synchronized (this) {
            long ident = Binder.clearCallingIdentity();
@@ -9022,13 +9028,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                }
                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveActivityToStack: moving r=" + r
                        + " to stackId=" + stackId);
                mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP);
                if (mFocusedActivity != r) {
                    setFocusedActivityLocked(r, "moveActivityToStack");
                } else {
                    mStackSupervisor.setFocusedStack(r, "moveActivityToStack");
                }
                mStackSupervisor.resumeTopActivitiesLocked(r.task.stack, null, null);
                mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, FORCE_FOCUS);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -9048,7 +9048,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            try {
                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
                        + " to stackId=" + stackId + " toTop=" + toTop);
                mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop);
                mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop, !FORCE_FOCUS);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
@@ -9074,9 +9074,9 @@ public final class ActivityManagerService extends ActivityManagerNative
        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
                "positionTaskInStack()");
        if (stackId == HOME_STACK_ID) {
            Slog.e(TAG, "positionTaskInStack: Attempt to change the position of task "
                    + taskId + " in/to home stack",
                    new RuntimeException("here").fillInStackTrace());
            throw new IllegalArgumentException(
                    "positionTaskInStack: Attempt to change the position of task "
                    + taskId + " in/to home stack");
        }
        synchronized (this) {
            long ident = Binder.clearCallingIdentity();
+70 −15
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.am;

import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
@@ -229,6 +230,8 @@ final class ActivityStack {

    // Whether or not this stack covers the entire screen; by default stacks are fullscreen
    boolean mFullscreen = true;
    // Current bounds of the stack or null if fullscreen.
    Rect mBounds = null;

    long mLaunchStartTime = 0;
    long mFullyDrawnStartTime = 0;
@@ -1227,8 +1230,42 @@ final class ActivityStack {
        return null;
    }

    // Checks if any of the stacks above this one has a fullscreen activity behind it.
    // If so, this stack is hidden, otherwise it is visible.
    /** Returns true if the stack contains a fullscreen task. */
    private boolean hasFullscreenTask() {
        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
            final TaskRecord task = mTaskHistory.get(i);
            if (task.mFullscreen) {
                return true;
            }
        }
        return false;
    }

    /** Return true if this stack is hidden by the presence of a docked stack. */
    private boolean isHiddenByDockedStack() {
        final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID);
        if (dockedStack != null) {
            final int dockedStackIndex = mStacks.indexOf(dockedStack);
            final int stackIndex = mStacks.indexOf(this);
            if (dockedStackIndex > stackIndex) {
                // Fullscreen stacks or stacks with fullscreen task below the docked stack are not
                // visible. We do this so we don't have the 2 stacks and their tasks overlap.
                if (mFullscreen) {
                    return true;
                }

                // We need to also check the tasks in the stack because they can be fullscreen
                // even though their stack isn't due to their root activity not been resizeable
                // (i.e. doesn't support multi-window mode).
                if (hasFullscreenTask()) {
                    return true;
                }
            }
        }
        return false;
    }

    /** Returns true if the stack is considered visible. */
    private boolean isStackVisibleLocked() {
        if (!isAttached()) {
            return false;
@@ -1238,12 +1275,15 @@ final class ActivityStack {
            return true;
        }

        // Any stack that isn't the front stack is not visible, except for the case of the home
        // stack behind the main application stack since we can have dialog activities on the
        // main application stack that need the home stack to display behind them.
        // TODO(multi-window): Also need to add exception for side-by-side stacks.
        final boolean homeStack = mStackId == HOME_STACK_ID;
        if (!homeStack) {
        final int stackIndex = mStacks.indexOf(this);

        if (stackIndex == mStacks.size() - 1) {
            Slog.wtf(TAG,
                    "Stack=" + this + " isn't front stack but is at the top of the stack list");
            return false;
        }

        if (isHiddenByDockedStack()) {
            return false;
        }

@@ -1252,13 +1292,24 @@ final class ActivityStack {
         * fullscreen activity, or a translucent activity that requested the
         * wallpaper to be shown behind it.
         */
        for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
        for (int i = stackIndex + 1; i < mStacks.size(); i++) {
            final ActivityStack stack = mStacks.get(i);
            if (stack.mStackId != FULLSCREEN_WORKSPACE_STACK_ID) {
            final ArrayList<TaskRecord> tasks = stack.getAllTasks();

            if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
                continue;
            }

            if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID
                    || stack.mStackId == HOME_STACK_ID) {
                // The freeform and home stacks can't have any other stack visible behind them
                // when they are fullscreen since they act as base/cut-off points for visibility.
                // NOTE: we don't cut-off at the FULLSCREEN_WORKSPACE_STACK_ID because the home
                // stack sometimes needs to be visible behind it when it is displaying a dialog
                // activity. We let it fall through to the logic below to determine visibility.
                return false;
            }

            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                final TaskRecord task = tasks.get(taskNdx);
                // task above isn't fullscreen, so, we assume we're still visible.
@@ -2680,9 +2731,10 @@ final class ActivityStack {
            ActivityRecord next = topRunningActivityLocked(null);
            final String myReason = reason + " adjustFocus";
            if (next != r) {
                if (next != null && mStackId == FREEFORM_WORKSPACE_STACK_ID) {
                    // For freeform stack we always keep the focus within the stack as long as
                    // there is a running activity in the stack that we can adjust focus to.
                if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID
                        || mStackId == DOCKED_STACK_ID)) {
                    // For freeform and docked stacks we always keep the focus within the stack as
                    // long as there is a running activity in the stack that we can adjust focus to.
                    mService.setFocusedActivityLocked(next, myReason);
                    return;
                } else {
@@ -4321,7 +4373,10 @@ final class ActivityStack {
            printed |= ActivityStackSupervisor.dumpHistoryList(fd, pw,
                    mTaskHistory.get(taskNdx).mActivities, "    ", "Hist", true, !dumpAll,
                    dumpClient, dumpPackage, needSep, header,
                    "    Task id #" + task.taskId);
                    "    Task id #" + task.taskId + "\n" +
                    "    mFullscreen=" + task.mFullscreen + "\n" +
                    "    mBounds=" + task.mBounds + "\n" +
                    "    mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
            if (printed) {
                header = null;
            }
+87 −16
Original line number Diff line number Diff line
@@ -184,6 +184,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
    // (e.g. stack) is due to it moving to another container.
    static final boolean MOVING = true;

    // Force the focus to change to the stack we are moving a task to..
    static final boolean FORCE_FOCUS = true;

    // Activity actions an app cannot start if it uses a permission which is not granted.
    private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
            new ArrayMap<>();
@@ -328,6 +331,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
    /** Used to keep resumeTopActivityLocked() from being entered recursively */
    boolean inResumeTopActivity;

    // temp. rect used during resize calculation so we don't need to create a new object each time.
    private final Rect tempRect = new Rect();

    /**
     * Description of a request to start a new activity, which has been held
     * due to app switches being disabled.
@@ -2908,16 +2914,13 @@ public final class ActivityStackSupervisor implements DisplayListener {
            return;
        }

        final ActivityRecord r = stack.topRunningActivityLocked(null);
        if (r != null && !r.task.mResizeable) {
            Slog.w(TAG, "resizeStack: top task " + r.task + " not resizeable.");
            return;
        }
        ActivityRecord r = stack.topRunningActivityLocked(null);
        final boolean resizeTasks = r != null && r.task.mResizeable;

        final IntArray changedTaskIds = new IntArray(stack.numTasks());
        final List<Configuration> newTaskConfigs = new ArrayList<>(stack.numTasks());
        stack.mFullscreen =
                mWindowManager.resizeStack(stackId, bounds, changedTaskIds, newTaskConfigs);
        stack.mFullscreen = mWindowManager.resizeStack(
                stackId, bounds, resizeTasks, changedTaskIds, newTaskConfigs);
        for (int i = changedTaskIds.size() - 1; i >= 0; i--) {
            final TaskRecord task = anyTaskForIdLocked(changedTaskIds.get(i), false);
            if (task == null) {
@@ -2927,6 +2930,55 @@ public final class ActivityStackSupervisor implements DisplayListener {
            task.updateOverrideConfiguration(newTaskConfigs.get(i), bounds);
        }

        if (stack.mStackId == DOCKED_STACK_ID) {
            // Dock stack funness...Yay!
            if (stack.mFullscreen) {
                // The dock stack went fullscreen which is kinda like dismissing it.
                // In this case we make all other static stacks fullscreen and move all
                // docked stack tasks to the fullscreen stack.
                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
                    if (i != DOCKED_STACK_ID) {
                        resizeStackLocked(i, null);
                    }
                }

                final ArrayList<TaskRecord> tasks = stack.getAllTasks();
                final int count = tasks.size();
                for (int i = 0; i < count; i++) {
                    moveTaskToStackLocked(tasks.get(i).taskId,
                            FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS);
                }

                // stack shouldn't contain anymore activities, so nothing to resume.
                r = null;
            } else {
                // Docked stacks occupy a dedicated region on screen so the size of all other
                // static stacks need to be adjusted so they don't overlap with the docked stack.
                final int leftChange = stack.mBounds.left - bounds.left;
                final int rightChange = stack.mBounds.right - bounds.right;
                final int topChange = stack.mBounds.top - bounds.top;
                final int bottomChange = stack.mBounds.bottom - bounds.bottom;

                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
                    if (i != DOCKED_STACK_ID) {
                        ActivityStack otherStack = getStack(i);
                        if (otherStack != null) {
                            tempRect.set(otherStack.mBounds);
                            // We adjust the opposing sides of the other stacks to
                            // the side in the dock stack that changed.
                            tempRect.left -= rightChange;
                            tempRect.right -= leftChange;
                            tempRect.top -= bottomChange;
                            tempRect.bottom -= topChange;
                            resizeStackLocked(i, tempRect);
                        }
                    }
                }

            }
        }
        stack.mBounds = stack.mFullscreen ? null : new Rect(bounds);

        if (r != null) {
            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
            // And we need to make sure at this point that all other activities
@@ -2968,7 +3020,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
        final boolean wasFrontStack = isFrontStack(task.stack);
        if (bounds == null && stackId != FULLSCREEN_WORKSPACE_STACK_ID) {
            stackId = FULLSCREEN_WORKSPACE_STACK_ID;
        } else if (bounds != null && task.stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
        } else if (bounds != null
                && stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
            stackId = FREEFORM_WORKSPACE_STACK_ID;
        }
        if (stackId != task.stack.mStackId) {
@@ -3069,8 +3122,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
     */
    private ActivityStack moveTaskToStackUncheckedLocked(
            TaskRecord task, int stackId, boolean toTop, String reason) {
        final ActivityStack stack =
                getStack(stackId, CREATE_IF_NEEDED, toTop);
        final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
        mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
        if (task.stack != null) {
            task.stack.removeTask(task, reason, MOVING);
@@ -3079,26 +3131,39 @@ public final class ActivityStackSupervisor implements DisplayListener {
        return stack;
    }

    void moveTaskToStackLocked(int taskId, int stackId, boolean toTop) {
    void moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus) {
        final TaskRecord task = anyTaskForIdLocked(taskId);
        if (task == null) {
            Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
            return;
        }
        ActivityStack stack =
                moveTaskToStackUncheckedLocked(task, stackId, toTop, "moveTaskToStack");
        final String reason = "moveTaskToStack";
        final ActivityRecord top = task.topRunningActivityLocked(null);
        final boolean adjustFocus = forceFocus || mService.mFocusedActivity == top;
        final ActivityStack stack =
                moveTaskToStackUncheckedLocked(task, stackId, toTop, reason);

        // Make sure the task has the appropriate bounds/size for the stack it is in.
        if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
            resizeTaskLocked(task, null);
            resizeTaskLocked(task, stack.mBounds);
        } else if (stackId == FREEFORM_WORKSPACE_STACK_ID
                && task.mBounds == null && task.mLastNonFullscreenBounds != null) {
            resizeTaskLocked(task, task.mLastNonFullscreenBounds);
        } else if (stackId == DOCKED_STACK_ID) {
            resizeTaskLocked(task, stack.mBounds);
        }

        if (top != null && adjustFocus) {
            if (mService.mFocusedActivity != top) {
                mService.setFocusedActivityLocked(top, reason);
            } else {
                setFocusedStack(top, reason);
            }
        }

        // The task might have already been running and its visibility needs to be synchronized with
        // the visibility of the stack / windows.
        stack.ensureActivitiesVisibleLocked(null, 0);
        ensureActivitiesVisibleLocked(null, 0);
        resumeTopActivitiesLocked();
    }

@@ -3659,6 +3724,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
                stackHeader.append("  Stack #");
                stackHeader.append(stack.mStackId);
                stackHeader.append(":");
                stackHeader.append("\n");
                stackHeader.append("  mFullscreen=" + stack.mFullscreen);
                stackHeader.append("\n");
                stackHeader.append("  mBounds=" + stack.mBounds);
                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
                        needSep, stackHeader.toString());
                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
@@ -4313,7 +4382,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
            mStack.mStacks = activityDisplay.mStacks;

            activityDisplay.attachActivities(mStack, onTop);
            mStack.mBounds =
                    mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop);
            mStack.mFullscreen = mStack.mBounds == null;
        }

        @Override
Loading