Added support for pinned stack.
Used to support picture-in-picture use case for multi-window
Bug: 25006507
Change-Id: I3bef3f75e0c003f5974274294f1250171d424625
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3bfeff0..6ae32d0 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -434,10 +434,17 @@
public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
/**
+ * ID of stack that always on top (always visible) when it exist.
+ * Mainly used for this in Picture-in-Picture mode.
+ * @hide
+ */
+ public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
+
+ /**
* Last static stack stack ID.
* @hide
*/
- public static final int LAST_STATIC_STACK_ID = DOCKED_STACK_ID;
+ public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
/**
* Start of ID range used by stacks that are created dynamically.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 4644211..afef763 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
@@ -5408,7 +5409,7 @@
* @Return Returns true if the window should show a non client decor.
**/
private static boolean hasNonClientDecor(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
+ return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
}
/**
@@ -5417,7 +5418,7 @@
* @Return Returns true if the window should show a shadow.
**/
private static boolean nonClientDecorHasShadow(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
+ return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c728b39..43e45e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,6 +24,7 @@
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -8638,9 +8639,10 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- // Note it's not allowed to resize a home stack task, or a docked task.
+ // Note it's not allowed to resize a home, docked, or pinned stack task.
int stackId = task.stack.mStackId;
- if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) {
+ if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID
+ || stackId == PINNED_STACK_ID) {
throw new IllegalArgumentException("trying to resizeTask on a "
+ "home or docked task");
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8bf1d22..ed3a1c2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -253,6 +254,9 @@
private final LaunchingTaskPositioner mTaskPositioner;
+ // If the bounds of task contained in this stack should be persisted across power cycles.
+ final boolean mPersistTaskBounds;
+
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -354,10 +358,6 @@
return count;
}
- int numTasks() {
- return mTaskHistory.size();
- }
-
ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
RecentTasks recentTasks) {
mActivityContainer = activityContainer;
@@ -370,6 +370,7 @@
mRecentTasks = recentTasks;
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
+ mPersistTaskBounds = mStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID;
}
void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
@@ -1306,6 +1307,11 @@
return true;
}
+ if (mStackId == PINNED_STACK_ID) {
+ // Pinned stack is always visible if it exist.
+ return true;
+ }
+
final int stackIndex = mStacks.indexOf(this);
if (stackIndex == mStacks.size() - 1) {
@@ -1328,8 +1334,9 @@
}
final int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
- if (focusedStackId == DOCKED_STACK_ID && stackIndex == belowFocusedIndex) {
- // Stacks directly behind the docked stack are always visible.
+ if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
+ && stackIndex == belowFocusedIndex) {
+ // Stacks directly behind the docked or pinned stack are always visible.
return true;
}
@@ -1343,9 +1350,10 @@
}
if (belowFocusedIndex >= 0) {
final ActivityStack stack = mStacks.get(belowFocusedIndex);
- if (stack.mStackId == DOCKED_STACK_ID && stackIndex == (belowFocusedIndex - 1)) {
- // The stack behind the docked stack is also visible so we can have a complete
- // backdrop to the translucent activity when the docked stack is up.
+ if ((stack.mStackId == DOCKED_STACK_ID || stack.mStackId == PINNED_STACK_ID)
+ && stackIndex == (belowFocusedIndex - 1)) {
+ // The stack behind the docked or pinned stack is also visible so we can have a
+ // complete backdrop to the translucent activity when the docked stack is up.
return true;
}
}
@@ -2784,9 +2792,10 @@
final String myReason = reason + " adjustFocus";
if (next != r) {
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.
+ || mStackId == DOCKED_STACK_ID || mStackId == PINNED_STACK_ID)) {
+ // For freeform, docked, and pinned 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 {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 99f7ec6..33f8da7 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3293,7 +3293,8 @@
return;
}
final String reason = "moveTaskToStack";
- if (stackId == DOCKED_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
// and then add a new one. This call will tell window manager about this, so it can
@@ -3314,7 +3315,7 @@
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
resizeTaskLocked(task, task.mLastNonFullscreenBounds,
RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
- } else if (stackId == DOCKED_STACK_ID) {
+ } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
resizeTaskLocked(task, stack.mBounds,
RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fe87a93..090a342 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -20,6 +20,7 @@
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;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -1189,14 +1190,14 @@
mFullscreen = bounds == null;
if (mFullscreen) {
- if (mBounds != null && stack.mStackId != DOCKED_STACK_ID) {
+ if (mBounds != null && stack.mPersistTaskBounds) {
mLastNonFullscreenBounds = mBounds;
}
mBounds = null;
mOverrideConfig = Configuration.EMPTY;
} else {
mBounds = new Rect(bounds);
- if (stack == null || stack.mStackId != DOCKED_STACK_ID) {
+ if (stack == null || stack.mPersistTaskBounds) {
mLastNonFullscreenBounds = mBounds;
}
@@ -1235,11 +1236,12 @@
/** Returns the bounds that should be used to launch this task. */
Rect getLaunchBounds() {
+ final int stackId = stack.mStackId;
if (stack == null
- || stack.mStackId == HOME_STACK_ID
- || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ || stackId == HOME_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
return (mResizeable && stack != null) ? stack.mBounds : null;
- } else if (stack.mStackId == DOCKED_STACK_ID) {
+ } else if (!stack.mPersistTaskBounds) {
return stack.mBounds;
}
return mLastNonFullscreenBounds;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7c56180..6527881 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,12 +17,14 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -187,7 +189,8 @@
bounds = mTmpRect;
mFullscreen = true;
} else {
- if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID || bounds.isEmpty()) {
+ if ((mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+ && mStack.mStackId != PINNED_STACK_ID) || bounds.isEmpty()) {
// ensure bounds are entirely within the display rect
if (!bounds.intersect(mTmpRect)) {
// Can't set bounds outside the containing display...Sorry!
@@ -241,8 +244,7 @@
private boolean useCurrentBounds() {
final DisplayContent displayContent = mStack.getDisplayContent();
if (mFullscreen
- || mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStack.mStackId == DOCKED_STACK_ID
+ || mStack.allowTaskResize()
|| displayContent == null
|| displayContent.getDockedStackLocked() != null) {
return true;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6734fd6..9b3d478 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
import static com.android.server.wm.WindowManagerService.TAG;
@@ -123,7 +124,8 @@
boolean allowTaskResize() {
return mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStackId == DOCKED_STACK_ID;
+ || mStackId == DOCKED_STACK_ID
+ || mStackId == PINNED_STACK_ID;
}
/**
@@ -202,6 +204,7 @@
private boolean useCurrentBounds() {
if (mFullscreen
|| mStackId == DOCKED_STACK_ID
+ || mStackId == PINNED_STACK_ID
|| mDisplayContent == null
|| mDisplayContent.getDockedStackLocked() != null) {
return true;
@@ -393,7 +396,7 @@
Rect bounds = null;
final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
- if (mStackId == DOCKED_STACK_ID || (dockedStack != null
+ if (mStackId == DOCKED_STACK_ID || (dockedStack != null && mStackId != PINNED_STACK_ID
&& mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
// The existence of a docked stack affects the size of any static stack created since
// the docked stack occupies a dedicated region on screen.
@@ -422,6 +425,7 @@
void getStackDockedModeBoundsLocked(Rect outBounds) {
if (mStackId == DOCKED_STACK_ID
+ || mStackId == PINNED_STACK_ID
|| mStackId > LAST_STATIC_STACK_ID
|| mDisplayContent == null) {
outBounds.set(mBounds);
@@ -533,7 +537,7 @@
for (int i = 0; i < count; i++) {
final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
final int otherStackId = otherStack.mStackId;
- if (otherStackId != DOCKED_STACK_ID
+ if (otherStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID
&& otherStackId >= FIRST_STATIC_STACK_ID
&& otherStackId <= LAST_STATIC_STACK_ID) {
mService.mH.sendMessage(