summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Wale Ogunwale <ogunwale@google.com> 2015-08-06 17:29:28 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2015-08-06 17:29:28 +0000
commit6e02fa01a023cea148e87899c9bdedf12c46075e (patch)
tree0dcdd0d297f507a9479fff448d546b9f3e157a87
parenta1a9644529292e531d5dbccdaeac1399544aff1a (diff)
parent868a5e16c3f40a1c36bc2bd15891e930800e781a (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.
-rw-r--r--core/java/android/app/Activity.java44
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityManagerNative.java49
-rw-r--r--core/java/android/app/ActivityThread.java6
-rw-r--r--core/java/android/app/Dialog.java16
-rw-r--r--core/java/android/app/IActivityManager.java7
-rw-r--r--core/java/android/view/IWindowManager.aidl33
-rw-r--r--core/java/android/view/Window.java21
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java37
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java51
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java73
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java57
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java31
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java4
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) {