diff options
| author | 2015-08-06 17:29:28 +0000 | |
|---|---|---|
| committer | 2015-08-06 17:29:28 +0000 | |
| commit | 6e02fa01a023cea148e87899c9bdedf12c46075e (patch) | |
| tree | 0dcdd0d297f507a9479fff448d546b9f3e157a87 | |
| parent | a1a9644529292e531d5dbccdaeac1399544aff1a (diff) | |
| parent | 868a5e16c3f40a1c36bc2bd15891e930800e781a (diff) | |
Merge changes I4fc1b47e,Icc6d6b25,I8c6bb864
* changes:
Support for an activity to change and/or query it's associated stack
Support creating/launching a task with non-fullscreen bounds
Support finishing a task with any finishing activity in the task.
16 files changed, 334 insertions, 103 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index c419a2b76340..983af2f85233 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -31,6 +31,7 @@ import android.transition.Scene; import android.transition.TransitionManager; import android.util.ArrayMap; import android.util.SuperNotCalledException; +import android.view.Window.WindowStackCallback; import android.widget.Toolbar; import com.android.internal.app.IVoiceInteractor; @@ -672,7 +673,7 @@ public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, - Window.OnWindowDismissedCallback { + Window.OnWindowDismissedCallback, WindowStackCallback { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -683,6 +684,13 @@ public class Activity extends ContextThemeWrapper /** Start of user-defined activity results. */ public static final int RESULT_FIRST_USER = 1; + /** @hide Task isn't finished when activity is finished */ + public static final int DONT_FINISH_TASK_WITH_ACTIVITY = 0; + /** @hide Task is finished if the finishing activity is the root of the task */ + public static final int FINISH_TASK_WITH_ROOT_ACTIVITY = 1; + /** @hide Task is finished along with the finishing activity*/ + public static final int FINISH_TASK_WITH_ACTIVITY = 2; + static final String FRAGMENTS_TAG = "android:fragments"; private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState"; @@ -2699,8 +2707,30 @@ public class Activity extends ContextThemeWrapper * @hide */ @Override - public void onWindowDismissed() { - finish(); + public void onWindowDismissed(boolean finishTask) { + finish(finishTask ? FINISH_TASK_WITH_ACTIVITY : DONT_FINISH_TASK_WITH_ACTIVITY); + } + + + /** Called to move the window and its activity/task to a different stack container. + * For example, a window can move between + * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and + * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack. + * + * @param stackId stack Id to change to. + * @hide + */ + @Override + public void changeWindowStack(int stackId) throws RemoteException { + ActivityManagerNative.getDefault().moveActivityToStack(mToken, stackId); + } + + /** Returns the current stack Id for the window. + * @hide + */ + @Override + public int getWindowStackId() throws RemoteException { + return ActivityManagerNative.getDefault().getActivityStackId(mToken); } /** @@ -4848,7 +4878,7 @@ public class Activity extends ContextThemeWrapper * Finishes the current activity and specifies whether to remove the task associated with this * activity. */ - private void finish(boolean finishTask) { + private void finish(int finishTask) { if (mParent == null) { int resultCode; Intent resultData; @@ -4879,7 +4909,7 @@ public class Activity extends ContextThemeWrapper * onActivityResult(). */ public void finish() { - finish(false); + finish(DONT_FINISH_TASK_WITH_ACTIVITY); } /** @@ -4979,10 +5009,10 @@ public class Activity extends ContextThemeWrapper /** * Call this when your activity is done and should be closed and the task should be completely - * removed as a part of finishing the Activity. + * removed as a part of finishing the root activity of the task. */ public void finishAndRemoveTask() { - finish(true); + finish(FINISH_TASK_WITH_ROOT_ACTIVITY); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index b7a7d075afc7..78e1b761382b 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -405,7 +405,7 @@ public class ActivityManager { public static final int COMPAT_MODE_TOGGLE = 2; /** - * First static stack stack ID. + * First static stack ID. * @hide */ public static final int FIRST_STATIC_STACK_ID = 0; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index e9d4113e8d70..4232db474820 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -346,7 +346,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM if (data.readInt() != 0) { resultData = Intent.CREATOR.createFromParcel(data); } - boolean finishTask = (data.readInt() != 0); + int finishTask = data.readInt(); boolean res = finishActivity(token, resultCode, resultData, finishTask); reply.writeNoException(); reply.writeInt(res ? 1 : 0); @@ -2636,6 +2636,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeInt(res ? 1 : 0); return true; } + case GET_ACTIVITY_STACK_ID_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + int stackId = getActivityStackId(token); + reply.writeNoException(); + reply.writeInt(stackId); + return true; + } + case MOVE_ACTIVITY_TO_STACK_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + int stackId = data.readInt(); + moveActivityToStack(token, stackId); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -2949,7 +2965,7 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); return result; } - public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask) + public boolean finishActivity(IBinder token, int resultCode, Intent resultData, int finishTask) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -2962,7 +2978,7 @@ class ActivityManagerProxy implements IActivityManager } else { data.writeInt(0); } - data.writeInt(finishTask ? 1 : 0); + data.writeInt(finishTask); mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; @@ -6101,5 +6117,32 @@ class ActivityManagerProxy implements IActivityManager return res != 0; } + @Override + public void moveActivityToStack(IBinder token, int stackId) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + data.writeInt(stackId); + mRemote.transact(MOVE_ACTIVITY_TO_STACK_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override + public int getActivityStackId(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(GET_ACTIVITY_STACK_ID_TRANSACTION, data, reply, 0); + reply.readException(); + int stackId = reply.readInt(); + data.recycle(); + reply.recycle(); + return stackId; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3257244f4768..594ec4132956 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2555,7 +2555,8 @@ public final class ActivityThread { // manager to stop us. try { ActivityManagerNative.getDefault() - .finishActivity(r.token, Activity.RESULT_CANCELED, null, false); + .finishActivity(r.token, Activity.RESULT_CANCELED, null, + Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { // Ignore } @@ -3281,7 +3282,8 @@ public final class ActivityThread { // just end this activity. try { ActivityManagerNative.getDefault() - .finishActivity(token, Activity.RESULT_CANCELED, null, false); + .finishActivity(token, Activity.RESULT_CANCELED, null, + Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { } } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index f6e0e1e721b9..6e8e2c44ce37 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -721,29 +721,29 @@ public class Dialog implements DialogInterface, Window.Callback, public void onContentChanged() { } - + public void onWindowFocusChanged(boolean hasFocus) { } public void onAttachedToWindow() { } - + public void onDetachedFromWindow() { } /** @hide */ @Override - public void onWindowDismissed() { + public void onWindowDismissed(boolean finishTask) { dismiss(); } - + /** - * Called to process key events. You can override this to intercept all - * key events before they are dispatched to the window. Be sure to call + * Called to process key events. You can override this to intercept all + * key events before they are dispatched to the window. Be sure to call * this implementation for key events that should be handled normally. - * + * * @param event The key event. - * + * * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5352465ff775..00e397dee3cc 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -93,7 +93,7 @@ public interface IActivityManager extends IInterface { public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent, Bundle options) throws RemoteException; public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException; - public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask) + public boolean finishActivity(IBinder token, int code, Intent data, int finishTask) throws RemoteException; public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException; public boolean finishActivityAffinity(IBinder token) throws RemoteException; @@ -527,6 +527,9 @@ public interface IActivityManager extends IInterface { // descriptor. public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException; + public int getActivityStackId(IBinder token) throws RemoteException; + public void moveActivityToStack(IBinder token, int stackId) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -879,4 +882,6 @@ public interface IActivityManager extends IInterface { int START_BINDER_TRACKING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 340; int STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 341; int POSITION_TASK_IN_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 342; + int GET_ACTIVITY_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 343; + int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344; } 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/core/java/android/view/Window.java b/core/java/android/view/Window.java index 07984e9ddd98..4fc2ad320af1 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -34,6 +34,7 @@ import android.media.session.MediaController; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; import android.os.SystemProperties; import android.transition.Scene; import android.transition.Transition; @@ -471,8 +472,24 @@ public abstract class Window { /** * Called when a window is dismissed. This informs the callback that the * window is gone, and it should finish itself. + * @param finishTask True if the task should also be finished. */ - public void onWindowDismissed(); + void onWindowDismissed(boolean finishTask); + } + + /** @hide */ + public interface WindowStackCallback { + /** Called to move the window and its activity/task to a different stack container. + * For example, a window can move between + * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and + * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack. + * + * @param stackId stack Id to change to. + */ + void changeWindowStack(int stackId) throws RemoteException; + + /** Returns the current stack Id for the window. */ + int getWindowStackId() throws RemoteException; } public Window(Context context) { @@ -659,7 +676,7 @@ public abstract class Window { /** @hide */ public final void dispatchOnWindowDismissed() { if (mOnWindowDismissedCallback != null) { - mOnWindowDismissedCallback.onWindowDismissed(); + mOnWindowDismissedCallback.onWindowDismissed(false); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7f4746ec2d8b..147a5fdd6116 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4252,14 +4252,13 @@ public final class ActivityManagerService extends ActivityManagerNative * @param token The Binder token referencing the Activity we want to finish. * @param resultCode Result code, if any, from this Activity. * @param resultData Result data (Intent), if any, from this Activity. - * @param finishTask Whether to finish the task associated with this Activity. Only applies to - * the root Activity in the task. + * @param finishTask Whether to finish the task associated with this Activity. * * @return Returns true if the activity successfully finished, or false if it is still running. */ @Override public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, - boolean finishTask) { + int finishTask) { // Refuse possible leaked file descriptors if (resultData != null && resultData.hasFileDescriptors() == true) { throw new IllegalArgumentException("File descriptors passed in Intent"); @@ -4306,7 +4305,8 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); try { boolean res; - if (finishTask && r == rootR) { + if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY + || (finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY && r == rootR)) { // If requested, remove the task that is associated to this activity only if it // was the root activity in the task. The result code and data is ignored // because we don't support returning them across task boundaries. @@ -8930,6 +8930,35 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public int getActivityStackId(IBinder token) throws RemoteException { + synchronized (this) { + ActivityStack stack = ActivityRecord.getStackLocked(token); + if (stack == null) { + throw new IllegalArgumentException( + "getActivityStackId: No stack for token=" + token); + } + return stack.mStackId; + } + } + + @Override + public void moveActivityToStack(IBinder token, int stackId) throws RemoteException { + synchronized(this) { + final long origId = Binder.clearCallingIdentity(); + try { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r == null) { + throw new IllegalArgumentException( + "moveActivityToStack: No activity record matching token=" + token); + } + moveTaskToStack(r.task.taskId, stackId, true /*toTop*/); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override public void moveTaskToStack(int taskId, int stackId, boolean toTop) { enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); 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..4a0fd8941399 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -17,15 +17,7 @@ package com.android.server.am; import static android.Manifest.permission.START_ANY_ACTIVITY; -import static android.app.ActivityManager.FIRST_DYNAMIC_STACK_ID; -import static android.app.ActivityManager.FIRST_STATIC_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; -import static android.app.ActivityManager.LAST_STATIC_STACK_ID; -import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; -import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; -import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; +import static android.app.ActivityManager.*; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -1789,20 +1781,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 +2893,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 +2918,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 +2970,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 +2998,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; } @@ -3015,6 +3016,15 @@ public final class ActivityStackSupervisor implements DisplayListener { task.stack.removeTask(task, "moveTaskToStack", false /* notMoving */); } stack.addTask(task, toTop, true); + + // 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); + } else if (stackId == FREEFORM_WORKSPACE_STACK_ID + && task.mBounds == null && task.mLastNonFullscreenBounds != null) { + resizeTaskLocked(task, task.mLastNonFullscreenBounds); + } + // 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); @@ -3819,6 +3829,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } info.taskIds = taskIds; info.taskNames = taskNames; + info.taskBounds = taskBounds; return info; } 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/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index aef99bc2d6b1..90d2593654d2 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -148,8 +148,7 @@ public class TaskStack implements DimLayer.DimLayerUser { void updateDisplayInfo() { if (mDisplayContent != null) { - mDisplayContent.getLogicalDisplayRect(mTmpRect); - mAnimationBackgroundSurface.setBounds(mTmpRect); + setBounds(mFullscreen ? null : mBounds); for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); } 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) { |