From 188d801038e9cb68dbbe0c69558c2f13ca71c20d Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Fri, 23 Oct 2020 13:45:54 -0700 Subject: [PATCH] Changed createRootTask task to be completely async Currently some of the information about a task created by createRootTask is returned with the method and the rest like leash is return in onTaskAppear. Mixing both async and sync infromation return styles complicates the mental model for this. So, we will have this be completely aync for now. Also, fixed an issue where the windowing mode for organizer created task was not set correctly before the organizer gets the first task appear signal. Bug: 169266958 Test: Split-screen works! Change-Id: Ia4180d5a42838bf566fbf75f40466dfd8237e0f1 --- api/test-current.txt | 2 +- core/java/android/app/TaskInfo.java | 1 + .../window/ITaskOrganizerController.aidl | 2 +- core/java/android/window/TaskOrganizer.java | 13 +++-- data/etc/services.core.protolog.json | 6 +++ .../Shell/res/raw/wm_shell_protolog.json | 36 +++++++++++-- .../android/wm/shell/ShellTaskOrganizer.java | 24 ++++----- .../splitscreen/DividerImeController.java | 4 +- .../wm/shell/splitscreen/DividerView.java | 4 +- .../shell/splitscreen/SplitDisplayLayout.java | 4 +- .../splitscreen/SplitScreenController.java | 17 ++++--- ...izer.java => SplitScreenTaskListener.java} | 50 +++++++++++-------- .../shell/splitscreen/WindowManagerProxy.java | 6 +-- .../core/java/com/android/server/wm/Task.java | 26 +++++++++- .../android/server/wm/TaskDisplayArea.java | 23 +++++++-- .../server/wm/TaskOrganizerController.java | 39 +++++++++------ .../android/server/wm/ActivityStackTests.java | 5 +- .../server/wm/ActivityStarterTests.java | 2 +- .../server/wm/WindowOrganizerTests.java | 36 +++++++------ .../android/server/wm/WindowTestsBase.java | 22 ++++---- 20 files changed, 211 insertions(+), 111 deletions(-) rename libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/{SplitScreenTaskOrganizer.java => SplitScreenTaskListener.java} (83%) diff --git a/api/test-current.txt b/api/test-current.txt index 599487d68965f..edf860665fb67 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2375,7 +2375,7 @@ package android.window { public class TaskOrganizer extends android.window.WindowOrganizer { ctor public TaskOrganizer(); - method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.app.ActivityManager.RunningTaskInfo createRootTask(int, int); + method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void createRootTask(int, int, @Nullable android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.WindowContainerToken getImeTarget(int); diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index f1a67767cbe65..5caf3057c8409 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -241,6 +241,7 @@ public class TaskInfo { /** @hide */ public void addLaunchCookie(IBinder cookie) { + if (cookie == null || launchCookies.contains(cookie)) return; launchCookies.add(cookie); } diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 3a84c1f98ce68..4a43a438b69d6 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -40,7 +40,7 @@ interface ITaskOrganizerController { void unregisterTaskOrganizer(ITaskOrganizer organizer); /** Creates a persistent root task in WM for a particular windowing-mode. */ - ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode); + void createRootTask(int displayId, int windowingMode, IBinder launchCookie); /** Deletes a persistent root task in WM */ boolean deleteRootTask(in WindowContainerToken task); diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 6c739bed35a45..ad48a9f9f7767 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.app.ActivityManager; +import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; @@ -101,12 +102,18 @@ public class TaskOrganizer extends WindowOrganizer { @BinderThread public void onBackPressedOnTaskRoot(@NonNull ActivityManager.RunningTaskInfo taskInfo) {} - /** Creates a persistent root task in WM for a particular windowing-mode. */ + /** + * Creates a persistent root task in WM for a particular windowing-mode. + * @param displayId The display to create the root task on. + * @param windowingMode Windowing mode to put the root task in. + * @param launchCookie Launch cookie to associate with the task so that is can be identified + * when the {@link ITaskOrganizer#onTaskAppeared} callback is called. + */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @Nullable - public ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode) { + public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) { try { - return mTaskOrganizerController.createRootTask(displayId, windowingMode); + mTaskOrganizerController.createRootTask(displayId, windowingMode, launchCookie); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index a430dcdda2471..e51bf5e4e1f6f 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2923,6 +2923,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, + "1396893178": { + "message": "createRootTask unknown displayId=%d", + "level": "ERROR", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "1401295262": { "message": "Mode default, asking user", "level": "WARN", diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json index 358d440686dd7..a4e69de972992 100644 --- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json +++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json @@ -37,6 +37,18 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" }, + "-1325223370": { + "message": "Task appeared taskId=%d listener=%s", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, + "-1312360667": { + "message": "createRootTask() displayId=%d winMode=%d listener=%s", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, "-1006733970": { "message": "Display added: %d", "level": "VERBOSE", @@ -61,6 +73,12 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/LetterboxTaskListener.java" }, + "-842742255": { + "message": "%s onTaskAppeared unknown taskId=%d winMode=%d", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java" + }, "-712674749": { "message": "Clip description: %s", "level": "VERBOSE", @@ -73,11 +91,11 @@ "group": "WM_SHELL_DRAG_AND_DROP", "at": "com\/android\/wm\/shell\/draganddrop\/DragLayout.java" }, - "-460572385": { - "message": "Task appeared taskId=%d", + "-679492476": { + "message": "%s onTaskAppeared Primary taskId=%d", "level": "VERBOSE", "group": "WM_SHELL_TASK_ORG", - "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java" }, "-191422040": { "message": "Transition animations finished, notifying core %s", @@ -85,6 +103,12 @@ "group": "WM_SHELL_TRANSITIONS", "at": "com\/android\/wm\/shell\/Transitions.java" }, + "154313206": { + "message": "%s onTaskAppeared Secondary taskId=%d", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java" + }, "157713005": { "message": "Task info changed taskId=%d", "level": "VERBOSE", @@ -162,6 +186,12 @@ "level": "VERBOSE", "group": "WM_SHELL_DRAG_AND_DROP", "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java" + }, + "2135461748": { + "message": "%s onTaskAppeared Supported", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java" } }, "groups": { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 5234fa0df77f3..d4ff275d426dd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -20,8 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; @@ -29,6 +27,7 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG import android.annotation.IntDef; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration.WindowingMode; +import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; import android.util.Log; @@ -63,15 +62,13 @@ public class ShellTaskOrganizer extends TaskOrganizer { public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2; public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3; public static final int TASK_LISTENER_TYPE_PIP = -4; - public static final int TASK_LISTENER_TYPE_SPLIT_SCREEN = -5; - public static final int TASK_LISTENER_TYPE_LETTERBOX = -6; + public static final int TASK_LISTENER_TYPE_LETTERBOX = -5; @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = { TASK_LISTENER_TYPE_UNDEFINED, TASK_LISTENER_TYPE_FULLSCREEN, TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_PIP, - TASK_LISTENER_TYPE_SPLIT_SCREEN, TASK_LISTENER_TYPE_LETTERBOX, }) public @interface TaskListenerType {} @@ -139,6 +136,14 @@ public class ShellTaskOrganizer extends TaskOrganizer { } } + public void createRootTask(int displayId, int windowingMode, TaskListener listener) { + ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s", + displayId, windowingMode, listener.toString()); + final IBinder cookie = new Binder(); + setPendingLaunchCookieListener(cookie, listener); + super.createRootTask(displayId, windowingMode, cookie); + } + /** * Adds a listener for a specific task id. */ @@ -238,10 +243,10 @@ public class ShellTaskOrganizer extends TaskOrganizer { private void onTaskAppeared(TaskAppearedInfo info) { final int taskId = info.getTaskInfo().taskId; - ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d", taskId); mTasks.put(taskId, info); final TaskListener listener = getTaskListener(info.getTaskInfo(), true /*removeLaunchCookieIfNeeded*/); + ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d listener=%s", taskId, listener); if (listener != null) { listener.onTaskAppeared(info.getTaskInfo(), info.getLeash()); } @@ -336,7 +341,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { } @WindowingMode - private static int getWindowingMode(RunningTaskInfo taskInfo) { + public static int getWindowingMode(RunningTaskInfo taskInfo) { return taskInfo.configuration.windowConfiguration.getWindowingMode(); } @@ -350,9 +355,6 @@ public class ShellTaskOrganizer extends TaskOrganizer { : TASK_LISTENER_TYPE_FULLSCREEN; case WINDOWING_MODE_MULTI_WINDOW: return TASK_LISTENER_TYPE_MULTI_WINDOW; - case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: - case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: - return TASK_LISTENER_TYPE_SPLIT_SCREEN; case WINDOWING_MODE_PINNED: return TASK_LISTENER_TYPE_PIP; case WINDOWING_MODE_FREEFORM: @@ -370,8 +372,6 @@ public class ShellTaskOrganizer extends TaskOrganizer { return "TASK_LISTENER_TYPE_LETTERBOX"; case TASK_LISTENER_TYPE_MULTI_WINDOW: return "TASK_LISTENER_TYPE_MULTI_WINDOW"; - case TASK_LISTENER_TYPE_SPLIT_SCREEN: - return "TASK_LISTENER_TYPE_SPLIT_SCREEN"; case TASK_LISTENER_TYPE_PIP: return "TASK_LISTENER_TYPE_PIP"; case TASK_LISTENER_TYPE_UNDEFINED: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java index ff617ed466d1b..eb82357f2dea7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java @@ -40,7 +40,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor private static final float ADJUSTED_NONFOCUS_DIM = 0.3f; - private final SplitScreenTaskOrganizer mSplits; + private final SplitScreenTaskListener mSplits; private final TransactionPool mTransactionPool; private final Handler mHandler; private final TaskOrganizer mTaskOrganizer; @@ -92,7 +92,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor private boolean mPausedTargetAdjusted = false; private boolean mAdjustedWhileHidden = false; - DividerImeController(SplitScreenTaskOrganizer splits, TransactionPool pool, Handler handler, + DividerImeController(SplitScreenTaskListener splits, TransactionPool pool, Handler handler, TaskOrganizer taskOrganizer) { mSplits = splits; mTransactionPool = pool; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java index 2b14e8bf88d6f..c6496ad6246ff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java @@ -155,7 +155,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private boolean mAdjustedForIme; private DividerState mState; - private SplitScreenTaskOrganizer mTiles; + private SplitScreenTaskListener mTiles; boolean mFirstLayout = true; int mDividerPositionX; int mDividerPositionY; @@ -354,7 +354,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, void injectDependencies(SplitScreenController splitScreenController, DividerWindowManager windowManager, DividerState dividerState, - DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl, + DividerCallbacks callback, SplitScreenTaskListener tiles, SplitDisplayLayout sdl, DividerImeController imeController, WindowManagerProxy wmProxy) { mSplitScreenController = splitScreenController; mWindowManager = windowManager; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java index 3c0f939067958..7d5e1a84a38d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java @@ -47,7 +47,7 @@ public class SplitDisplayLayout { private static final int DIVIDER_WIDTH_INACTIVE_DP = 4; - SplitScreenTaskOrganizer mTiles; + SplitScreenTaskListener mTiles; DisplayLayout mDisplayLayout; Context mContext; @@ -62,7 +62,7 @@ public class SplitDisplayLayout { Rect mAdjustedPrimary = null; Rect mAdjustedSecondary = null; - public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskOrganizer taskTiles) { + public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskListener taskTiles) { mTiles = taskTiles; mDisplayLayout = dl; mContext = ctx; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 43e4d62baaf69..69d428a3ae1fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -75,7 +75,7 @@ public class SplitScreenController implements SplitScreen, private final DividerState mDividerState = new DividerState(); private final ForcedResizableInfoActivityController mForcedResizableController; private final Handler mHandler; - private final SplitScreenTaskOrganizer mSplits; + private final SplitScreenTaskListener mSplits; private final SystemWindows mSystemWindows; final TransactionPool mTransactionPool; private final WindowManagerProxy mWindowManagerProxy; @@ -117,7 +117,7 @@ public class SplitScreenController implements SplitScreen, mTransactionPool = transactionPool; mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer); mTaskOrganizer = shellTaskOrganizer; - mSplits = new SplitScreenTaskOrganizer(this, shellTaskOrganizer); + mSplits = new SplitScreenTaskListener(this, shellTaskOrganizer); mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler, shellTaskOrganizer); mRotationController = @@ -164,6 +164,14 @@ public class SplitScreenController implements SplitScreen, // Don't initialize the divider or anything until we get the default display. } + void onSplitScreenSupported() { + // Set starting tile bounds based on middle target + final WindowContainerTransaction tct = new WindowContainerTransaction(); + int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; + mSplitLayout.resizeSplits(midPos, tct); + mTaskOrganizer.applyTransaction(tct); + } + @Override public boolean isSplitScreenSupported() { return mSplits.isSplitScreenSupported(); @@ -196,11 +204,6 @@ public class SplitScreenController implements SplitScreen, } try { mSplits.init(); - // Set starting tile bounds based on middle target - final WindowContainerTransaction tct = new WindowContainerTransaction(); - int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; - mSplitLayout.resizeSplits(midPos, tct); - mTaskOrganizer.applyTransaction(tct); } catch (Exception e) { Slog.e(TAG, "Failed to register docked stack listener", e); removeDivider(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java similarity index 83% rename from libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java index f763d6d714c44..191a317452e35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java @@ -23,25 +23,24 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_SPLIT_SCREEN; -import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; +import static com.android.wm.shell.ShellTaskOrganizer.getWindowingMode; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import android.app.ActivityManager.RunningTaskInfo; import android.graphics.Rect; -import android.os.RemoteException; import android.util.Log; -import android.view.Display; import android.view.SurfaceControl; import android.view.SurfaceSession; import androidx.annotation.NonNull; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import java.io.PrintWriter; -class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { - private static final String TAG = "SplitScreenTaskOrg"; +class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { + private static final String TAG = "SplitScreenTaskListener"; private static final boolean DEBUG = SplitScreenController.DEBUG; private final ShellTaskOrganizer mTaskOrganizer; @@ -58,20 +57,19 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { final SurfaceSession mSurfaceSession = new SurfaceSession(); - SplitScreenTaskOrganizer(SplitScreenController splitScreenController, + SplitScreenTaskListener(SplitScreenController splitScreenController, ShellTaskOrganizer shellTaskOrganizer) { mSplitScreenController = splitScreenController; mTaskOrganizer = shellTaskOrganizer; - mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_SPLIT_SCREEN); } - void init() throws RemoteException { + void init() { synchronized (this) { try { - mPrimary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - mSecondary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + mTaskOrganizer.createRootTask( + DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, this); + mTaskOrganizer.createRootTask( + DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, this); } catch (Exception e) { // teardown to prevent callbacks mTaskOrganizer.removeListener(this); @@ -95,19 +93,26 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (this) { - if (mPrimary == null || mSecondary == null) { - Log.w(TAG, "Received onTaskAppeared before creating root tasks " + taskInfo); - return; - } - - if (taskInfo.token.equals(mPrimary.token)) { + final int winMode = getWindowingMode(taskInfo); + if (winMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + ProtoLog.v(WM_SHELL_TASK_ORG, + "%s onTaskAppeared Primary taskId=%d", TAG, taskInfo.taskId); + mPrimary = taskInfo; mPrimarySurface = leash; - } else if (taskInfo.token.equals(mSecondary.token)) { + } else if (winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { + ProtoLog.v(WM_SHELL_TASK_ORG, + "%s onTaskAppeared Secondary taskId=%d", TAG, taskInfo.taskId); + mSecondary = taskInfo; mSecondarySurface = leash; + } else { + ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared unknown taskId=%d winMode=%d", + TAG, taskInfo.taskId, winMode); } if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) { mSplitScreenSupported = true; + mSplitScreenController.onSplitScreenSupported(); + ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG); // Initialize dim surfaces: mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession) @@ -240,10 +245,13 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; pw.println(prefix + this); + pw.println(innerPrefix + "mSplitScreenSupported=" + mSplitScreenSupported); + if (mPrimary != null) pw.println(innerPrefix + "mPrimary.taskId=" + mPrimary.taskId); + if (mSecondary != null) pw.println(innerPrefix + "mSecondary.taskId=" + mSecondary.taskId); } @Override public String toString() { - return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_SPLIT_SCREEN); + return TAG; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java index 47e7c99d22681..c51bbeb7b6c28 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java @@ -88,7 +88,7 @@ class WindowManagerProxy { mTaskOrganizer = taskOrganizer; } - void dismissOrMaximizeDocked(final SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout, + void dismissOrMaximizeDocked(final SplitScreenTaskListener tiles, SplitDisplayLayout layout, final boolean dismissOrMaximize) { mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize)); } @@ -189,7 +189,7 @@ class WindowManagerProxy { * * @return whether the home stack is resizable */ - boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) { + boolean applyEnterSplit(SplitScreenTaskListener tiles, SplitDisplayLayout layout) { // Set launchtile first so that any stack created after // getAllRootTaskInfos and before reparent (even if unlikely) are placed // correctly. @@ -242,7 +242,7 @@ class WindowManagerProxy { * split (thus resulting in the top of the secondary split becoming * fullscreen. {@code false} resolves the other way. */ - void applyDismissSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout, + void applyDismissSplit(SplitScreenTaskListener tiles, SplitDisplayLayout layout, boolean dismissOrMaximize) { // Set launch root first so that any task created after getChildContainers and // before reparent (pretty unlikely) are put into fullscreen. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ca4982f44a417..67eb482df1408 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -795,6 +795,10 @@ class Task extends WindowContainer { */ boolean mTaskAppearedSent; + // If the sending of the task appear signal should be deferred until this flag is set back to + // false. + private boolean mDeferTaskAppear; + /** * This task was created by the task organizer which has the following implementations. *
    @@ -807,14 +811,20 @@ class Task extends WindowContainer { @VisibleForTesting boolean mCreatedByOrganizer; + // Tracking cookie for the creation of this task. + IBinder mLaunchCookie; + /** * Don't use constructor directly. Use {@link TaskDisplayArea#createStackUnchecked()} instead. */ - Task(ActivityTaskManagerService atmService, int id, int activityType, - ActivityInfo info, Intent intent, boolean createdByOrganizer) { + Task(ActivityTaskManagerService atmService, int id, int activityType, ActivityInfo info, + Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, + IBinder launchCookie) { this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/, null /*taskDescription*/, null /*stack*/); mCreatedByOrganizer = createdByOrganizer; + mLaunchCookie = launchCookie; + mDeferTaskAppear = deferTaskAppear; setActivityType(activityType); } @@ -4088,6 +4098,7 @@ class Task extends WindowContainer { info.topActivityInfo = mReuseActivitiesReport.top != null ? mReuseActivitiesReport.top.info : null; + info.addLaunchCookie(mLaunchCookie); forAllActivities(r -> { info.addLaunchCookie(r.mLaunchCookie); }); @@ -4857,6 +4868,13 @@ class Task extends WindowContainer { return mHasBeenVisible; } + void setDeferTaskAppear(boolean deferTaskAppear) { + mDeferTaskAppear = deferTaskAppear; + if (!mDeferTaskAppear) { + sendTaskAppeared(); + } + } + /** In the case that these conditions are true, we want to send the Task to the organizer: * 1. An organizer has been set * 2. The Task was created by the organizer @@ -4871,6 +4889,10 @@ class Task extends WindowContainer { return false; } + if (mDeferTaskAppear) { + return false; + } + if (mCreatedByOrganizer) { return true; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 9392666fbf54d..68cb3173cfd72 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -50,6 +50,7 @@ import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.os.IBinder; import android.os.UserHandle; import android.util.IntArray; import android.util.Slog; @@ -996,6 +997,13 @@ final class TaskDisplayArea extends DisplayArea { false /* createdByOrganizer */); } + Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info, + Intent intent, boolean createdByOrganizer) { + return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */, + false /* createdByOrganizer */ , false /* deferTaskAppear */, + null /* launchCookie */); + } + /** * Creates a stack matching the input windowing mode and activity type on this display. * @@ -1013,10 +1021,14 @@ final class TaskDisplayArea extends DisplayArea { * @param intent The intent that started this task. * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false} * otherwise. + * @param deferTaskAppear @{code true} if the task appeared signal should be deferred. + * @param launchCookie Launch cookie used for tracking/association of the task we are + * creating. * @return The newly created stack. */ Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info, - Intent intent, boolean createdByOrganizer) { + Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, + IBinder launchCookie) { if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) { // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants // anything else should be passing it in anyways...except for the task organizer. @@ -1048,7 +1060,7 @@ final class TaskDisplayArea extends DisplayArea { final int stackId = getNextStackId(); return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent, - createdByOrganizer); + createdByOrganizer, deferTaskAppear, launchCookie); } /** @return the root task to create the next task in. */ @@ -1078,8 +1090,9 @@ final class TaskDisplayArea extends DisplayArea { } @VisibleForTesting - Task createStackUnchecked(int windowingMode, int activityType, int stackId, - boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) { + Task createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop, + ActivityInfo info, Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, + IBinder launchCookie) { if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) { throw new IllegalArgumentException("Stack with windowing mode cannot with non standard " + "activity type."); @@ -1097,7 +1110,7 @@ final class TaskDisplayArea extends DisplayArea { } final Task stack = new Task(mAtmService, stackId, activityType, - info, intent, createdByOrganizer); + info, intent, createdByOrganizer, deferTaskAppear, launchCookie); if (launchRootTask != null) { launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); if (onTop) { diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 5f86c2f60db74..a70efbcf55005 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -156,9 +156,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) { - if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) { - // Skip if the task has not yet received taskAppeared(), except for tasks created - // by the organizer that don't receive that signal + if (!task.mTaskAppearedSent) { + // Skip if the task has not yet received taskAppeared(). return; } ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId); @@ -179,9 +178,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onBackPressedOnTaskRoot(Task task) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task back pressed on root taskId=%d", task.mTaskId); - if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) { - // Skip if the task has not yet received taskAppeared(), except for tasks created - // by the organizer that don't receive that signal + if (!task.mTaskAppearedSent) { + // Skip if the task has not yet received taskAppeared(). return; } if (!task.isOrganized()) { @@ -402,30 +400,39 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public RunningTaskInfo createRootTask(int displayId, int windowingMode) { + public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) { enforceStackPermission("createRootTask()"); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId); if (display == null) { - return null; + ProtoLog.e(WM_DEBUG_WINDOW_ORGANIZER, + "createRootTask unknown displayId=%d", displayId); + return; } - ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d", - displayId, windowingMode); - final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode, - ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(), - true /* createdByOrganizer */); - RunningTaskInfo out = task.getTaskInfo(); - mLastSentTaskInfos.put(task, out); - return out; + createRootTask(display, windowingMode, launchCookie); } } finally { Binder.restoreCallingIdentity(origId); } } + @VisibleForTesting + Task createRootTask(DisplayContent display, int windowingMode, @Nullable IBinder launchCookie) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d", + display.mDisplayId, windowingMode); + // We want to defer the task appear signal until the task is fully created and attached to + // to the hierarchy so that the complete starting configuration is in the task info we send + // over to the organizer. + final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode, + ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(), + true /* createdByOrganizer */, true /* deferTaskAppear */, launchCookie); + task.setDeferTaskAppear(false /* deferTaskAppear */); + return task; + } + @Override public boolean deleteRootTask(WindowContainerToken token) { enforceStackPermission("deleteRootTask()"); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index caf8a720e26c4..b6323313dd27e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -245,9 +245,8 @@ public class ActivityStackTests extends WindowTestsBase { .setStack(rootHomeTask) .setCreateTask(true) .build(); - final Task secondaryStack = (Task) WindowContainer.fromBinder( - mAtm.mTaskOrganizerController.createRootTask(rootHomeTask.getDisplayId(), - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token.asBinder()); + final Task secondaryStack = mAtm.mTaskOrganizerController.createRootTask( + rootHomeTask.getDisplayContent(), WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); rootHomeTask.reparent(secondaryStack, POSITION_TOP); assertEquals(secondaryStack, rootHomeTask.getParent()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index e47881917b2c2..3720e520b1b93 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1022,7 +1022,7 @@ public class ActivityStarterTests extends WindowTestsBase { assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse(); // Move activity to split-screen-primary stack and make sure it has the focus. - TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayId()); + TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent()); top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM); top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent"); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 7a1f65a3b62cf..b4480aea3ce48 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -479,21 +479,22 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testCreateDeleteRootTasks() { - RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); + + Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( + dc, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); + RunningTaskInfo info1 = task1.getTaskInfo(); assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, info1.configuration.windowConfiguration.getWindowingMode()); assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); - RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( + dc, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); + RunningTaskInfo info2 = task2.getTaskInfo(); assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, info2.configuration.windowConfiguration.getWindowingMode()); assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType); - DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); List infos = getTasksCreatedByOrganizer(dc); assertEquals(2, infos.size()); @@ -521,8 +522,9 @@ public class WindowOrganizerTests extends WindowTestsBase { } }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); - RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( + mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); + RunningTaskInfo info1 = task.getTaskInfo(); final Task stack = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); @@ -579,8 +581,9 @@ public class WindowOrganizerTests extends WindowTestsBase { } }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); - RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( + mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); + RunningTaskInfo info1 = task.getTaskInfo(); lastReportedTiles.clear(); called[0] = false; @@ -640,10 +643,13 @@ public class WindowOrganizerTests extends WindowTestsBase { } }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); - RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + + Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( + mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); + RunningTaskInfo info1 = task1.getTaskInfo(); + Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( + mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); + RunningTaskInfo info2 = task2.getTaskInfo(); final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( mDisplayContent.mDisplayId, null /* activityTypes */).size(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 1eb7cbed02c85..62f04a1c830a1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -973,8 +973,9 @@ class WindowTestsBase extends SystemServiceTestsBase { final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextStackId(); if (mParentTask == null) { task = mTaskDisplayArea.createStackUnchecked( - mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, - mIntent, false /* createdByOrganizer */); + mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, mIntent, + false /* createdByOrganizer */, false /* deferTaskAppear */, + null /* launchCookie */); } else { task = new Task(mSupervisor.mService, taskId, mActivityInfo, mIntent /*intent*/, mVoiceSession, null /*_voiceInteractor*/, @@ -1016,20 +1017,17 @@ class WindowTestsBase extends SystemServiceTestsBase { // moves everything to secondary. Most tests expect this since sysui usually does it. boolean mMoveToSecondaryOnEnter = true; int mDisplayId; - TestSplitOrganizer(ActivityTaskManagerService service, int displayId) { + TestSplitOrganizer(ActivityTaskManagerService service, DisplayContent display) { mService = service; - mDisplayId = displayId; + mDisplayId = display.mDisplayId; mService.mTaskOrganizerController.registerTaskOrganizer(this); - WindowContainerToken primary = mService.mTaskOrganizerController.createRootTask( - displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token; - mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask(); - WindowContainerToken secondary = mService.mTaskOrganizerController.createRootTask( - displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token; - mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask(); + mPrimary = mService.mTaskOrganizerController.createRootTask( + display, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); + mSecondary = mService.mTaskOrganizerController.createRootTask( + display, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);; } TestSplitOrganizer(ActivityTaskManagerService service) { - this(service, - service.mStackSupervisor.mRootWindowContainer.getDefaultDisplay().mDisplayId); + this(service, service.mStackSupervisor.mRootWindowContainer.getDefaultDisplay()); } public void setMoveToSecondaryOnEnter(boolean move) { mMoveToSecondaryOnEnter = move;