Loading core/java/android/app/ActivityOptions.java +30 −0 Original line number Diff line number Diff line Loading @@ -221,6 +221,13 @@ public class ActivityOptions { */ private static final String KEY_AVOID_MOVE_TO_FRONT = "android.activity.avoidMoveToFront"; /** * See {@link #setFreezeRecentTasksReordering()}. * @hide */ private static final String KEY_FREEZE_RECENT_TASKS_REORDERING = "android.activity.freezeRecentTasksReordering"; /** * Where the split-screen-primary stack should be positioned. * @hide Loading Loading @@ -324,6 +331,7 @@ public class ActivityOptions { private boolean mTaskOverlay; private boolean mTaskOverlayCanResume; private boolean mAvoidMoveToFront; private boolean mFreezeRecentTasksReordering; private AppTransitionAnimationSpec mAnimSpecs[]; private int mRotationAnimationHint = -1; private Bundle mAppVerificationBundle; Loading Loading @@ -946,6 +954,7 @@ public class ActivityOptions { mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false); mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false); mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false); mFreezeRecentTasksReordering = opts.getBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, false); mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT); mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean( Loading Loading @@ -1304,6 +1313,24 @@ public class ActivityOptions { return mAvoidMoveToFront; } /** * Sets whether the launch of this activity should freeze the recent task list reordering until * the next user interaction or timeout. This flag is only applied when starting an activity * in recents. * @hide */ public void setFreezeRecentTasksReordering() { mFreezeRecentTasksReordering = true; } /** * @return whether the launch of this activity should freeze the recent task list reordering * @hide */ public boolean freezeRecentTasksReordering() { return mFreezeRecentTasksReordering; } /** @hide */ public int getSplitScreenCreateMode() { return mSplitScreenCreateMode; Loading Loading @@ -1502,6 +1529,9 @@ public class ActivityOptions { if (mAvoidMoveToFront) { b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront); } if (mFreezeRecentTasksReordering) { b.putBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, mFreezeRecentTasksReordering); } if (mSplitScreenCreateMode != SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT) { b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode); } Loading packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +8 −0 Original line number Diff line number Diff line Loading @@ -78,4 +78,12 @@ public abstract class ActivityOptionsCompat { } }); } /** * Sets the flag to freeze the recents task list reordering as a part of launching the activity. */ public static ActivityOptions setFreezeRecentTasksList(ActivityOptions opts) { opts.setFreezeRecentTasksReordering(); return opts; } } services/core/java/com/android/server/wm/ActivityStackSupervisor.java +3 −0 Original line number Diff line number Diff line Loading @@ -2736,6 +2736,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (activityOptions != null) { activityType = activityOptions.getLaunchActivityType(); windowingMode = activityOptions.getLaunchWindowingMode(); if (activityOptions.freezeRecentTasksReordering()) { mRecentTasks.setFreezeTaskListReordering(); } } if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { throw new IllegalArgumentException("startActivityFromRecents: Task " Loading services/core/java/com/android/server/wm/DisplayContent.java +29 −0 Original line number Diff line number Diff line Loading @@ -41,12 +41,14 @@ import static android.view.View.GONE; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; Loading Loading @@ -182,6 +184,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ToBooleanFunction; import com.android.internal.util.function.TriConsumer; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AnimationThread; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.DisplayRotationUtil; Loading Loading @@ -887,6 +891,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTapDetector = new TaskTapPointerEventListener(mWmService, this); registerPointerEventListener(mTapDetector); registerPointerEventListener(mWmService.mMousePositionTracker); if (mWmService.mAtmService.getRecentTasks() != null) { registerPointerEventListener( mWmService.mAtmService.getRecentTasks().getInputListener()); } mDisplayPolicy = new DisplayPolicy(service, this); mDisplayRotation = new DisplayRotation(service, this); Loading Loading @@ -2380,6 +2388,27 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return -1; } /** * Returns true if the input point is within an app window. */ boolean pointWithinAppWindow(int x, int y) { final int[] targetWindowType = {-1}; final Consumer fn = PooledLambda.obtainConsumer((w, nonArg) -> { if (targetWindowType[0] != -1) { return; } if (w.isOnScreen() && w.isVisibleLw() && w.getFrameLw().contains(x, y)) { targetWindowType[0] = w.mAttrs.type; return; } }, PooledLambda.__(WindowState.class), mTmpRect); forAllWindows(fn, true /* traverseTopToBottom */); ((PooledConsumer) fn).recycle(); return FIRST_APPLICATION_WINDOW <= targetWindowType[0] && targetWindowType[0] <= LAST_APPLICATION_WINDOW; } /** * Find the task whose outside touch area (for resizing) (x, y) falls within. * Returns null if the touch doesn't fall into a resizing area. Loading services/core/java/com/android/server/wm/RecentTasks.java +141 −22 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; Loading @@ -67,8 +68,11 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.MotionEvent; import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.am.ActivityManagerService; import com.google.android.collect.Sets; Loading Loading @@ -109,8 +113,11 @@ class RecentTasks { private static final int DEFAULT_INITIAL_CAPACITY = 5; // Whether or not to move all affiliated tasks to the front when one of the tasks is launched private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false; // The duration of time after freezing the recent tasks list where getRecentTasks() will return // a stable ordering of the tasks. Upon the next call to getRecentTasks() beyond this duration, // the task list will be unfrozen and committed (the current top task will be moved to the // front of the list) private static final long FREEZE_TASK_LIST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5); // Comparator to sort by taskId private static final Comparator<TaskRecord> TASK_ID_COMPARATOR = Loading Loading @@ -174,12 +181,45 @@ class RecentTasks { private int mMaxNumVisibleTasks; private long mActiveTasksSessionDurationMs; // When set, the task list will not be reordered as tasks within the list are moved to the // front. Newly created tasks, or tasks that are removed from the list will continue to change // the list. This does not affect affiliated tasks. private boolean mFreezeTaskListReordering; private long mFreezeTaskListReorderingTime; private long mFreezeTaskListTimeoutMs = FREEZE_TASK_LIST_TIMEOUT_MS; // Mainly to avoid object recreation on multiple calls. private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>(); private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>(); private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>(); private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app private final PointerEventListener mListener = new PointerEventListener() { @Override public void onPointerEvent(MotionEvent ev) { if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN) { // Skip if we aren't freezing or starting a gesture return; } int displayId = ev.getDisplayId(); int x = (int) ev.getX(); int y = (int) ev.getY(); mService.mH.post(PooledLambda.obtainRunnable((nonArg) -> { synchronized (mService.mGlobalLock) { // Unfreeze the task list once we touch down in a task final RootActivityContainer rac = mService.mRootActivityContainer; final DisplayContent dc = rac.getActivityDisplay(displayId).mDisplayContent; if (dc.pointWithinAppWindow(x, y)) { final ActivityStack stack = mService.getTopDisplayFocusedStack(); final TaskRecord topTask = stack != null ? stack.topTask() : null; resetFreezeTaskListReordering(topTask); } } }, null).recycleOnUse()); } }; @VisibleForTesting RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) { mService = service; Loading Loading @@ -214,6 +254,73 @@ class RecentTasks { mGlobalMaxNumTasks = globalMaxNumTasks; } @VisibleForTesting void setFreezeTaskListTimeoutParams(long reorderingTime, long timeoutMs) { mFreezeTaskListReorderingTime = reorderingTime; mFreezeTaskListTimeoutMs = timeoutMs; } PointerEventListener getInputListener() { return mListener; } /** * Freezes the current recent task list order until either a user interaction with the current * app, or a timeout occurs. */ void setFreezeTaskListReordering() { // Always update the reordering time when this is called to ensure that the timeout // is reset mFreezeTaskListReordering = true; mFreezeTaskListReorderingTime = SystemClock.elapsedRealtime(); } /** * Commits the frozen recent task list order, moving the provided {@param topTask} to the * front of the list. */ void resetFreezeTaskListReordering(TaskRecord topTask) { if (!mFreezeTaskListReordering) { return; } // Once we end freezing the task list, reset the existing task order to the stable state mFreezeTaskListReordering = false; // If the top task is provided, then restore the top task to the front of the list if (topTask != null) { mTasks.remove(topTask); mTasks.add(0, topTask); } // Resume trimming tasks trimInactiveRecentTasks(); } /** * Resets the frozen recent task list order if the timeout has passed. This should be called * before we need to iterate the task list in order (either for purposes of returning the list * to SystemUI or if we need to trim tasks in order) */ void resetFreezeTaskListReorderingOnTimeout() { // Unfreeze the recent task list if the time heuristic has passed if (mFreezeTaskListReorderingTime > (SystemClock.elapsedRealtime() - mFreezeTaskListTimeoutMs)) { return; } final ActivityStack focusedStack = mService.getTopDisplayFocusedStack(); final TaskRecord topTask = focusedStack != null ? focusedStack.topTask() : null; resetFreezeTaskListReordering(topTask); } @VisibleForTesting boolean isFreezeTaskListReorderingSet() { return mFreezeTaskListReordering; } /** * Loads the parameters from the system resources. */ Loading Loading @@ -351,7 +458,8 @@ class RecentTasks { } Slog.i(TAG, "Loading recents for user " + userId + " into memory."); mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks)); List<TaskRecord> tasks = mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks); mTasks.addAll(tasks); cleanupLocked(userId); mUsersWithRecentsLoaded.put(userId, true); Loading Loading @@ -746,11 +854,10 @@ class RecentTasks { getDetailedTasks, userId, callingUid)); } /** * @return the list of recent tasks for presentation. */ ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags, private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags, boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) { final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0; Loading @@ -763,6 +870,9 @@ class RecentTasks { final Set<Integer> includedUsers = getProfileIds(userId); includedUsers.add(Integer.valueOf(userId)); // Check if the frozen task list has timed out resetFreezeTaskListReorderingOnTimeout(); final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>(); final int size = mTasks.size(); int numVisibleTasks = 0; Loading Loading @@ -946,24 +1056,20 @@ class RecentTasks { if (task.inRecents) { int taskIndex = mTasks.indexOf(task); if (taskIndex >= 0) { if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) { // Simple case: this is not an affiliated task, so we just move it to the front. if (!isAffiliated) { if (!mFreezeTaskListReordering) { // Simple case: this is not an affiliated task, so we just move it to the // front unless overridden by the provided activity options mTasks.remove(taskIndex); mTasks.add(0, task); notifyTaskPersisterLocked(task, false); if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task if (DEBUG_RECENTS) { Slog.d(TAG_RECENTS, "addRecent: moving to top " + task + " from " + taskIndex); return; } else { // More complicated: need to keep all affiliated tasks together. if (moveAffiliatedTasksToFront(task, taskIndex)) { // All went well. return; } // Uh oh... something bad in the affiliation chain, try to rebuild // everything and then go through our general path of adding a new task. needAffiliationFix = true; } notifyTaskPersisterLocked(task, false); return; } } else { Slog.wtf(TAG, "Task with inRecent not in recents: " + task); Loading Loading @@ -1063,6 +1169,11 @@ class RecentTasks { * Trims the recents task list to the global max number of recents. */ private void trimInactiveRecentTasks() { if (mFreezeTaskListReordering) { // Defer trimming inactive recent tasks until we are unfrozen return; } int recentsCount = mTasks.size(); // Remove from the end of the list until we reach the max number of recents Loading @@ -1086,7 +1197,7 @@ class RecentTasks { + " quiet=" + mTmpQuietProfileUserIds.get(userId)); } // Remove any inactive tasks, calculate the latest set of visible tasks // Remove any inactive tasks, calculate the latest set of visible tasks. int numVisibleTasks = 0; for (int i = 0; i < mTasks.size();) { final TaskRecord task = mTasks.get(i); Loading Loading @@ -1300,6 +1411,11 @@ class RecentTasks { * list (if any). */ private int findRemoveIndexForAddTask(TaskRecord task) { if (mFreezeTaskListReordering) { // Defer removing tasks due to the addition of new tasks until the task list is unfrozen return -1; } final int recentsCount = mTasks.size(); final Intent intent = task.intent; final boolean document = intent != null && intent.isDocument(); Loading Loading @@ -1536,6 +1652,9 @@ class RecentTasks { pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)"); pw.println("mRecentsUid=" + mRecentsUid); pw.println("mRecentsComponent=" + mRecentsComponent); pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering); pw.println("mFreezeTaskListReorderingTime (time since)=" + (SystemClock.elapsedRealtime() - mFreezeTaskListReorderingTime) + "ms"); if (mTasks.isEmpty()) { return; } Loading Loading
core/java/android/app/ActivityOptions.java +30 −0 Original line number Diff line number Diff line Loading @@ -221,6 +221,13 @@ public class ActivityOptions { */ private static final String KEY_AVOID_MOVE_TO_FRONT = "android.activity.avoidMoveToFront"; /** * See {@link #setFreezeRecentTasksReordering()}. * @hide */ private static final String KEY_FREEZE_RECENT_TASKS_REORDERING = "android.activity.freezeRecentTasksReordering"; /** * Where the split-screen-primary stack should be positioned. * @hide Loading Loading @@ -324,6 +331,7 @@ public class ActivityOptions { private boolean mTaskOverlay; private boolean mTaskOverlayCanResume; private boolean mAvoidMoveToFront; private boolean mFreezeRecentTasksReordering; private AppTransitionAnimationSpec mAnimSpecs[]; private int mRotationAnimationHint = -1; private Bundle mAppVerificationBundle; Loading Loading @@ -946,6 +954,7 @@ public class ActivityOptions { mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false); mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false); mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false); mFreezeRecentTasksReordering = opts.getBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, false); mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT); mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean( Loading Loading @@ -1304,6 +1313,24 @@ public class ActivityOptions { return mAvoidMoveToFront; } /** * Sets whether the launch of this activity should freeze the recent task list reordering until * the next user interaction or timeout. This flag is only applied when starting an activity * in recents. * @hide */ public void setFreezeRecentTasksReordering() { mFreezeRecentTasksReordering = true; } /** * @return whether the launch of this activity should freeze the recent task list reordering * @hide */ public boolean freezeRecentTasksReordering() { return mFreezeRecentTasksReordering; } /** @hide */ public int getSplitScreenCreateMode() { return mSplitScreenCreateMode; Loading Loading @@ -1502,6 +1529,9 @@ public class ActivityOptions { if (mAvoidMoveToFront) { b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront); } if (mFreezeRecentTasksReordering) { b.putBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, mFreezeRecentTasksReordering); } if (mSplitScreenCreateMode != SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT) { b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode); } Loading
packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +8 −0 Original line number Diff line number Diff line Loading @@ -78,4 +78,12 @@ public abstract class ActivityOptionsCompat { } }); } /** * Sets the flag to freeze the recents task list reordering as a part of launching the activity. */ public static ActivityOptions setFreezeRecentTasksList(ActivityOptions opts) { opts.setFreezeRecentTasksReordering(); return opts; } }
services/core/java/com/android/server/wm/ActivityStackSupervisor.java +3 −0 Original line number Diff line number Diff line Loading @@ -2736,6 +2736,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (activityOptions != null) { activityType = activityOptions.getLaunchActivityType(); windowingMode = activityOptions.getLaunchWindowingMode(); if (activityOptions.freezeRecentTasksReordering()) { mRecentTasks.setFreezeTaskListReordering(); } } if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { throw new IllegalArgumentException("startActivityFromRecents: Task " Loading
services/core/java/com/android/server/wm/DisplayContent.java +29 −0 Original line number Diff line number Diff line Loading @@ -41,12 +41,14 @@ import static android.view.View.GONE; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; Loading Loading @@ -182,6 +184,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ToBooleanFunction; import com.android.internal.util.function.TriConsumer; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AnimationThread; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.DisplayRotationUtil; Loading Loading @@ -887,6 +891,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTapDetector = new TaskTapPointerEventListener(mWmService, this); registerPointerEventListener(mTapDetector); registerPointerEventListener(mWmService.mMousePositionTracker); if (mWmService.mAtmService.getRecentTasks() != null) { registerPointerEventListener( mWmService.mAtmService.getRecentTasks().getInputListener()); } mDisplayPolicy = new DisplayPolicy(service, this); mDisplayRotation = new DisplayRotation(service, this); Loading Loading @@ -2380,6 +2388,27 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return -1; } /** * Returns true if the input point is within an app window. */ boolean pointWithinAppWindow(int x, int y) { final int[] targetWindowType = {-1}; final Consumer fn = PooledLambda.obtainConsumer((w, nonArg) -> { if (targetWindowType[0] != -1) { return; } if (w.isOnScreen() && w.isVisibleLw() && w.getFrameLw().contains(x, y)) { targetWindowType[0] = w.mAttrs.type; return; } }, PooledLambda.__(WindowState.class), mTmpRect); forAllWindows(fn, true /* traverseTopToBottom */); ((PooledConsumer) fn).recycle(); return FIRST_APPLICATION_WINDOW <= targetWindowType[0] && targetWindowType[0] <= LAST_APPLICATION_WINDOW; } /** * Find the task whose outside touch area (for resizing) (x, y) falls within. * Returns null if the touch doesn't fall into a resizing area. Loading
services/core/java/com/android/server/wm/RecentTasks.java +141 −22 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; Loading @@ -67,8 +68,11 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.MotionEvent; import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.am.ActivityManagerService; import com.google.android.collect.Sets; Loading Loading @@ -109,8 +113,11 @@ class RecentTasks { private static final int DEFAULT_INITIAL_CAPACITY = 5; // Whether or not to move all affiliated tasks to the front when one of the tasks is launched private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false; // The duration of time after freezing the recent tasks list where getRecentTasks() will return // a stable ordering of the tasks. Upon the next call to getRecentTasks() beyond this duration, // the task list will be unfrozen and committed (the current top task will be moved to the // front of the list) private static final long FREEZE_TASK_LIST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5); // Comparator to sort by taskId private static final Comparator<TaskRecord> TASK_ID_COMPARATOR = Loading Loading @@ -174,12 +181,45 @@ class RecentTasks { private int mMaxNumVisibleTasks; private long mActiveTasksSessionDurationMs; // When set, the task list will not be reordered as tasks within the list are moved to the // front. Newly created tasks, or tasks that are removed from the list will continue to change // the list. This does not affect affiliated tasks. private boolean mFreezeTaskListReordering; private long mFreezeTaskListReorderingTime; private long mFreezeTaskListTimeoutMs = FREEZE_TASK_LIST_TIMEOUT_MS; // Mainly to avoid object recreation on multiple calls. private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>(); private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>(); private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>(); private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app private final PointerEventListener mListener = new PointerEventListener() { @Override public void onPointerEvent(MotionEvent ev) { if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN) { // Skip if we aren't freezing or starting a gesture return; } int displayId = ev.getDisplayId(); int x = (int) ev.getX(); int y = (int) ev.getY(); mService.mH.post(PooledLambda.obtainRunnable((nonArg) -> { synchronized (mService.mGlobalLock) { // Unfreeze the task list once we touch down in a task final RootActivityContainer rac = mService.mRootActivityContainer; final DisplayContent dc = rac.getActivityDisplay(displayId).mDisplayContent; if (dc.pointWithinAppWindow(x, y)) { final ActivityStack stack = mService.getTopDisplayFocusedStack(); final TaskRecord topTask = stack != null ? stack.topTask() : null; resetFreezeTaskListReordering(topTask); } } }, null).recycleOnUse()); } }; @VisibleForTesting RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) { mService = service; Loading Loading @@ -214,6 +254,73 @@ class RecentTasks { mGlobalMaxNumTasks = globalMaxNumTasks; } @VisibleForTesting void setFreezeTaskListTimeoutParams(long reorderingTime, long timeoutMs) { mFreezeTaskListReorderingTime = reorderingTime; mFreezeTaskListTimeoutMs = timeoutMs; } PointerEventListener getInputListener() { return mListener; } /** * Freezes the current recent task list order until either a user interaction with the current * app, or a timeout occurs. */ void setFreezeTaskListReordering() { // Always update the reordering time when this is called to ensure that the timeout // is reset mFreezeTaskListReordering = true; mFreezeTaskListReorderingTime = SystemClock.elapsedRealtime(); } /** * Commits the frozen recent task list order, moving the provided {@param topTask} to the * front of the list. */ void resetFreezeTaskListReordering(TaskRecord topTask) { if (!mFreezeTaskListReordering) { return; } // Once we end freezing the task list, reset the existing task order to the stable state mFreezeTaskListReordering = false; // If the top task is provided, then restore the top task to the front of the list if (topTask != null) { mTasks.remove(topTask); mTasks.add(0, topTask); } // Resume trimming tasks trimInactiveRecentTasks(); } /** * Resets the frozen recent task list order if the timeout has passed. This should be called * before we need to iterate the task list in order (either for purposes of returning the list * to SystemUI or if we need to trim tasks in order) */ void resetFreezeTaskListReorderingOnTimeout() { // Unfreeze the recent task list if the time heuristic has passed if (mFreezeTaskListReorderingTime > (SystemClock.elapsedRealtime() - mFreezeTaskListTimeoutMs)) { return; } final ActivityStack focusedStack = mService.getTopDisplayFocusedStack(); final TaskRecord topTask = focusedStack != null ? focusedStack.topTask() : null; resetFreezeTaskListReordering(topTask); } @VisibleForTesting boolean isFreezeTaskListReorderingSet() { return mFreezeTaskListReordering; } /** * Loads the parameters from the system resources. */ Loading Loading @@ -351,7 +458,8 @@ class RecentTasks { } Slog.i(TAG, "Loading recents for user " + userId + " into memory."); mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks)); List<TaskRecord> tasks = mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks); mTasks.addAll(tasks); cleanupLocked(userId); mUsersWithRecentsLoaded.put(userId, true); Loading Loading @@ -746,11 +854,10 @@ class RecentTasks { getDetailedTasks, userId, callingUid)); } /** * @return the list of recent tasks for presentation. */ ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags, private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags, boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) { final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0; Loading @@ -763,6 +870,9 @@ class RecentTasks { final Set<Integer> includedUsers = getProfileIds(userId); includedUsers.add(Integer.valueOf(userId)); // Check if the frozen task list has timed out resetFreezeTaskListReorderingOnTimeout(); final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>(); final int size = mTasks.size(); int numVisibleTasks = 0; Loading Loading @@ -946,24 +1056,20 @@ class RecentTasks { if (task.inRecents) { int taskIndex = mTasks.indexOf(task); if (taskIndex >= 0) { if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) { // Simple case: this is not an affiliated task, so we just move it to the front. if (!isAffiliated) { if (!mFreezeTaskListReordering) { // Simple case: this is not an affiliated task, so we just move it to the // front unless overridden by the provided activity options mTasks.remove(taskIndex); mTasks.add(0, task); notifyTaskPersisterLocked(task, false); if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task if (DEBUG_RECENTS) { Slog.d(TAG_RECENTS, "addRecent: moving to top " + task + " from " + taskIndex); return; } else { // More complicated: need to keep all affiliated tasks together. if (moveAffiliatedTasksToFront(task, taskIndex)) { // All went well. return; } // Uh oh... something bad in the affiliation chain, try to rebuild // everything and then go through our general path of adding a new task. needAffiliationFix = true; } notifyTaskPersisterLocked(task, false); return; } } else { Slog.wtf(TAG, "Task with inRecent not in recents: " + task); Loading Loading @@ -1063,6 +1169,11 @@ class RecentTasks { * Trims the recents task list to the global max number of recents. */ private void trimInactiveRecentTasks() { if (mFreezeTaskListReordering) { // Defer trimming inactive recent tasks until we are unfrozen return; } int recentsCount = mTasks.size(); // Remove from the end of the list until we reach the max number of recents Loading @@ -1086,7 +1197,7 @@ class RecentTasks { + " quiet=" + mTmpQuietProfileUserIds.get(userId)); } // Remove any inactive tasks, calculate the latest set of visible tasks // Remove any inactive tasks, calculate the latest set of visible tasks. int numVisibleTasks = 0; for (int i = 0; i < mTasks.size();) { final TaskRecord task = mTasks.get(i); Loading Loading @@ -1300,6 +1411,11 @@ class RecentTasks { * list (if any). */ private int findRemoveIndexForAddTask(TaskRecord task) { if (mFreezeTaskListReordering) { // Defer removing tasks due to the addition of new tasks until the task list is unfrozen return -1; } final int recentsCount = mTasks.size(); final Intent intent = task.intent; final boolean document = intent != null && intent.isDocument(); Loading Loading @@ -1536,6 +1652,9 @@ class RecentTasks { pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)"); pw.println("mRecentsUid=" + mRecentsUid); pw.println("mRecentsComponent=" + mRecentsComponent); pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering); pw.println("mFreezeTaskListReorderingTime (time since)=" + (SystemClock.elapsedRealtime() - mFreezeTaskListReorderingTime) + "ms"); if (mTasks.isEmpty()) { return; } Loading