diff options
7 files changed, 168 insertions, 64 deletions
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 5994d4fbae39..0a4b9826e2be 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -80,10 +80,37 @@ interface IWindowManager void setEventDispatching(boolean enabled); void addWindowToken(IBinder token, int type); void removeWindowToken(IBinder token); - void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId, + /** + * Adds an application token to the specified task Id. + * @param addPos The position to add the token to in the task. + * @param token The token to add. + * @param taskId The Id of the task we are adding the token to. + * @param stackId Stack Id to create a new Task with the input task Id on + * if the task doesn't exist yet. + * @param requestedOrientation Orientation to use. + * @param fullscreen True if the application token is fullscreen. + * @param showWhenLocked True if the application token should be shown when locked. + * @param userId Id of user to associate the token with. + * @param configChanges Input configuration changes. + * @param voiceInteraction True if the token is in voice interaction mode. + * @param launchTaskBehind True if the token is been launched from behind. + * @param taskBounds Bounds to use when creating a new Task with the input task Id if + * the task doesn't exist yet. + * @return The configuration of the task if it was newly created. null otherwise. + */ + Configuration addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, - int configChanges, boolean voiceInteraction, boolean launchTaskBehind); - void setAppTask(IBinder token, int taskId); + int configChanges, boolean voiceInteraction, boolean launchTaskBehind, + in Rect taskBounds); + /** + * + * @param token The token we are adding to the input task Id. + * @param taskId The Id of the task we are adding the token to. + * @param taskBounds Bounds to use when creating a new Task with the input task Id if + * the task doesn't exist yet. + * @return The configuration of the task if it was newly created. null otherwise. + */ + Configuration setAppTask(IBinder token, int taskId, in Rect taskBounds); void setAppOrientation(IApplicationToken token, int requestedOrientation); int getAppOrientation(IApplicationToken token); void setFocusedApp(IBinder token, boolean moveFocusNow); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 2b3cc929b6a7..494ae6cf6776 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -25,7 +25,9 @@ import static com.android.server.am.ActivityManagerDebugConfig.*; import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; +import android.graphics.Rect; import android.util.ArraySet; +import android.view.IApplicationToken; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; import com.android.internal.os.BatteryStatsImpl; @@ -2157,11 +2159,7 @@ final class ActivityStack { + task, new RuntimeException("here").fillInStackTrace()); task.addActivityToTop(r); r.putInHistory(); - mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, - r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, - (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, - r.userId, r.info.configChanges, task.voiceSession != null, - r.mLaunchTaskBehind); + addAppToken(r, task); if (VALIDATE_TOKENS) { validateAppTokensLocked(); } @@ -2221,10 +2219,7 @@ final class ActivityStack { : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition); mNoAnimActivities.remove(r); } - mWindowManager.addAppToken(task.mActivities.indexOf(r), - r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, - (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, - r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind); + addAppToken(r, task); boolean doShow = true; if (newTask) { // Even though this activity is starting fresh, we still need @@ -2273,10 +2268,7 @@ final class ActivityStack { } else { // If this is the first activity, don't do any fancy animations, // because there is nothing for it to animate on top of. - mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, - r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, - (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, - r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind); + addAppToken(r, task); ActivityOptions.abort(options); options = null; } @@ -2390,8 +2382,7 @@ final class ActivityStack { + " out to new task " + target.task); } - final int targetTaskId = targetTask.taskId; - mWindowManager.setAppTask(target.appToken, targetTaskId); + setAppTask(target, targetTask); boolean noOptions = canMoveOptions; final int start = replyChainEnd < 0 ? i : replyChainEnd; @@ -2416,10 +2407,10 @@ final class ActivityStack { p.setTask(targetTask, null); targetTask.addActivityAtBottom(p); - mWindowManager.setAppTask(p.appToken, targetTaskId); + setAppTask(p, targetTask); } - mWindowManager.moveTaskToBottom(targetTaskId); + mWindowManager.moveTaskToBottom(targetTask.taskId); if (VALIDATE_TOKENS) { validateAppTokensLocked(); } @@ -2558,7 +2549,7 @@ final class ActivityStack { + " callers=" + Debug.getCallers(3)); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Pulling activity " + p + " from " + srcPos + " in to resetting task " + task); - mWindowManager.setAppTask(p.appToken, taskId); + setAppTask(p, task); } mWindowManager.moveTaskToTop(taskId); if (VALIDATE_TOKENS) { @@ -4420,6 +4411,30 @@ final class ActivityStack { } } + void addAppToken(ActivityRecord r, TaskRecord task) { + final Rect bounds = task.getLaunchBounds(); + final Configuration config = + mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, + r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, + (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, + r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind, + bounds); + if (config != null) { + task.updateOverrideConfiguration(config, bounds); + } + r.taskConfigOverride = task.mOverrideConfig; + } + + private void setAppTask(ActivityRecord r, TaskRecord task) { + final Rect bounds = task.getLaunchBounds(); + final Configuration config = + mWindowManager.setAppTask(r.appToken, task.taskId, task.getLaunchBounds()); + if (config != null) { + task.updateOverrideConfiguration(config, bounds); + } + r.taskConfigOverride = task.mOverrideConfig; + } + public int getStackId() { return mStackId; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 76bac911cdf4..296f16c87227 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1789,20 +1789,22 @@ public final class ActivityStackSupervisor implements DisplayListener { return mFocusedStack; } + // We first try to put the task in the first dynamic stack. final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks; for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) { stack = homeDisplayStacks.get(stackNdx); - if (!stack.isHomeStack()) { + final boolean isDynamicStack = stack.mStackId >= FIRST_DYNAMIC_STACK_ID; + if (isDynamicStack) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: Setting focused stack=" + stack); return stack; } } - // TODO (multi-window): Change to select task id based on if the task should on in - // fullscreen, freefrom, or sid-by-side stack. + // If there is no suitable dynamic stack then we figure out which static stack to use. stack = getStack( - FULLSCREEN_WORKSPACE_STACK_ID, + task != null + ? task.getLaunchStackId(mFocusedStack) : FULLSCREEN_WORKSPACE_STACK_ID, true /*createStaticStackIfNeeded*/, true /*createOnTop*/); if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r=" @@ -2899,7 +2901,7 @@ public final class ActivityStackSupervisor implements DisplayListener { Slog.wtf(TAG, "Task in WindowManager, but not in ActivityManager???"); continue; } - task.updateOverrideConfiguration(newTaskConfigs.get(i)); + task.updateOverrideConfiguration(newTaskConfigs.get(i), bounds); } if (r != null) { @@ -2924,16 +2926,20 @@ public final class ActivityStackSupervisor implements DisplayListener { return; } - task.mBounds = new Rect(bounds); - if (!mWindowManager.isValidTaskId(task.taskId)) { // Task doesn't exist in window manager yet (e.g. was restored from recents). - // No need to do anything else until we add the task to window manager. + // All we can do for now is update the bounds so it can be used when the task is + // added to window manager. + task.mBounds = task.mLastNonFullscreenBounds = new Rect(bounds); + if (task.stack != null && task.stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) { + // re-restore the task so it can have the proper stack association. + restoreRecentTaskLocked(task); + } return; } final Configuration overrideConfig = mWindowManager.resizeTask(task.taskId, bounds); - if (task.updateOverrideConfiguration(overrideConfig)) { + if (task.updateOverrideConfiguration(overrideConfig, bounds)) { ActivityRecord r = task.topRunningActivityLocked(null); if (r != null) { final ActivityStack stack = task.stack; @@ -2972,13 +2978,21 @@ public final class ActivityStackSupervisor implements DisplayListener { } private boolean restoreRecentTaskLocked(TaskRecord task) { - // TODO (multi-window): Change to select task id based on if the task should on in - // fullscreen, freefrom, or sid-by-side stack. - // Always put task for lean back device in home stack since they only have one stack, - // else use the preferred stack ID to get the stack we should use if it already exists. - ActivityStack stack = mLeanbackOnlyDevice ? mHomeStack : - getStack(FULLSCREEN_WORKSPACE_STACK_ID, - true /*createStaticStackIfNeeded*/, false /*createOnTop*/); + final int stackId = + mLeanbackOnlyDevice ? mHomeStack.mStackId : task.getLaunchStackId(mFocusedStack); + if (task.stack != null) { + // Task has already been restored once. See if we need to do anything more + if (task.stack.mStackId == stackId) { + // Nothing else to do since it is already restored in the right stack. + return true; + } + // Remove current stack association, so we can re-associate the task with the + // right stack below. + task.stack.removeTask(task, "restoreRecentTaskLocked", false /*notMoving*/); + } + + ActivityStack stack = + getStack(stackId, true /*createStaticStackIfNeeded*/, false /*createOnTop*/); if (stack == null) { // What does this mean??? Not sure how we would get here... @@ -2992,12 +3006,7 @@ public final class ActivityStackSupervisor implements DisplayListener { "Added restored task=" + task + " to stack=" + stack); final ArrayList<ActivityRecord> activities = task.mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - mWindowManager.addAppToken(0, r.appToken, task.taskId, stack.mStackId, - r.info.screenOrientation, r.fullscreen, - (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, - r.userId, r.info.configChanges, task.voiceSession != null, - r.mLaunchTaskBehind); + stack.addAppToken(activities.get(activityNdx), task); } return true; } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index a892c7d41ed0..20117c3053af 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -16,6 +16,9 @@ package com.android.server.am; +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.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; @@ -94,7 +97,7 @@ final class TaskRecord { private static final String ATTR_CALLING_PACKAGE = "calling_package"; private static final String ATTR_RESIZEABLE = "resizeable"; private static final String ATTR_PRIVILEGED = "privileged"; - private static final String ATTR_BOUNDS = "bounds"; + private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds"; private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail"; @@ -207,6 +210,10 @@ final class TaskRecord { // Bounds of the Task. null for fullscreen tasks. Rect mBounds = null; + // Last non-fullscreen bounds the task was launched in or resized to. + // The information is persisted and used to determine the appropriate stack to launch the + // task into on restore. + Rect mLastNonFullscreenBounds = null; Configuration mOverrideConfig = Configuration.EMPTY; @@ -301,7 +308,7 @@ final class TaskRecord { mCallingPackage = callingPackage; mResizeable = resizeable; mPrivileged = privileged; - mBounds = bounds; + mBounds = mLastNonFullscreenBounds = bounds; } void touchActiveTime() { @@ -963,8 +970,9 @@ final class TaskRecord { out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage); out.attribute(null, ATTR_RESIZEABLE, String.valueOf(mResizeable)); out.attribute(null, ATTR_PRIVILEGED, String.valueOf(mPrivileged)); - if (mBounds != null) { - out.attribute(null, ATTR_BOUNDS, mBounds.flattenToString()); + if (mLastNonFullscreenBounds != null) { + out.attribute( + null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString()); } if (affinityIntent != null) { @@ -1084,7 +1092,7 @@ final class TaskRecord { resizeable = Boolean.valueOf(attrValue); } else if (ATTR_PRIVILEGED.equals(attrName)) { privileged = Boolean.valueOf(attrValue); - } else if (ATTR_BOUNDS.equals(attrName)) { + } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) { bounds = Rect.unflattenFromString(attrValue); } else { Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName); @@ -1155,16 +1163,53 @@ final class TaskRecord { return task; } - boolean updateOverrideConfiguration(Configuration newConfig) { + boolean updateOverrideConfiguration(Configuration newConfig, Rect bounds) { Configuration oldConfig = mOverrideConfig; mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig; // We override the configuration only when the task's dimensions are different from the // display. In this manner, we know that if the override configuration is empty, the task // is necessarily fullscreen. mFullscreen = Configuration.EMPTY.equals(mOverrideConfig); + if (mFullscreen) { + if (mBounds != null) { + mLastNonFullscreenBounds = mBounds; + } + mBounds = null; + } else { + mBounds = mLastNonFullscreenBounds = new Rect(bounds); + } return !mOverrideConfig.equals(oldConfig); } + /** Returns the stack that should be used to launch this task. */ + int getLaunchStackId(ActivityStack focusStack) { + if (stack != null) { + // We are already in a stack silly... + return stack.mStackId; + } + if (isHomeTask()) { + return HOME_STACK_ID; + } + if (focusStack != null && focusStack.mStackId != HOME_STACK_ID) { + // Like it or not you are going in the focused stack! + return focusStack.mStackId; + } + if (mBounds != null || mLastNonFullscreenBounds != null) { + return FREEFORM_WORKSPACE_STACK_ID; + } + return FULLSCREEN_WORKSPACE_STACK_ID; + } + + /** Returns the bounds that should be used to launch this task. */ + Rect getLaunchBounds() { + if (stack == null + || stack.mStackId == HOME_STACK_ID + || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) { + return null; + } + return mLastNonFullscreenBounds; + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("userId="); pw.print(userId); pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 79527b785466..3ff5be12e607 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -76,12 +76,13 @@ class Task implements DimLayer.DimLayerUser { // of creating a new object per fullscreen task on a display. private static final SparseArray<DimLayer> sSharedFullscreenDimLayers = new SparseArray<>(); - Task(int taskId, TaskStack stack, int userId, WindowManagerService service) { + Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds) { mTaskId = taskId; mStack = stack; mUserId = userId; mService = service; mOverrideConfig = Configuration.EMPTY; + setBounds(bounds); } DisplayContent getDisplayContent() { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4b6f4799eacc..3d7b499f9403 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3687,24 +3687,26 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } - private Task createTaskLocked(int taskId, int stackId, int userId, AppWindowToken atoken) { + private Task createTaskLocked( + int taskId, int stackId, int userId, AppWindowToken atoken, Rect bounds) { if (DEBUG_STACK) Slog.i(TAG, "createTaskLocked: taskId=" + taskId + " stackId=" + stackId - + " atoken=" + atoken); + + " atoken=" + atoken + " bounds=" + bounds); final TaskStack stack = mStackIdToStack.get(stackId); if (stack == null) { throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId); } EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId); - Task task = new Task(taskId, stack, userId, this); + Task task = new Task(taskId, stack, userId, this, bounds); mTaskIdToTask.put(taskId, task); stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showForAllUsers); return task; } @Override - public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, + public Configuration addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId, - int configChanges, boolean voiceInteraction, boolean launchTaskBehind) { + int configChanges, boolean voiceInteraction, boolean launchTaskBehind, + Rect taskBounds) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3728,7 +3730,7 @@ public class WindowManagerService extends IWindowManager.Stub AppWindowToken atoken = findAppWindowToken(token.asBinder()); if (atoken != null) { Slog.w(TAG, "Attempted to add existing app token: " + token); - return; + return null; } atoken = new AppWindowToken(this, token, voiceInteraction); atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; @@ -3742,8 +3744,10 @@ public class WindowManagerService extends IWindowManager.Stub + " to stack=" + stackId + " task=" + taskId + " at " + addPos); Task task = mTaskIdToTask.get(taskId); + Configuration outConfig = null; if (task == null) { - task = createTaskLocked(taskId, stackId, userId, atoken); + task = createTaskLocked(taskId, stackId, userId, atoken, taskBounds); + outConfig = task.mOverrideConfig; } task.addAppToken(addPos, atoken); @@ -3753,12 +3757,12 @@ public class WindowManagerService extends IWindowManager.Stub atoken.hidden = true; atoken.hiddenRequested = true; - //dump(); + return outConfig; } } @Override - public void setAppTask(IBinder token, int taskId) { + public Configuration setAppTask(IBinder token, int taskId, Rect taskBounds) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppTask()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3768,17 +3772,20 @@ public class WindowManagerService extends IWindowManager.Stub final AppWindowToken atoken = findAppWindowToken(token); if (atoken == null) { Slog.w(TAG, "Attempted to set task id of non-existing app token: " + token); - return; + return null; } final Task oldTask = atoken.mTask; oldTask.removeAppToken(atoken); Task newTask = mTaskIdToTask.get(taskId); + Configuration outConfig = null; if (newTask == null) { - newTask = - createTaskLocked(taskId, oldTask.mStack.mStackId, oldTask.mUserId, atoken); + newTask = createTaskLocked( + taskId, oldTask.mStack.mStackId, oldTask.mUserId, atoken, taskBounds); + outConfig = newTask.mOverrideConfig; } newTask.addAppToken(Integer.MAX_VALUE /* at top */, atoken); + return outConfig; } } diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index e44969d9baf2..6177784a17c5 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -93,7 +93,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false); + mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null); fail("IWindowManager.addAppToken did not throw SecurityException as" + " expected"); } catch (SecurityException e) { @@ -103,7 +103,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.setAppTask(null, 0); + mWm.setAppTask(null, 0, null); fail("IWindowManager.setAppGroupId did not throw SecurityException as" + " expected"); } catch (SecurityException e) { |