diff options
28 files changed, 1589 insertions, 1055 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 2ba6e01b8966..dff43edc3013 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -16,13 +16,6 @@ package android.app; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -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 android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; @@ -1439,7 +1432,6 @@ public class ActivityManager { } dest.writeInt(stackId); dest.writeInt(userId); - dest.writeLong(firstActiveTime); dest.writeLong(lastActiveTime); dest.writeInt(affiliatedTaskId); dest.writeInt(affiliatedTaskColor); @@ -1468,7 +1460,6 @@ public class ActivityManager { TaskDescription.CREATOR.createFromParcel(source) : null; stackId = source.readInt(); userId = source.readInt(); - firstActiveTime = source.readLong(); lastActiveTime = source.readLong(); affiliatedTaskId = source.readInt(); affiliatedTaskColor = source.readInt(); @@ -1511,31 +1502,6 @@ public class ActivityManager { public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002; /** - * Provides a list that contains recent tasks for all - * profiles of a user. - * @hide - */ - public static final int RECENT_INCLUDE_PROFILES = 0x0004; - - /** - * Ignores all tasks that are on the home stack. - * @hide - */ - public static final int RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS = 0x0008; - - /** - * Ignores the top task in the docked stack. - * @hide - */ - public static final int RECENT_INGORE_DOCKED_STACK_TOP_TASK = 0x0010; - - /** - * Ignores all tasks that are on the pinned stack. - * @hide - */ - public static final int RECENT_INGORE_PINNED_STACK_TASKS = 0x0020; - - /** * <p></p>Return a list of the tasks that the user has recently launched, with * the most recent being first and older ones after in order. * @@ -1570,33 +1536,7 @@ public class ActivityManager { public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags) throws SecurityException { try { - return getService().getRecentTasks(maxNum, - flags, UserHandle.myUserId()).getList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Same as {@link #getRecentTasks(int, int)} but returns the recent tasks for a - * specific user. It requires holding - * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. - * @param maxNum The maximum number of entries to return in the list. The - * actual number returned may be smaller, depending on how many tasks the - * user has started and the maximum number the system can remember. - * @param flags Information about what to return. May be any combination - * of {@link #RECENT_WITH_EXCLUDED} and {@link #RECENT_IGNORE_UNAVAILABLE}. - * - * @return Returns a list of RecentTaskInfo records describing each of - * the recent tasks. Most recently activated tasks go first. - * - * @hide - */ - public List<RecentTaskInfo> getRecentTasksForUser(int maxNum, int flags, int userId) - throws SecurityException { - try { - return getService().getRecentTasks(maxNum, - flags, userId).getList(); + return getService().getRecentTasks(maxNum, flags, UserHandle.myUserId()).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index f03951607678..117854a60499 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -178,8 +178,8 @@ interface IActivityManager { * SIGUSR1 is delivered. All others are ignored. */ void signalPersistentProcesses(int signal); - ParceledListSlice getRecentTasks(int maxNum, - int flags, int userId); + + ParceledListSlice getRecentTasks(int maxNum, int flags, int userId); oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res); oneway void activityDestroyed(in IBinder token); IIntentSender getIntentSender(int type, in String packageName, in IBinder token, diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a062db43c5ef..584dad0428c5 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6792,14 +6792,6 @@ public final class Settings { "lock_screen_show_notifications"; /** - * This preference stores the last stack active task time for each user, which affects what - * tasks will be visible in Overview. - * @hide - */ - public static final String OVERVIEW_LAST_STACK_ACTIVE_TIME = - "overview_last_stack_active_time"; - - /** * List of TV inputs that are currently hidden. This is a string * containing the IDs of all hidden TV inputs. Each ID is encoded by * {@link android.net.Uri#encode(String)} and separated by ':'. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index dcb56a2ddfcc..570f37cb0e8d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2352,9 +2352,37 @@ <!-- Package name for default network scorer app; overridden by product overlays. --> <string name="config_defaultNetworkScorerPackageName"></string> - <!-- default device has recents property --> + <!-- Determines whether recent tasks are provided to the user. Default device has recents + property. If this is false, then the following recents config flags are ignored. --> <bool name="config_hasRecents">true</bool> + <!-- The minimum number of visible recent tasks to be presented to the user through the + SystemUI. Can be -1 if there is no minimum limit. --> + <integer name="config_minNumVisibleRecentTasks_grid">-1</integer> + + <!-- The maximum number of visible recent tasks to be presented to the user through the + SystemUI. Can be -1 if there is no maximum limit. --> + <integer name="config_maxNumVisibleRecentTasks_grid">9</integer> + + <!-- The minimum number of visible recent tasks to be presented to the user through the + SystemUI. Can be -1 if there is no minimum limit. --> + <integer name="config_minNumVisibleRecentTasks_lowRam">-1</integer> + + <!-- The maximum number of visible recent tasks to be presented to the user through the + SystemUI. Can be -1 if there is no maximum limit. --> + <integer name="config_maxNumVisibleRecentTasks_lowRam">9</integer> + + <!-- The minimum number of visible recent tasks to be presented to the user through the + SystemUI. Can be -1 if there is no minimum limit. --> + <integer name="config_minNumVisibleRecentTasks">5</integer> + + <!-- The maximum number of visible recent tasks to be presented to the user through the + SystemUI. Can be -1 if there is no maximum limit. --> + <integer name="config_maxNumVisibleRecentTasks">-1</integer> + + <!-- The duration in which a recent task is considered in session and should be visible. --> + <integer name="config_activeTaskDurationHours">6</integer> + <!-- default window ShowCircularMask property --> <bool name="config_windowShowCircularMask">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 44a10e8178d1..5ac120b3ddfe 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -311,6 +311,13 @@ <java-symbol type="bool" name="config_enableMultiUserUI"/> <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/> <java-symbol type="bool" name="config_hasRecents" /> + <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" /> + <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" /> + <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" /> + <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_grid" /> + <java-symbol type="integer" name="config_minNumVisibleRecentTasks" /> + <java-symbol type="integer" name="config_maxNumVisibleRecentTasks" /> + <java-symbol type="integer" name="config_activeTaskDurationHours" /> <java-symbol type="bool" name="config_windowShowCircularMask" /> <java-symbol type="bool" name="config_windowEnableCircularEmulatorDisplayOverlay" /> <java-symbol type="bool" name="config_wifi_framework_enable_associated_network_selection" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 67315362f67b..5848649227b3 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -463,7 +463,6 @@ public class SettingsBackupTest { Settings.Secure.NFC_PAYMENT_FOREGROUND, Settings.Secure.NIGHT_DISPLAY_ACTIVATED, Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, - Settings.Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, Settings.Secure.PACKAGE_VERIFIER_STATE, Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT, Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE, diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 86b77900fdbc..ed447c645df1 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -188,41 +188,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { // When switching users, dismiss Recents to Home similar to screen off finish(); - } else if (action.equals(Intent.ACTION_TIME_CHANGED)) { - // If the time shifts but the currentTime >= lastStackActiveTime, then that boundary - // is still valid. Otherwise, we need to reset the lastStackactiveTime to the - // currentTime and remove the old tasks in between which would not be previously - // visible, but currently would be in the new currentTime - int currentUser = SystemServicesProxy.getInstance(RecentsActivity.this) - .getCurrentUser(); - long oldLastStackActiveTime = Settings.Secure.getLongForUser(getContentResolver(), - Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, -1, currentUser); - if (oldLastStackActiveTime != -1) { - long currentTime = System.currentTimeMillis(); - if (currentTime < oldLastStackActiveTime) { - // We are only removing tasks that are between the new current time - // and the old last stack active time, they were not visible and in the - // TaskStack so we don't need to remove any associated TaskViews but we do - // need to load the task id's from the system - RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(ctx); - loader.preloadRawTasks(loadPlan, false /* includeFrontMostExcludedTask */); - List<ActivityManager.RecentTaskInfo> tasks = loadPlan.getRawTasks(); - for (int i = tasks.size() - 1; i >= 0; i--) { - ActivityManager.RecentTaskInfo task = tasks.get(i); - if (currentTime <= task.lastActiveTime && task.lastActiveTime < - oldLastStackActiveTime) { - Recents.getSystemServices().removeTask(task.persistentId); - } - } - Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync( - currentTime, currentUser); - - // Clear the last PiP task time, it's an edge case and we'd rather it - // not relaunch the PiP task if the user double taps - RecentsImpl.clearLastPipTime(); - } - } } } }; @@ -383,7 +348,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD // Register the broadcast receiver to handle messages when the screen is turned off IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_USER_SWITCHED); registerReceiver(mSystemBroadcastReceiver, filter); @@ -467,8 +431,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); if (!loadPlan.hasTasks()) { - loader.preloadTasks(loadPlan, launchState.launchedToTaskId, - !launchState.launchedFromHome && !launchState.launchedViaDockGesture); + loader.preloadTasks(loadPlan, launchState.launchedToTaskId); } RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); @@ -889,8 +852,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD RecentsActivityLaunchState launchState = config.getLaunchState(); RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this); - loader.preloadTasks(loadPlan, -1 /* runningTaskId */, - false /* includeFrontMostExcludedTask */); + loader.preloadTasks(loadPlan, -1 /* runningTaskId */); RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; @@ -931,13 +893,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD Recents.getTaskLoader().dump(prefix, writer); String id = Integer.toHexString(System.identityHashCode(this)); - long lastStackActiveTime = Settings.Secure.getLongForUser(getContentResolver(), - Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, -1, - SystemServicesProxy.getInstance(this).getCurrentUser()); writer.print(prefix); writer.print(TAG); writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N"); - writer.print(" lastStackTaskActiveTime="); writer.print(lastStackActiveTime); writer.print(" currentTime="); writer.print(System.currentTimeMillis()); writer.print(" [0x"); writer.print(id); writer.print("]"); writer.println(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 3e2a5f3f2043..8dd3bdc49d09 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -142,7 +142,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask(); RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); - loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */); + loader.preloadTasks(plan, -1); TaskStack stack = plan.getTaskStack(); RecentsActivityLaunchState launchState = new RecentsActivityLaunchState(); RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options(); @@ -283,7 +283,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // We can use a new plan since the caches will be the same. RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); - loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */); + loader.preloadTasks(plan, -1); RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options(); launchOpts.numVisibleTasks = loader.getIconCacheSize(); launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize(); @@ -473,8 +473,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // RecentsActivity) only if there is a task to animate to. Post this to ensure that we // don't block the touch feedback on the nav bar button which triggers this. mHandler.post(() -> { - MutableBoolean isHomeStackVisible = new MutableBoolean(true); - if (!ssp.isRecentsActivityVisible(isHomeStackVisible)) { + if (!ssp.isRecentsActivityVisible(null)) { ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); if (runningTask == null) { return; @@ -482,7 +481,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener RecentsTaskLoader loader = Recents.getTaskLoader(); sInstanceLoadPlan = loader.createLoadPlan(mContext); - loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible.value); + loader.preloadTasks(sInstanceLoadPlan, runningTask.id); TaskStack stack = sInstanceLoadPlan.getTaskStack(); if (stack.getTaskCount() > 0) { // Only preload the icon (but not the thumbnail since it may not have been taken @@ -522,7 +521,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener SystemServicesProxy ssp = Recents.getSystemServices(); RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); - loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */); + loader.preloadTasks(plan, -1); TaskStack focusedStack = plan.getTaskStack(); // Return early if there are no tasks in the focused stack @@ -577,7 +576,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener SystemServicesProxy ssp = Recents.getSystemServices(); RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); - loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */); + loader.preloadTasks(plan, -1); TaskStack focusedStack = plan.getTaskStack(); // Return early if there are no tasks in the focused stack @@ -1012,7 +1011,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener sInstanceLoadPlan = loader.createLoadPlan(mContext); } if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) { - loader.preloadTasks(sInstanceLoadPlan, runningTaskId, !isHomeStackVisible); + loader.preloadTasks(sInstanceLoadPlan, runningTaskId); } TaskStack stack = sInstanceLoadPlan.getTaskStack(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index bddf9a5983db..bc3adaf58fa6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -16,6 +16,7 @@ package com.android.systemui.recents.misc; +import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -68,7 +69,6 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.provider.Settings.Secure; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.util.ArraySet; @@ -385,20 +385,15 @@ public class SystemServicesProxy { /** * Returns a list of the recents tasks. - * - * @param includeFrontMostExcludedTask if set, will ensure that the front most excluded task - * will be visible, otherwise no excluded tasks will be - * visible. */ - public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId, - boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) { + public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) { if (mAm == null) return null; // If we are mocking, then create some recent tasks if (RecentsDebugFlags.Static.EnableMockTasks) { ArrayList<ActivityManager.RecentTaskInfo> tasks = new ArrayList<ActivityManager.RecentTaskInfo>(); - int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount); + int count = Math.min(numTasks, RecentsDebugFlags.Static.MockTaskCount); for (int i = 0; i < count; i++) { // Create a dummy component name int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount; @@ -412,7 +407,7 @@ public class SystemServicesProxy { rti.baseIntent = new Intent(); rti.baseIntent.setComponent(cn); rti.description = description; - rti.firstActiveTime = rti.lastActiveTime = i; + rti.lastActiveTime = i; if (i % 2 == 0) { rti.taskDescription = new ActivityManager.TaskDescription(description, Bitmap.createBitmap(mDummyIcon), null, @@ -427,57 +422,24 @@ public class SystemServicesProxy { return tasks; } - // Remove home/recents/excluded tasks - int minNumTasksToQuery = 10; - int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks); - int flags = ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS | - ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK | - ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS | - ActivityManager.RECENT_IGNORE_UNAVAILABLE | - ActivityManager.RECENT_INCLUDE_PROFILES; - if (includeFrontMostExcludedTask) { - flags |= ActivityManager.RECENT_WITH_EXCLUDED; - } - List<ActivityManager.RecentTaskInfo> tasks = null; try { - tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId); + List<ActivityManager.RecentTaskInfo> tasks = mIam.getRecentTasks(numTasks, + RECENT_IGNORE_UNAVAILABLE, userId).getList(); + Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); + while (iter.hasNext()) { + ActivityManager.RecentTaskInfo t = iter.next(); + + // Remove the task if it or it's package are blacklsited + if (sRecentsBlacklist.contains(t.realActivity.getClassName()) || + sRecentsBlacklist.contains(t.realActivity.getPackageName())) { + iter.remove(); + } + } + return tasks; } catch (Exception e) { Log.e(TAG, "Failed to get recent tasks", e); - } - - // Break early if we can't get a valid set of tasks - if (tasks == null) { return new ArrayList<>(); } - - boolean isFirstValidTask = true; - Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); - while (iter.hasNext()) { - ActivityManager.RecentTaskInfo t = iter.next(); - - // NOTE: The order of these checks happens in the expected order of the traversal of the - // tasks - - // Remove the task if it or it's package are blacklsited - if (sRecentsBlacklist.contains(t.realActivity.getClassName()) || - sRecentsBlacklist.contains(t.realActivity.getPackageName())) { - iter.remove(); - continue; - } - - // Remove the task if it is marked as excluded, unless it is the first most task and we - // are requested to include it - boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; - isExcluded |= quietProfileIds.contains(t.userId); - if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) { - iter.remove(); - } - - isFirstValidTask = false; - } - - return tasks.subList(0, Math.min(tasks.size(), numLatestTasks)); } /** @@ -1292,14 +1254,6 @@ public class SystemServicesProxy { }); } - public void updateOverviewLastStackActiveTimeAsync(long newLastStackActiveTime, - int currentUserId) { - mUiOffloadThread.submit(() -> { - Settings.Secure.putLongForUser(mContext.getContentResolver(), - Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, newLastStackActiveTime, currentUserId); - }); - } - public interface StartActivityFromRecentsResultListener { void onStartActivityResult(boolean succeeded); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index d5e031355810..3a357e0e1dcd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -57,10 +57,6 @@ import java.util.List; */ public class RecentsTaskLoadPlan { - private static int MIN_NUM_TASKS = 5; - private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ * - 6 /* hrs */; - /** The set of conditions to load tasks. */ public static class Options { public int runningTaskId = -1; @@ -74,44 +70,24 @@ public class RecentsTaskLoadPlan { Context mContext; - int mPreloadedUserId; List<ActivityManager.RecentTaskInfo> mRawTasks; TaskStack mStack; - ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>(); /** Package level ctor */ RecentsTaskLoadPlan(Context context) { mContext = context; } - private void updateCurrentQuietProfilesCache(int currentUserId) { - mCurrentQuietProfiles.clear(); - - UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - List<UserInfo> profiles = userManager.getProfiles(currentUserId); - if (profiles != null) { - for (int i = 0; i < profiles.size(); i++) { - UserInfo user = profiles.get(i); - if (user.isManagedProfile() && user.isQuietModeEnabled()) { - mCurrentQuietProfiles.add(user.id); - } - } - } - } - /** * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent * to most-recent order. * * Note: Do not lock, callers should synchronize on the loader before making this call. */ - void preloadRawTasks(boolean includeFrontMostExcludedTask) { + void preloadRawTasks() { SystemServicesProxy ssp = Recents.getSystemServices(); int currentUserId = ssp.getCurrentUser(); - updateCurrentQuietProfilesCache(currentUserId); - mPreloadedUserId = currentUserId; - mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), - currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles); + mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), currentUserId); // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it Collections.reverse(mRawTasks); @@ -129,12 +105,11 @@ public class RecentsTaskLoadPlan { * Note: Do not lock, since this can be calling back to the loader, which separately also drives * this call (callers should synchronize on the loader before making this call). */ - void preloadPlan(RecentsTaskLoader loader, int runningTaskId, - boolean includeFrontMostExcludedTask) { + void preloadPlan(RecentsTaskLoader loader, int runningTaskId) { Resources res = mContext.getResources(); ArrayList<Task> allTasks = new ArrayList<>(); if (mRawTasks == null) { - preloadRawTasks(includeFrontMostExcludedTask); + preloadRawTasks(); } SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>(); @@ -144,14 +119,6 @@ public class RecentsTaskLoadPlan { R.string.accessibility_recents_item_will_be_dismissed); String appInfoDescFormat = mContext.getString( R.string.accessibility_recents_item_open_app_info); - int currentUserId = mPreloadedUserId; - long legacyLastStackActiveTime = migrateLegacyLastStackActiveTime(currentUserId); - long lastStackActiveTime = Settings.Secure.getLongForUser(mContext.getContentResolver(), - Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, legacyLastStackActiveTime, currentUserId); - if (RecentsDebugFlags.Static.EnableMockTasks) { - lastStackActiveTime = 0; - } - long newLastStackActiveTime = -1; int taskCount = mRawTasks.size(); for (int i = 0; i < taskCount; i++) { ActivityManager.RecentTaskInfo t = mRawTasks.get(i); @@ -159,35 +126,12 @@ public class RecentsTaskLoadPlan { // Compose the task key final int windowingMode = t.configuration.windowConfiguration.getWindowingMode(); Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, windowingMode, t.baseIntent, - t.userId, t.firstActiveTime, t.lastActiveTime); + t.userId, t.lastActiveTime); - // This task is only shown in the stack if it satisfies the historical time or min - // number of tasks constraints. Freeform tasks are also always shown. boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM; - boolean isStackTask; - if (Recents.getConfiguration().isGridEnabled) { - // When grid layout is enabled, we only show the first - // TaskGridLayoutAlgorithm.MAX_LAYOUT_FROM_HOME_TASK_COUNT} tasks. - isStackTask = t.lastActiveTime >= lastStackActiveTime && - i >= taskCount - TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT; - } else if (Recents.getConfiguration().isLowRamDevice) { - // Show a max of 3 items - isStackTask = t.lastActiveTime >= lastStackActiveTime && - i >= taskCount - TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT; - } else { - isStackTask = isFreeformTask || !isHistoricalTask(t) || - (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS)); - } + boolean isStackTask = !isFreeformTask; boolean isLaunchTarget = taskKey.id == runningTaskId; - // The last stack active time is the baseline for which we show visible tasks. Since - // the system will store all the tasks, we don't want to show the tasks prior to the - // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy - // the other stack-task constraints. - if (isStackTask && newLastStackActiveTime < 0) { - newLastStackActiveTime = t.lastActiveTime; - } - // Load the title, icon, and color ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey); String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription); @@ -213,17 +157,13 @@ public class RecentsTaskLoadPlan { Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon, thumbnail, title, titleDescription, dismissDescription, appInfoDescription, activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp, - t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode, t.topActivity, - isLocked); + t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode, + t.topActivity, isLocked); allTasks.add(task); affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1); affiliatedTasks.put(taskKey.id, taskKey); } - if (newLastStackActiveTime != -1) { - Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync( - newLastStackActiveTime, currentUserId); - } // Initialize the stacks mStack = new TaskStack(); @@ -289,42 +229,4 @@ public class RecentsTaskLoadPlan { } return false; } - - /** - * Returns whether this task is too old to be shown. - */ - private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) { - return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME); - } - - - /** - * Migrate the last active time from the prefs to the secure settings. - * - * The first time this runs, it will: - * 1) fetch the last stack active time from the prefs - * 2) set the prefs to the last stack active time for all users - * 3) clear the pref - * 4) return the last stack active time - * - * Subsequent calls to this will return zero. - */ - private long migrateLegacyLastStackActiveTime(int currentUserId) { - long legacyLastStackActiveTime = Prefs.getLong(mContext, - Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1); - if (legacyLastStackActiveTime != -1) { - Prefs.remove(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME); - UserManager userMgr = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - List<UserInfo> users = userMgr.getUsers(); - for (int i = 0; i < users.size(); i++) { - int userId = users.get(i).id; - if (userId != currentUserId) { - Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync( - legacyLastStackActiveTime, userId); - } - } - return legacyLastStackActiveTime; - } - return 0; - } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 1b893862cb7e..94558c3c3625 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -337,18 +337,11 @@ public class RecentsTaskLoader { return plan; } - /** Preloads raw recents tasks using the specified plan to store the output. */ - public synchronized void preloadRawTasks(RecentsTaskLoadPlan plan, - boolean includeFrontMostExcludedTask) { - plan.preloadRawTasks(includeFrontMostExcludedTask); - } - /** Preloads recents tasks using the specified plan to store the output. */ - public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId, - boolean includeFrontMostExcludedTask) { + public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) { try { Trace.beginSection("preloadPlan"); - plan.preloadPlan(this, runningTaskId, includeFrontMostExcludedTask); + plan.preloadPlan(this, runningTaskId); } finally { Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index abdb5cb8c2e3..b4715749ce71 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -69,13 +69,11 @@ public class Task { private int mHashCode; - public TaskKey(int id, int windowingMode, Intent intent, int userId, long firstActiveTime, - long lastActiveTime) { + public TaskKey(int id, int windowingMode, Intent intent, int userId, long lastActiveTime) { this.id = id; this.windowingMode = windowingMode; this.baseIntent = intent; this.userId = userId; - this.firstActiveTime = firstActiveTime; this.lastActiveTime = lastActiveTime; updateHashCode(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index fdae917cf1ab..0c8316b492f5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -960,12 +960,6 @@ public class TaskStack { ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>(); // Sort all tasks by increasing firstActiveTime of the task ArrayList<Task> tasks = mStackTaskList.getTasks(); - Collections.sort(tasks, new Comparator<Task>() { - @Override - public int compare(Task task, Task task2) { - return Long.compare(task.key.firstActiveTime, task2.key.firstActiveTime); - } - }); // Create groups when sequential packages are the same NamedCounter counter = new NamedCounter("task-group", ""); int taskCount = tasks.size(); @@ -1006,12 +1000,6 @@ public class TaskStack { int groupCount = mGroups.size(); for (int i = 0; i < groupCount; i++) { TaskGrouping group = mGroups.get(i); - Collections.sort(group.mTaskKeys, new Comparator<Task.TaskKey>() { - @Override - public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) { - return Long.compare(taskKey.firstActiveTime, taskKey2.firstActiveTime); - } - }); ArrayList<Task.TaskKey> groupTasks = group.mTaskKeys; int groupTaskCount = groupTasks.size(); for (int j = 0; j < groupTaskCount; j++) { diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java index 7699bb90e611..9211e3f6e86c 100644 --- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java @@ -16,6 +16,10 @@ package com.android.systemui.shortcut; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.os.UserHandle.USER_CURRENT; + import android.accessibilityservice.AccessibilityServiceInfo; import android.app.ActivityManager; import android.app.IActivityManager; @@ -102,11 +106,10 @@ public class ShortcutKeyDispatcher extends SystemUI // If there is no window docked, we dock the top-most window. Recents recents = getComponent(Recents.class); int dockMode = (shortcutCode == SC_DOCK_LEFT) - ? ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT - : ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; + ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT + : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; List<ActivityManager.RecentTaskInfo> taskList = - SystemServicesProxy.getInstance(mContext).getRecentTasks(1, - UserHandle.USER_CURRENT, false, new ArraySet<>()); + SystemServicesProxy.getInstance(mContext).getRecentTasks(1, USER_CURRENT); recents.showRecentApps( false /* triggeredFromAltTab */, false /* fromHome */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 9c837ed8fc74..b0ce57751f40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -639,12 +639,17 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } private Intent getTaskIntent(int taskId, int userId) { - List<ActivityManager.RecentTaskInfo> tasks = mContext.getSystemService(ActivityManager.class) - .getRecentTasksForUser(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId); - for (int i = 0; i < tasks.size(); i++) { - if (tasks.get(i).id == taskId) { - return tasks.get(i).baseIntent; + try { + final List<ActivityManager.RecentTaskInfo> tasks = + ActivityManager.getService().getRecentTasks( + NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId).getList(); + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i).id == taskId) { + return tasks.get(i).baseIntent; + } } + } catch (RemoteException e) { + // Fall through } return null; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 3a6819eb9105..9f039543e3e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -5881,8 +5881,7 @@ public class StatusBar extends SystemUI implements DemoMode, List<ActivityManager.RecentTaskInfo> recentTask = null; try { recentTask = ActivityManager.getService().getRecentTasks(1, - ActivityManager.RECENT_WITH_EXCLUDED - | ActivityManager.RECENT_INCLUDE_PROFILES, + ActivityManager.RECENT_WITH_EXCLUDED, mCurrentUserId).getList(); } catch (RemoteException e) { // Abandon hope activity manager not running. diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java index 767e1246e45b..4564c8c79814 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java @@ -60,7 +60,7 @@ public class HighResThumbnailLoaderTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mLoader = new HighResThumbnailLoader(mMockSystemServicesProxy, Looper.getMainLooper(), false); - mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, 0, 0, 0); + mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, 0, 0); when(mMockSystemServicesProxy.getTaskThumbnail(anyInt(), anyBoolean())) .thenReturn(mThumbnailData); mLoader.setVisible(true); diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 3a9bf1258d12..ceb2ad622b8d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -59,7 +59,6 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_FOCUS = false; static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false; - static final boolean DEBUG_LOCKSCREEN = DEBUG_ALL || false; static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false; static final boolean DEBUG_LRU = DEBUG_ALL || false; static final boolean DEBUG_MU = DEBUG_ALL || false; @@ -74,10 +73,10 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_PROVIDER = DEBUG_ALL || false; static final boolean DEBUG_PSS = DEBUG_ALL || false; static final boolean DEBUG_RECENTS = DEBUG_ALL || false; + static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false; static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_RESULTS = DEBUG_ALL || false; static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false; - static final boolean DEBUG_SCREENSHOTS = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_SERVICE = DEBUG_ALL || false; static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false; static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false; @@ -85,7 +84,6 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_SWITCH = DEBUG_ALL || false; static final boolean DEBUG_TASKS = DEBUG_ALL || false; - static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false; static final boolean DEBUG_TRANSITION = DEBUG_ALL || false; static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false; static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false; @@ -105,7 +103,6 @@ class ActivityManagerDebugConfig { static final String POSTFIX_FOCUS = (APPEND_CATEGORY_NAME) ? "_Focus" : ""; static final String POSTFIX_IDLE = (APPEND_CATEGORY_NAME) ? "_Idle" : ""; static final String POSTFIX_IMMERSIVE = (APPEND_CATEGORY_NAME) ? "_Immersive" : ""; - static final String POSTFIX_LOCKSCREEN = (APPEND_CATEGORY_NAME) ? "_LockScreen" : ""; static final String POSTFIX_LOCKTASK = (APPEND_CATEGORY_NAME) ? "_LockTask" : ""; static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : ""; static final String POSTFIX_MU = "_MU"; @@ -122,7 +119,6 @@ class ActivityManagerDebugConfig { static final String POSTFIX_RELEASE = (APPEND_CATEGORY_NAME) ? "_Release" : ""; static final String POSTFIX_RESULTS = (APPEND_CATEGORY_NAME) ? "_Results" : ""; static final String POSTFIX_SAVED_STATE = (APPEND_CATEGORY_NAME) ? "_SavedState" : ""; - static final String POSTFIX_SCREENSHOTS = (APPEND_CATEGORY_NAME) ? "_Screenshots" : ""; static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : ""; static final String POSTFIX_SERVICE_EXECUTING = (APPEND_CATEGORY_NAME) ? "_ServiceExecuting" : ""; @@ -130,13 +126,11 @@ class ActivityManagerDebugConfig { static final String POSTFIX_STATES = (APPEND_CATEGORY_NAME) ? "_States" : ""; static final String POSTFIX_SWITCH = (APPEND_CATEGORY_NAME) ? "_Switch" : ""; static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : ""; - static final String POSTFIX_THUMBNAILS = (APPEND_CATEGORY_NAME) ? "_Thumbnails" : ""; static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : ""; static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME) ? "_UidObservers" : ""; static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : ""; static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : ""; static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : ""; - static final String POSTFIX_VISIBLE_BEHIND = (APPEND_CATEGORY_NAME) ? "_VisibleBehind" : ""; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7ff73a121013..3955339a91a1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -31,7 +31,6 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; @@ -133,7 +132,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; @@ -1732,9 +1730,6 @@ public class ActivityManagerService extends IActivityManager.Stub */ private boolean mUserIsMonkey; - /** Flag whether the device has a Recents UI */ - boolean mHasRecents; - /** The dimensions of the thumbnails in the Recents UI. */ int mThumbnailWidth; int mThumbnailHeight; @@ -2782,6 +2777,7 @@ public class ActivityManagerService extends IActivityManager.Stub new TaskChangeNotificationController(this, mStackSupervisor, mHandler); mActivityStarter = new ActivityStarter(this, mStackSupervisor); mRecentTasks = new RecentTasks(this, mStackSupervisor); + mStackSupervisor.setRecentTasks(mRecentTasks); mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler); mProcessCpuThread = new Thread("CpuTracker") { @@ -5963,16 +5959,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (appInfo != null) { forceStopPackageLocked(packageName, appInfo.uid, "clear data"); - // Remove all tasks match the cleared application package and user - for (int i = mRecentTasks.size() - 1; i >= 0; i--) { - final TaskRecord tr = mRecentTasks.get(i); - final String taskPackageName = - tr.getBaseIntent().getComponent().getPackageName(); - if (tr.userId != resolvedUserId) continue; - if (!taskPackageName.equals(packageName)) continue; - mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, - REMOVE_FROM_RECENTS); - } + mRecentTasks.removeTasksByPackageName(packageName, resolvedUserId); } } @@ -6553,7 +6540,7 @@ public class ActivityManagerService extends IActivityManager.Stub } // Clean-up disabled tasks - cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId); + mRecentTasks.cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId); // Clean-up disabled services. mServices.bringDownDisabledPackageServicesLocked( @@ -9800,35 +9787,12 @@ public class ActivityManagerService extends IActivityManager.Stub public List<IBinder> getAppTasks(String callingPackage) { int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); - - synchronized(this) { - ArrayList<IBinder> list = new ArrayList<IBinder>(); - try { - if (DEBUG_ALL) Slog.v(TAG, "getAppTasks"); - - final int N = mRecentTasks.size(); - for (int i = 0; i < N; i++) { - TaskRecord tr = mRecentTasks.get(i); - // Skip tasks that do not match the caller. We don't need to verify - // callingPackage, because we are also limiting to callingUid and know - // that will limit to the correct security sandbox. - if (tr.effectiveUid != callingUid) { - continue; - } - Intent intent = tr.getBaseIntent(); - if (intent == null || - !callingPackage.equals(intent.getComponent().getPackageName())) { - continue; - } - ActivityManager.RecentTaskInfo taskInfo = - createRecentTaskInfoFromTaskRecord(tr); - AppTaskImpl taskImpl = new AppTaskImpl(taskInfo.persistentId, callingUid); - list.add(taskImpl.asBinder()); - } - } finally { - Binder.restoreCallingIdentity(ident); + try { + synchronized(this) { + return mRecentTasks.getAppTasksList(callingUid, callingPackage); } - return list; + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -9851,58 +9815,6 @@ public class ActivityManagerService extends IActivityManager.Stub return list; } - /** - * Creates a new RecentTaskInfo from a TaskRecord. - */ - private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) { - // Update the task description to reflect any changes in the task stack - tr.updateTaskDescription(); - - // Compose the recent task info - ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); - rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId; - rti.persistentId = tr.taskId; - rti.baseIntent = new Intent(tr.getBaseIntent()); - rti.origActivity = tr.origActivity; - rti.realActivity = tr.realActivity; - rti.description = tr.lastDescription; - rti.stackId = tr.getStackId(); - rti.userId = tr.userId; - rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription); - rti.firstActiveTime = tr.firstActiveTime; - rti.lastActiveTime = tr.lastActiveTime; - rti.affiliatedTaskId = tr.mAffiliatedTaskId; - rti.affiliatedTaskColor = tr.mAffiliatedTaskColor; - rti.numActivities = 0; - if (tr.mBounds != null) { - rti.bounds = new Rect(tr.mBounds); - } - rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode(); - rti.resizeMode = tr.mResizeMode; - rti.configuration.setTo(tr.getConfiguration()); - - ActivityRecord base = null; - ActivityRecord top = null; - ActivityRecord tmp; - - for (int i = tr.mActivities.size() - 1; i >= 0; --i) { - tmp = tr.mActivities.get(i); - if (tmp.finishing) { - continue; - } - base = tmp; - if (top == null || (top.state == ActivityState.INITIALIZING)) { - top = base; - } - rti.numActivities++; - } - - rti.baseActivity = (base != null) ? base.intent.getComponent() : null; - rti.topActivity = (top != null) ? top.intent.getComponent() : null; - - return rti; - } - private boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) { boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; @@ -9936,119 +9848,15 @@ public class ActivityManagerService extends IActivityManager.Stub final int callingUid = Binder.getCallingUid(); userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, false, ALLOW_FULL_ONLY, "getRecentTasks", null); + final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(), + callingUid); + final boolean detailed = checkCallingPermission( + android.Manifest.permission.GET_DETAILED_TASKS) + == PackageManager.PERMISSION_GRANTED; - final boolean includeProfiles = (flags & ActivityManager.RECENT_INCLUDE_PROFILES) != 0; - final boolean withExcluded = (flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0; synchronized (this) { - final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(), + return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId, callingUid); - final boolean detailed = checkCallingPermission( - android.Manifest.permission.GET_DETAILED_TASKS) - == PackageManager.PERMISSION_GRANTED; - - if (!isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) { - Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents"); - return ParceledListSlice.emptyList(); - } - mRecentTasks.loadUserRecentsLocked(userId); - - final int recentsCount = mRecentTasks.size(); - ArrayList<ActivityManager.RecentTaskInfo> res = - new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount); - - final Set<Integer> includedUsers; - if (includeProfiles) { - includedUsers = mUserController.getProfileIds(userId); - } else { - includedUsers = new HashSet<>(); - } - includedUsers.add(Integer.valueOf(userId)); - - for (int i = 0; i < recentsCount && maxNum > 0; i++) { - TaskRecord tr = mRecentTasks.get(i); - // Only add calling user or related users recent tasks - if (!includedUsers.contains(Integer.valueOf(tr.userId))) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr); - continue; - } - - if (tr.realActivitySuspended) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr); - continue; - } - - // Return the entry if desired by the caller. We always return - // the first entry, because callers always expect this to be the - // foreground app. We may filter others if the caller has - // not supplied RECENT_WITH_EXCLUDED and there is some reason - // we should exclude the entry. - - if (i == 0 - || withExcluded - || (tr.intent == null) - || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - == 0)) { - if (!allowed) { - // If the caller doesn't have the GET_TASKS permission, then only - // allow them to see a small subset of tasks -- their own and home. - if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr); - continue; - } - } - final ActivityStack stack = tr.getStack(); - if ((flags & ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS) != 0) { - if (stack != null && stack.isHomeOrRecentsStack()) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, home or recents stack task: " + tr); - continue; - } - } - if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) { - if (stack != null && stack.inSplitScreenPrimaryWindowingMode() - && stack.topTask() == tr) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, top task in docked stack: " + tr); - continue; - } - } - if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) { - if (stack != null && stack.inPinnedWindowingMode()) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, pinned stack task: " + tr); - continue; - } - } - if (tr.autoRemoveRecents && tr.getTopActivity() == null) { - // Don't include auto remove tasks that are finished or finishing. - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, auto-remove without activity: " + tr); - continue; - } - if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0 - && !tr.isAvailable) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, unavail real act: " + tr); - continue; - } - - if (!tr.mUserSetupComplete) { - // Don't include task launched while user is not done setting-up. - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, user setup not complete: " + tr); - continue; - } - - ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr); - if (!detailed) { - rti.baseIntent.replaceExtras((Bundle)null); - } - - res.add(rti); - maxNum--; - } - } - return new ParceledListSlice<>(res); } } @@ -10120,23 +9928,10 @@ public class ActivityManagerService extends IActivityManager.Stub TaskRecord task = new TaskRecord(this, mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent, description); - - int trimIdx = mRecentTasks.trimForTaskLocked(task, false); - if (trimIdx >= 0) { - // If this would have caused a trim, then we'll abort because that - // means it would be added at the end of the list but then just removed. + if (!mRecentTasks.addToBottom(task)) { return INVALID_TASK_ID; } - - final int N = mRecentTasks.size(); - if (N >= (ActivityManager.getMaxRecentTasksStatic()-1)) { - final TaskRecord tr = mRecentTasks.remove(N - 1); - tr.removedFromRecents(); - } - - task.inRecents = true; - mRecentTasks.add(task); - r.getStack().addTask(task, false, "addAppTask"); + r.getStack().addTask(task, !ON_TOP, "addAppTask"); // TODO: Send the thumbnail to WM to store it. @@ -10352,38 +10147,6 @@ public class ActivityManagerService extends IActivityManager.Stub mWindowManager.executeAppTransition(); } - private void removeTasksByPackageNameLocked(String packageName, int userId) { - // Remove all tasks with activities in the specified package from the list of recent tasks - for (int i = mRecentTasks.size() - 1; i >= 0; i--) { - TaskRecord tr = mRecentTasks.get(i); - if (tr.userId != userId) continue; - - ComponentName cn = tr.intent.getComponent(); - if (cn != null && cn.getPackageName().equals(packageName)) { - // If the package name matches, remove the task. - mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS); - } - } - } - - private void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses, - int userId) { - - for (int i = mRecentTasks.size() - 1; i >= 0; i--) { - TaskRecord tr = mRecentTasks.get(i); - if (userId != UserHandle.USER_ALL && tr.userId != userId) { - continue; - } - - ComponentName cn = tr.intent.getComponent(); - final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName) - && (filterByClasses == null || filterByClasses.contains(cn.getClassName())); - if (sameComponent) { - mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS); - } - } - } - @Override public void removeStack(int stackId) { enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()"); @@ -14148,7 +13911,6 @@ public class ActivityManagerService extends IActivityManager.Stub // Load resources only after the current configuration has been set. final Resources res = mContext.getResources(); - mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents); mThumbnailWidth = res.getDimensionPixelSize( com.android.internal.R.dimen.thumbnail_width); mThumbnailHeight = res.getDimensionPixelSize( @@ -15136,7 +14898,9 @@ public class ActivityManagerService extends IActivityManager.Stub } } else if ("recents".equals(cmd) || "r".equals(cmd)) { synchronized (this) { - dumpRecentsLocked(fd, pw, args, opti, true, dumpPackage); + if (mRecentTasks != null) { + mRecentTasks.dump(pw, true /* dumpAll */, dumpPackage); + } } } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) { String[] newArgs; @@ -15357,7 +15121,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage); + if (mRecentTasks != null) { + mRecentTasks.dump(pw, dumpAll, dumpPackage); + } pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); @@ -15427,7 +15193,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage); + if (mRecentTasks != null) { + mRecentTasks.dump(pw, dumpAll, dumpPackage); + } pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); @@ -15513,42 +15281,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - void dumpRecentsLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, String dumpPackage) { - pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)"); - - boolean printedAnything = false; - - if (mRecentTasks != null && mRecentTasks.size() > 0) { - boolean printedHeader = false; - - final int N = mRecentTasks.size(); - for (int i=0; i<N; i++) { - TaskRecord tr = mRecentTasks.get(i); - if (dumpPackage != null) { - if (tr.realActivity == null || - !dumpPackage.equals(tr.realActivity.getPackageName())) { - continue; - } - } - if (!printedHeader) { - pw.println(" Recent tasks:"); - printedHeader = true; - printedAnything = true; - } - pw.print(" * Recent #"); pw.print(i); pw.print(": "); - pw.println(tr); - if (dumpAll) { - mRecentTasks.get(i).dump(pw, " "); - } - } - } - - if (!printedAnything) { - pw.println(" (nothing)"); - } - } - void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)"); @@ -19363,7 +19095,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Remove all permissions granted from/to this package removeUriPermissionsForPackageLocked(ssp, userId, true); - removeTasksByPackageNameLocked(ssp, userId); + mRecentTasks.removeTasksByPackageName(ssp, userId); mServices.forceStopPackageLocked(ssp, userId); @@ -24371,125 +24103,6 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * An implementation of IAppTask, that allows an app to manage its own tasks via - * {@link android.app.ActivityManager.AppTask}. We keep track of the callingUid to ensure that - * only the process that calls getAppTasks() can call the AppTask methods. - */ - class AppTaskImpl extends IAppTask.Stub { - private int mTaskId; - private int mCallingUid; - - public AppTaskImpl(int taskId, int callingUid) { - mTaskId = taskId; - mCallingUid = callingUid; - } - - private void checkCaller() { - if (mCallingUid != Binder.getCallingUid()) { - throw new SecurityException("Caller " + mCallingUid - + " does not match caller of getAppTasks(): " + Binder.getCallingUid()); - } - } - - @Override - public void finishAndRemoveTask() { - checkCaller(); - - synchronized (ActivityManagerService.this) { - long origId = Binder.clearCallingIdentity(); - try { - // We remove the task from recents to preserve backwards - if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false, - REMOVE_FROM_RECENTS)) { - throw new IllegalArgumentException("Unable to find task ID " + mTaskId); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public ActivityManager.RecentTaskInfo getTaskInfo() { - checkCaller(); - - synchronized (ActivityManagerService.this) { - long origId = Binder.clearCallingIdentity(); - try { - TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(mTaskId); - if (tr == null) { - throw new IllegalArgumentException("Unable to find task ID " + mTaskId); - } - return createRecentTaskInfoFromTaskRecord(tr); - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - @Override - public void moveToFront() { - checkCaller(); - // Will bring task to front if it already has a root activity. - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (this) { - mStackSupervisor.startActivityFromRecentsInner(mTaskId, null); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - @Override - public int startActivity(IBinder whoThread, String callingPackage, - Intent intent, String resolvedType, Bundle bOptions) { - checkCaller(); - - int callingUser = UserHandle.getCallingUserId(); - TaskRecord tr; - IApplicationThread appThread; - synchronized (ActivityManagerService.this) { - tr = mStackSupervisor.anyTaskForIdLocked(mTaskId); - if (tr == null) { - throw new IllegalArgumentException("Unable to find task ID " + mTaskId); - } - appThread = IApplicationThread.Stub.asInterface(whoThread); - if (appThread == null) { - throw new IllegalArgumentException("Bad app thread " + appThread); - } - } - return mActivityStarter.startActivityMayWait(appThread, -1, callingPackage, intent, - resolvedType, null, null, null, null, 0, 0, null, null, - null, bOptions, false, callingUser, tr, "AppTaskImpl"); - } - - @Override - public void setExcludeFromRecents(boolean exclude) { - checkCaller(); - - synchronized (ActivityManagerService.this) { - long origId = Binder.clearCallingIdentity(); - try { - TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(mTaskId); - if (tr == null) { - throw new IllegalArgumentException("Unable to find task ID " + mTaskId); - } - Intent intent = tr.getBaseIntent(); - if (exclude) { - intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - } else { - intent.setFlags(intent.getFlags() - & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - } - - /** * Kill processes for the user with id userId and that depend on the package named packageName */ @Override diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 7075e67b9852..3d3bae3dc24f 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -16,7 +16,6 @@ package com.android.server.am; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -34,8 +33,8 @@ import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; - import static android.view.Display.INVALID_DISPLAY; + import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM; import static com.android.server.am.ActivityDisplay.POSITION_TOP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE; @@ -99,12 +98,13 @@ import static java.lang.Integer.MAX_VALUE; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityManager.StackId; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityController; import android.app.ResultInfo; import android.app.WindowConfiguration; +import android.app.WindowConfiguration.ActivityType; +import android.app.WindowConfiguration.WindowingMode; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -193,10 +193,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // finished destroying itself. private static final int DESTROY_TIMEOUT = 10 * 1000; - // How long until we reset a task when the user returns to it. Currently - // disabled. - private static final long ACTIVITY_INACTIVE_RESET_TIME = 0; - // Set to false to disable the preview that is shown while a new activity // is being started. private static final boolean SHOW_APP_STARTING_PREVIEW = true; @@ -2169,7 +2165,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mResumedActivity = r; r.state = ActivityState.RESUMED; mService.setResumedActivityUncheckLocked(r, reason); - mStackSupervisor.addRecentActivity(r); + mStackSupervisor.mRecentTasks.add(r.getTask()); } private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { @@ -3189,15 +3185,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop, ActivityRecord newActivity) { - boolean forceReset = + final boolean forceReset = (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; - if (ACTIVITY_INACTIVE_RESET_TIME > 0 - && taskTop.getTask().getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { - if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { - forceReset = true; - } - } - final TaskRecord task = taskTop.getTask(); /** False until we evaluate the TaskRecord associated with taskTop. Switches to true @@ -4467,7 +4456,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Don't refocus if invisible to current user final ActivityRecord top = tr.getTopActivity(); if (top == null || !top.okToShowLocked()) { - mStackSupervisor.addRecentActivity(top); + if (top != null) { + mStackSupervisor.mRecentTasks.add(top.getTask()); + } ActivityOptions.abort(options); return; } @@ -4892,7 +4883,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (focusedStack && topTask) { // Give the latest time to ensure foreground task can be sorted // at the first, because lastActiveTime of creating task is 0. - ci.lastActiveTime = System.currentTimeMillis(); + ci.lastActiveTime = SystemClock.elapsedRealtime(); topTask = false; } @@ -5072,7 +5063,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (task.autoRemoveFromRecents() || isVoiceSession) { // Task creator asked to remove this when done, or this task was a voice // interaction, so it should not remain on the recent tasks list. - mStackSupervisor.removeTaskFromRecents(task); + mStackSupervisor.mRecentTasks.remove(task); } task.removeWindowContainer(); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c5cb5bbcdd73..5221afd74fa9 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -46,6 +46,7 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.TYPE_VIRTUAL; + import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; @@ -85,12 +86,13 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; +import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS; import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID; import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER; import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY; -import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS; + import static java.lang.Integer.MAX_VALUE; import android.Manifest; @@ -101,7 +103,6 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackInfo; import android.app.ActivityManagerInternal.SleepToken; import android.app.ActivityOptions; @@ -175,7 +176,8 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener { +public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener, + RecentTasks.Callbacks { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM; private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; private static final String TAG_IDLE = TAG + POSTFIX_IDLE; @@ -285,7 +287,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ActivityManagerService mService; - private RecentTasks mRecentTasks; + RecentTasks mRecentTasks; final ActivityStackSupervisorHandler mHandler; @@ -577,6 +579,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void setRecentTasks(RecentTasks recentTasks) { mRecentTasks = recentTasks; + mRecentTasks.registerCallback(this); } /** @@ -750,7 +753,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Otherwise, check the recent tasks and return if we find it there and we are not restoring // the task from recents if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents"); - final TaskRecord task = mRecentTasks.taskForIdLocked(id); + final TaskRecord task = mRecentTasks.getTask(id); if (task == null) { if (DEBUG_RECENTS) { @@ -859,7 +862,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on. int candidateTaskId = nextTaskIdForUser(currentTaskId, userId); - while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId) + while (mRecentTasks.containsTaskId(candidateTaskId, userId) || anyTaskForIdLocked( candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) { candidateTaskId = nextTaskIdForUser(candidateTaskId, userId); @@ -2893,23 +2896,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - void addRecentActivity(ActivityRecord r) { - if (r == null) { - return; - } - final TaskRecord task = r.getTask(); - mRecentTasks.addLocked(task); - task.touchActiveTime(); - } - - void removeTaskFromRecents(TaskRecord task) { - mRecentTasks.remove(task); - task.removedFromRecents(); - } - void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) { if (removeFromRecents) { - removeTaskFromRecents(tr); + mRecentTasks.remove(tr); } ComponentName component = tr.getBaseIntent().getComponent(); if (component == null) { @@ -2989,7 +2978,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * Restores a recent task to a stack + * Called to restore the state of the task into the stack that it's supposed to go into. + * * @param task The recent task to be restored. * @param aOptions The activity options to use for restoration. * @return true if the task has been restored successfully. @@ -3020,6 +3010,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } + @Override + public void onRecentTaskAdded(TaskRecord task) { + task.touchActiveTime(); + } + + @Override + public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) { + if (wasTrimmed) { + // Task was trimmed from the recent tasks list -- remove the active task record as well + // since the user won't really be able to go back to it + removeTaskByIdLocked(task.taskId, false /* killProcess */, + false /* removeFromRecents */); + } + task.removedFromRecents(); + } + /** * Move stack with all its existing content to specified display. * @param stackId Id of stack to move. @@ -3500,7 +3506,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ActivityStack stack = task.getStack(); r.mLaunchTaskBehind = false; - mRecentTasks.addLocked(task); + mRecentTasks.add(task); mService.mTaskChangeNotificationController.notifyTaskStackChanged(); r.setVisibility(false); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 830008395ad5..cceb57674daa 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1239,8 +1239,8 @@ class ActivityStarter { mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); } - } else { - mSupervisor.addRecentActivity(mStartActivity); + } else if (mStartActivity != null) { + mSupervisor.mRecentTasks.add(mStartActivity.getTask()); } mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java new file mode 100644 index 000000000000..a4e2e70e0e60 --- /dev/null +++ b/services/core/java/com/android/server/am/AppTaskImpl.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; + +import android.app.ActivityManager; +import android.app.IAppTask; +import android.app.IApplicationThread; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.UserHandle; + +/** + * An implementation of IAppTask, that allows an app to manage its own tasks via + * {@link android.app.ActivityManager.AppTask}. We keep track of the callingUid to ensure that + * only the process that calls getAppTasks() can call the AppTask methods. + */ +class AppTaskImpl extends IAppTask.Stub { + private ActivityManagerService mService; + + private int mTaskId; + private int mCallingUid; + + public AppTaskImpl(ActivityManagerService service, int taskId, int callingUid) { + mService = service; + mTaskId = taskId; + mCallingUid = callingUid; + } + + private void checkCaller() { + if (mCallingUid != Binder.getCallingUid()) { + throw new SecurityException("Caller " + mCallingUid + + " does not match caller of getAppTasks(): " + Binder.getCallingUid()); + } + } + + @Override + public void finishAndRemoveTask() { + checkCaller(); + + synchronized (mService) { + long origId = Binder.clearCallingIdentity(); + try { + // We remove the task from recents to preserve backwards + if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false, + REMOVE_FROM_RECENTS)) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public ActivityManager.RecentTaskInfo getTaskInfo() { + checkCaller(); + + synchronized (mService) { + long origId = Binder.clearCallingIdentity(); + try { + TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId); + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + return RecentTasks.createRecentTaskInfo(tr); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public void moveToFront() { + checkCaller(); + // Will bring task to front if it already has a root activity. + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (this) { + mService.mStackSupervisor.startActivityFromRecentsInner(mTaskId, null); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public int startActivity(IBinder whoThread, String callingPackage, + Intent intent, String resolvedType, Bundle bOptions) { + checkCaller(); + + int callingUser = UserHandle.getCallingUserId(); + TaskRecord tr; + IApplicationThread appThread; + synchronized (mService) { + tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId); + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + appThread = IApplicationThread.Stub.asInterface(whoThread); + if (appThread == null) { + throw new IllegalArgumentException("Bad app thread " + appThread); + } + } + return mService.mActivityStarter.startActivityMayWait(appThread, -1, callingPackage, + intent, resolvedType, null, null, null, null, 0, 0, null, null, + null, bOptions, false, callingUser, tr, "AppTaskImpl"); + } + + @Override + public void setExcludeFromRecents(boolean exclude) { + checkCaller(); + + synchronized (mService) { + long origId = Binder.clearCallingIdentity(); + try { + TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId); + if (tr == null) { + throw new IllegalArgumentException("Unable to find task ID " + mTaskId); + } + Intent intent = tr.getBaseIntent(); + if (exclude) { + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + } else { + intent.setFlags(intent.getFlags() + & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 365c5b1d005c..8fef6bea1c3b 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -16,15 +16,25 @@ package com.android.server.am; +import static android.app.ActivityManager.FLAG_AND_UNLOCKED; +import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; +import static android.app.ActivityManager.RECENT_WITH_EXCLUDED; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import com.google.android.collect.Sets; @@ -37,43 +47,87 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; +import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Rect; +import android.os.Bundle; import android.os.Environment; +import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArraySet; +import android.util.MutableBoolean; +import android.util.MutableInt; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.am.ActivityStack.ActivityState; + import java.io.File; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Set; +import java.util.concurrent.TimeUnit; /** - * Class for managing the recent tasks list. + * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the + * least recent. */ -class RecentTasks extends ArrayList<TaskRecord> { +class RecentTasks { private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM; private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; private static final String TAG_TASKS = TAG + POSTFIX_TASKS; + private static final boolean TRIMMED = true; - // Maximum number recent bitmaps to keep in memory. - private static final int MAX_RECENT_BITMAPS = 3; 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; + // Comparator to sort by taskId + private static final Comparator<TaskRecord> TASK_ID_COMPARATOR = + (lhs, rhs) -> rhs.taskId - lhs.taskId; + + // Placeholder variables to keep track of activities/apps that are no longer avialble while + // iterating through the recents list + private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo(); + private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo(); + + /** + * Callbacks made when manipulating the list. + */ + interface Callbacks { + /** + * Called when a task is added to the recent tasks list. + */ + void onRecentTaskAdded(TaskRecord task); + + /** + * Called when a task is removed from the recent tasks list. + */ + void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed); + } + /** * Save recent tasks information across reboots. */ private final TaskPersister mTaskPersister; private final ActivityManagerService mService; + private final UserController mUserController; + + /** + * Mapping of user id -> whether recent tasks have been loaded for that user. + */ private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray( DEFAULT_INITIAL_CAPACITY); @@ -81,21 +135,106 @@ class RecentTasks extends ArrayList<TaskRecord> { * Stores for each user task ids that are taken by tasks residing in persistent storage. These * tasks may or may not currently be in memory. */ - final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>( + private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>( DEFAULT_INITIAL_CAPACITY); + // List of all active recent tasks + private final ArrayList<TaskRecord> mTasks = new ArrayList<>(); + private final ArrayList<Callbacks> mCallbacks = new ArrayList<>(); + + // These values are generally loaded from resources, but can be set dynamically in the tests + private boolean mHasVisibleRecentTasks; + private int mGlobalMaxNumTasks; + private int mMinNumVisibleTasks; + private int mMaxNumVisibleTasks; + private long mActiveTasksSessionDurationMs; + // Mainly to avoid object recreation on multiple calls. - private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>(); + 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 ActivityInfo mTmpActivityInfo = new ActivityInfo(); - private final ApplicationInfo mTmpAppInfo = new ApplicationInfo(); + private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); - RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) { - File systemDir = Environment.getDataSystemDirectory(); + @VisibleForTesting + RecentTasks(ActivityManagerService service, TaskPersister taskPersister, + UserController userController) { mService = service; - mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this); - mStackSupervisor.setRecentTasks(this); + mUserController = userController; + mTaskPersister = taskPersister; + mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic(); + mHasVisibleRecentTasks = true; + } + + RecentTasks(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) { + final File systemDir = Environment.getDataSystemDirectory(); + final Resources res = service.mContext.getResources(); + mService = service; + mUserController = service.mUserController; + mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this); + mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic(); + mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents); + loadParametersFromResources(service.mContext.getResources()); + } + + @VisibleForTesting + void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks, + long activeSessionDurationMs) { + mMinNumVisibleTasks = minNumVisibleTasks; + mMaxNumVisibleTasks = maxNumVisibleTasks; + mActiveTasksSessionDurationMs = activeSessionDurationMs; + } + + @VisibleForTesting + void setGlobalMaxNumTasks(int globalMaxNumTasks) { + mGlobalMaxNumTasks = globalMaxNumTasks; + } + + /** + * Loads the parameters from the system resources. + */ + @VisibleForTesting + void loadParametersFromResources(Resources res) { + if (ActivityManager.isLowRamDeviceStatic()) { + mMinNumVisibleTasks = res.getInteger( + com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam); + mMaxNumVisibleTasks = res.getInteger( + com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam); + } else if (SystemProperties.getBoolean("ro.recents.grid", false)) { + mMinNumVisibleTasks = res.getInteger( + com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid); + mMaxNumVisibleTasks = res.getInteger( + com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid); + } else { + mMinNumVisibleTasks = res.getInteger( + com.android.internal.R.integer.config_minNumVisibleRecentTasks); + mMaxNumVisibleTasks = res.getInteger( + com.android.internal.R.integer.config_maxNumVisibleRecentTasks); + } + final int sessionDurationHrs = res.getInteger( + com.android.internal.R.integer.config_activeTaskDurationHours); + mActiveTasksSessionDurationMs = (sessionDurationHrs > 0) + ? TimeUnit.HOURS.toMillis(sessionDurationHrs) + : -1; + } + + void registerCallback(Callbacks callback) { + mCallbacks.add(callback); + } + + void unregisterCallback(Callbacks callback) { + mCallbacks.remove(callback); + } + + private void notifyTaskAdded(TaskRecord task) { + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).onRecentTaskAdded(task); + } + } + + private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) { + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed); + } } /** @@ -106,6 +245,7 @@ class RecentTasks extends ArrayList<TaskRecord> { */ void loadUserRecentsLocked(int userId) { if (mUsersWithRecentsLoaded.get(userId)) { + // User already loaded, return early return; } @@ -114,14 +254,14 @@ class RecentTasks extends ArrayList<TaskRecord> { // Check if any tasks are added before recents is loaded final SparseBooleanArray preaddedTasks = new SparseBooleanArray(); - for (final TaskRecord task : this) { + for (final TaskRecord task : mTasks) { if (task.userId == userId && shouldPersistTaskLocked(task)) { preaddedTasks.put(task.taskId, true); } } Slog.i(TAG, "Loading recents for user " + userId + " into memory."); - addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks)); + mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks)); cleanupLocked(userId); mUsersWithRecentsLoaded.put(userId, true); @@ -140,11 +280,25 @@ class RecentTasks extends ArrayList<TaskRecord> { } } - boolean taskIdTakenForUserLocked(int taskId, int userId) { + /** + * @return whether the {@param taskId} is currently in use for the given user. + */ + boolean containsTaskId(int taskId, int userId) { loadPersistedTaskIdsForUserLocked(userId); return mPersistedTaskIds.get(userId).get(taskId); } + /** + * @return all the task ids for the user with the given {@param userId}. + */ + SparseBooleanArray getTaskIdsForUser(int userId) { + loadPersistedTaskIdsForUserLocked(userId); + return mPersistedTaskIds.get(userId); + } + + /** + * Kicks off the task persister to write any pending tasks to disk. + */ void notifyTaskPersisterLocked(TaskRecord task, boolean flush) { final ActivityStack stack = task != null ? task.getStack() : null; if (stack != null && stack.isHomeOrRecentsStack()) { @@ -164,8 +318,8 @@ class RecentTasks extends ArrayList<TaskRecord> { mPersistedTaskIds.valueAt(i).clear(); } } - for (int i = size() - 1; i >= 0; i--) { - final TaskRecord task = get(i); + for (int i = mTasks.size() - 1; i >= 0; i--) { + final TaskRecord task = mTasks.get(i); if (shouldPersistTaskLocked(task)) { // Set of persisted taskIds for task.userId should not be null here // TODO Investigate why it can happen. For now initialize with an empty set @@ -180,12 +334,12 @@ class RecentTasks extends ArrayList<TaskRecord> { } private static boolean shouldPersistTaskLocked(TaskRecord task) { - final ActivityStack<?> stack = task.getStack(); + final ActivityStack stack = task.getStack(); return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack()); } void onSystemReadyLocked() { - clear(); + mTasks.clear(); mTaskPersister.startPersisting(); } @@ -225,14 +379,6 @@ class RecentTasks extends ArrayList<TaskRecord> { return usersWithRecentsLoaded; } - private void unloadUserRecentsLocked(int userId) { - if (mUsersWithRecentsLoaded.get(userId)) { - Slog.i(TAG, "Unloading recents for user " + userId + " from memory."); - mUsersWithRecentsLoaded.delete(userId); - removeTasksForUserLocked(userId); - } - } - /** * Removes recent tasks and any other state kept in memory for the passed in user. Does not * touch the information present on persistent storage. @@ -240,44 +386,36 @@ class RecentTasks extends ArrayList<TaskRecord> { * @param userId the id of the user */ void unloadUserDataFromMemoryLocked(int userId) { - unloadUserRecentsLocked(userId); + if (mUsersWithRecentsLoaded.get(userId)) { + Slog.i(TAG, "Unloading recents for user " + userId + " from memory."); + mUsersWithRecentsLoaded.delete(userId); + removeTasksForUserLocked(userId); + } mPersistedTaskIds.delete(userId); mTaskPersister.unloadUserDataFromMemory(userId); } - TaskRecord taskForIdLocked(int id) { - final int recentsCount = size(); - for (int i = 0; i < recentsCount; i++) { - TaskRecord tr = get(i); - if (tr.taskId == id) { - return tr; - } - } - return null; - } - /** Remove recent tasks for a user. */ - void removeTasksForUserLocked(int userId) { + private void removeTasksForUserLocked(int userId) { if(userId <= 0) { Slog.i(TAG, "Can't remove recent task on user " + userId); return; } - for (int i = size() - 1; i >= 0; --i) { - TaskRecord tr = get(i); + for (int i = mTasks.size() - 1; i >= 0; --i) { + TaskRecord tr = mTasks.get(i); if (tr.userId == userId) { if(DEBUG_TASKS) Slog.i(TAG_TASKS, "remove RecentTask " + tr + " when finishing user" + userId); - remove(i); - tr.removedFromRecents(); + remove(mTasks.get(i)); } } } void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) { final Set<String> packageNames = Sets.newHashSet(packages); - for (int i = size() - 1; i >= 0; --i) { - final TaskRecord tr = get(i); + for (int i = mTasks.size() - 1; i >= 0; --i) { + final TaskRecord tr = mTasks.get(i); if (tr.realActivity != null && packageNames.contains(tr.realActivity.getPackageName()) && tr.userId == userId @@ -286,7 +424,38 @@ class RecentTasks extends ArrayList<TaskRecord> { notifyTaskPersisterLocked(tr, false); } } + } + + void removeTasksByPackageName(String packageName, int userId) { + final int size = mTasks.size(); + for (int i = 0; i < size; i++) { + final TaskRecord tr = mTasks.get(i); + final String taskPackageName = + tr.getBaseIntent().getComponent().getPackageName(); + if (tr.userId != userId) return; + if (!taskPackageName.equals(packageName)) return; + + mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS); + } + } + + void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses, + int userId) { + final int size = mTasks.size(); + for (int i = 0; i < size; i++) { + final TaskRecord tr = mTasks.get(i); + if (userId != UserHandle.USER_ALL && tr.userId != userId) { + continue; + } + ComponentName cn = tr.intent.getComponent(); + final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName) + && (filterByClasses == null || filterByClasses.contains(cn.getClassName())); + if (sameComponent) { + mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, + REMOVE_FROM_RECENTS); + } + } } /** @@ -295,24 +464,28 @@ class RecentTasks extends ArrayList<TaskRecord> { * of affiliations. */ void cleanupLocked(int userId) { - int recentsCount = size(); + int recentsCount = mTasks.size(); if (recentsCount == 0) { // Happens when called from the packagemanager broadcast before boot, // or just any empty list. return; } + // Clear the temp lists + mTmpAvailActCache.clear(); + mTmpAvailAppCache.clear(); + final IPackageManager pm = AppGlobals.getPackageManager(); for (int i = recentsCount - 1; i >= 0; i--) { - final TaskRecord task = get(i); + final TaskRecord task = mTasks.get(i); if (userId != UserHandle.USER_ALL && task.userId != userId) { // Only look at tasks for the user ID of interest. continue; } if (task.autoRemoveRecents && task.getTopActivity() == null) { // This situation is broken, and we should just get rid of it now. - remove(i); - task.removedFromRecents(); + mTasks.remove(i); + notifyTaskRemoved(task, !TRIMMED); Slog.w(TAG, "Removing auto-remove without activity: " + task); continue; } @@ -331,11 +504,11 @@ class RecentTasks extends ArrayList<TaskRecord> { continue; } if (ai == null) { - ai = mTmpActivityInfo; + ai = NO_ACTIVITY_INFO_TOKEN; } mTmpAvailActCache.put(task.realActivity, ai); } - if (ai == mTmpActivityInfo) { + if (ai == NO_ACTIVITY_INFO_TOKEN) { // This could be either because the activity no longer exists, or the // app is temporarily gone. For the former we want to remove the recents // entry; for the latter we want to mark it as unavailable. @@ -350,15 +523,15 @@ class RecentTasks extends ArrayList<TaskRecord> { continue; } if (app == null) { - app = mTmpAppInfo; + app = NO_APPLICATION_INFO_TOKEN; } mTmpAvailAppCache.put(task.realActivity.getPackageName(), app); } - if (app == mTmpAppInfo + if (app == NO_APPLICATION_INFO_TOKEN || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { // Doesn't exist any more! Good-bye. - remove(i); - task.removedFromRecents(); + mTasks.remove(i); + notifyTaskRemoved(task, !TRIMMED); Slog.w(TAG, "Removing no longer valid recent: " + task); continue; } else { @@ -390,121 +563,197 @@ class RecentTasks extends ArrayList<TaskRecord> { // Verify the affiliate chain for each task. int i = 0; - recentsCount = size(); + recentsCount = mTasks.size(); while (i < recentsCount) { i = processNextAffiliateChainLocked(i); } // recent tasks are now in sorted, affiliated order. } - private final boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) { - int recentsCount = size(); - TaskRecord top = task; - int topIndex = taskIndex; - while (top.mNextAffiliate != null && topIndex > 0) { - top = top.mNextAffiliate; - topIndex--; + /** + * @return whether the given {@param task} can be added to the list without causing another + * task to be trimmed as a result of that add. + */ + private boolean canAddTaskWithoutTrim(TaskRecord task) { + return findTrimIndexForAddTask(task) == -1; + } + + /** + * Returns the list of {@link ActivityManager.AppTask}s. + */ + ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) { + final ArrayList<IBinder> list = new ArrayList<>(); + final int size = mTasks.size(); + for (int i = 0; i < size; i++) { + final TaskRecord tr = mTasks.get(i); + // Skip tasks that do not match the caller. We don't need to verify + // callingPackage, because we are also limiting to callingUid and know + // that will limit to the correct security sandbox. + if (tr.effectiveUid != callingUid) { + continue; + } + Intent intent = tr.getBaseIntent(); + if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) { + continue; + } + ActivityManager.RecentTaskInfo taskInfo = createRecentTaskInfo(tr); + AppTaskImpl taskImpl = new AppTaskImpl(mService, taskInfo.persistentId, callingUid); + list.add(taskImpl.asBinder()); } - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at " - + topIndex + " from intial " + taskIndex); - // Find the end of the chain, doing a sanity check along the way. - boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId; - int endIndex = topIndex; - TaskRecord prev = top; - while (endIndex < recentsCount) { - TaskRecord cur = get(endIndex); - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @" - + endIndex + " " + cur); - if (cur == top) { - // Verify start of the chain. - if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": first task has next affiliate: " + prev); - sane = false; - break; + return list; + } + + /** + * @return the list of recent tasks for presentation. + */ + ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, + boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) { + final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0; + + if (!mService.isUserRunning(userId, FLAG_AND_UNLOCKED)) { + Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents"); + return ParceledListSlice.emptyList(); + } + loadUserRecentsLocked(userId); + + final Set<Integer> includedUsers = mUserController.getProfileIds(userId); + includedUsers.add(Integer.valueOf(userId)); + + final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>(); + final int size = mTasks.size(); + int numVisibleTasks = 0; + for (int i = 0; i < size; i++) { + final TaskRecord tr = mTasks.get(i); + + if (isVisibleRecentTask(tr)) { + numVisibleTasks++; + if (isInVisibleRange(tr, numVisibleTasks)) { + // Fall through + } else { + // Not in visible range + continue; } } else { - // Verify middle of the chain's next points back to the one before. - if (cur.mNextAffiliate != prev - || cur.mNextAffiliateTaskId != prev.taskId) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": middle task " + cur + " @" + endIndex - + " has bad next affiliate " - + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId - + ", expected " + prev); - sane = false; - break; - } + // Not visible + continue; } - if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) { - // Chain ends here. - if (cur.mPrevAffiliate != null) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": last task " + cur + " has previous affiliate " - + cur.mPrevAffiliate); - sane = false; - } - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex); - break; - } else { - // Verify middle of the chain's prev points to a valid item. - if (cur.mPrevAffiliate == null) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": task " + cur + " has previous affiliate " - + cur.mPrevAffiliate + " but should be id " - + cur.mPrevAffiliate); - sane = false; - break; - } + + // Skip remaining tasks once we reach the requested size + if (res.size() >= maxNum) { + continue; } - if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": task " + cur + " has affiliated id " - + cur.mAffiliatedTaskId + " but should be " - + task.mAffiliatedTaskId); - sane = false; - break; + + // Only add calling user or related users recent tasks + if (!includedUsers.contains(Integer.valueOf(tr.userId))) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr); + continue; } - prev = cur; - endIndex++; - if (endIndex >= recentsCount) { - Slog.wtf(TAG, "Bad chain ran off index " + endIndex - + ": last task " + prev); - sane = false; - break; + + if (tr.realActivitySuspended) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr); + continue; } - } - if (sane) { - if (endIndex < taskIndex) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": did not extend to task " + task + " @" + taskIndex); - sane = false; + + // Return the entry if desired by the caller. We always return + // the first entry, because callers always expect this to be the + // foreground app. We may filter others if the caller has + // not supplied RECENT_WITH_EXCLUDED and there is some reason + // we should exclude the entry. + + if (i == 0 + || withExcluded + || (tr.intent == null) + || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + == 0)) { + if (!getTasksAllowed) { + // If the caller doesn't have the GET_TASKS permission, then only + // allow them to see a small subset of tasks -- their own and home. + if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr); + continue; + } + } + if (tr.autoRemoveRecents && tr.getTopActivity() == null) { + // Don't include auto remove tasks that are finished or finishing. + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, + "Skipping, auto-remove without activity: " + tr); + continue; + } + if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, + "Skipping, unavail real act: " + tr); + continue; + } + + if (!tr.mUserSetupComplete) { + // Don't include task launched while user is not done setting-up. + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, + "Skipping, user setup not complete: " + tr); + continue; + } + + ActivityManager.RecentTaskInfo rti = RecentTasks.createRecentTaskInfo(tr); + if (!getDetailedTasks) { + rti.baseIntent.replaceExtras((Bundle)null); + } + + res.add(rti); } } - if (sane) { - // All looks good, we can just move all of the affiliated tasks - // to the top. - for (int i=topIndex; i<=endIndex; i++) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task - + " from " + i + " to " + (i-topIndex)); - TaskRecord cur = remove(i); - add(i - topIndex, cur); + return new ParceledListSlice<>(res); + } + + /** + * @return the list of persistable task ids. + */ + void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) { + final int size = mTasks.size(); + for (int i = 0; i < size; i++) { + final TaskRecord task = mTasks.get(i); + if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + + " persistable=" + task.isPersistable); + final ActivityStack stack = task.getStack(); + if ((task.isPersistable || task.inRecents) + && (stack == null || !stack.isHomeOrRecentsStack())) { + if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task); + persistentTaskIds.add(task.taskId); + } else { + if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + + task); } - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex - + " to " + endIndex); - return true; } + } - // Whoops, couldn't do it. - return false; + @VisibleForTesting + ArrayList<TaskRecord> getRawTasks() { + return mTasks; + } + + /** + * @return the task in the task list with the given {@param id} if one exists. + */ + TaskRecord getTask(int id) { + final int recentsCount = mTasks.size(); + for (int i = 0; i < recentsCount; i++) { + TaskRecord tr = mTasks.get(i); + if (tr.taskId == id) { + return tr; + } + } + return null; } - final void addLocked(TaskRecord task) { + /** + * Add a new task to the recent tasks list. + */ + void add(TaskRecord task) { + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task); + final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId || task.mNextAffiliateTaskId != INVALID_TASK_ID || task.mPrevAffiliateTaskId != INVALID_TASK_ID; - int recentsCount = size(); + int recentsCount = mTasks.size(); // Quick case: never add voice sessions. // TODO: VI what about if it's just an activity? // Probably nothing to do here @@ -514,15 +763,15 @@ class RecentTasks extends ArrayList<TaskRecord> { return; } // Another quick case: check if the top-most recent task is the same. - if (!isAffiliated && recentsCount > 0 && get(0) == task) { + if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) { if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task); return; } // Another quick case: check if this is part of a set of affiliated // tasks that are at the top. if (isAffiliated && recentsCount > 0 && task.inRecents - && task.mAffiliatedTaskId == get(0).mAffiliatedTaskId) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + get(0) + && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0) + " at top when adding " + task); return; } @@ -532,12 +781,12 @@ class RecentTasks extends ArrayList<TaskRecord> { // Slightly less quick case: the task is already in recents, so all we need // to do is move it. if (task.inRecents) { - int taskIndex = indexOf(task); + int taskIndex = mTasks.indexOf(task); if (taskIndex >= 0) { - if (!isAffiliated || MOVE_AFFILIATED_TASKS_TO_FRONT) { + if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) { // Simple case: this is not an affiliated task, so we just move it to the front. - remove(taskIndex); - add(0, task); + mTasks.remove(taskIndex); + mTasks.add(0, task); notifyTaskPersisterLocked(task, false); if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task + " from " + taskIndex); @@ -560,20 +809,14 @@ class RecentTasks extends ArrayList<TaskRecord> { } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task); - trimForTaskLocked(task, true); + trimForAddTask(task); - recentsCount = size(); - final int maxRecents = ActivityManager.getMaxRecentTasksStatic(); - while (recentsCount >= maxRecents) { - final TaskRecord tr = remove(recentsCount - 1); - tr.removedFromRecents(); - recentsCount--; - } task.inRecents = true; if (!isAffiliated || needAffiliationFix) { // If this is a simple non-affiliated task, or we had some failure trying to // handle it as part of an affilated task, then just place it at the top. - add(0, task); + mTasks.add(0, task); + notifyTaskAdded(task); if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task); } else if (isAffiliated) { // If this is a new affiliated task, then move all of the affiliated tasks @@ -583,7 +826,7 @@ class RecentTasks extends ArrayList<TaskRecord> { other = task.mPrevAffiliate; } if (other != null) { - int otherIndex = indexOf(other); + int otherIndex = mTasks.indexOf(other); if (otherIndex >= 0) { // Insert new task at appropriate location. int taskIndex; @@ -598,7 +841,8 @@ class RecentTasks extends ArrayList<TaskRecord> { } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: new affiliated task added at " + taskIndex + ": " + task); - add(taskIndex, task); + mTasks.add(taskIndex, task); + notifyTaskAdded(task); // Now move everything to the front. if (moveAffiliatedTasksToFront(task, taskIndex)) { @@ -625,21 +869,235 @@ class RecentTasks extends ArrayList<TaskRecord> { if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations"); cleanupLocked(task.userId); } + + // Trim the set of tasks to the active set + trimInactiveRecentTasks(); + } + + /** + * Add the task to the bottom if possible. + */ + boolean addToBottom(TaskRecord task) { + if (!canAddTaskWithoutTrim(task)) { + // Adding this task would cause the task to be removed (since it's appended at + // the bottom and would be trimmed) so just return now + return false; + } + + add(task); + return true; + } + + /** + * Remove a task from the recent tasks list. + */ + void remove(TaskRecord task) { + mTasks.remove(task); + notifyTaskRemoved(task, !TRIMMED); + } + + /** + * Trims the recents task list to the global max number of recents. + */ + private void trimInactiveRecentTasks() { + int recentsCount = mTasks.size(); + + // Remove from the end of the list until we reach the max number of recents + while (recentsCount > mGlobalMaxNumTasks) { + final TaskRecord tr = mTasks.remove(recentsCount - 1); + notifyTaskRemoved(tr, TRIMMED); + recentsCount--; + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr + + " max=" + mGlobalMaxNumTasks); + } + + // Remove any tasks that belong to currently quiet profiles + final int[] profileUserIds = mUserController.getCurrentProfileIds(); + mTmpQuietProfileUserIds.clear(); + for (int userId : profileUserIds) { + final UserInfo userInfo = mUserController.getUserInfo(userId); + if (userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) { + mTmpQuietProfileUserIds.put(userId, true); + } + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo + + " quiet=" + mTmpQuietProfileUserIds.get(userId)); + } + + // 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); + + if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) { + if (!mHasVisibleRecentTasks) { + // Keep all active tasks if visible recent tasks is not supported + i++; + continue; + } + + if (!isVisibleRecentTask(task)) { + // Keep all active-but-invisible tasks + i++; + continue; + } else { + numVisibleTasks++; + if (isInVisibleRange(task, numVisibleTasks)) { + // Keep visible tasks in range + i++; + continue; + } else { + // Fall through to trim visible tasks that are no longer in range + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, + "Trimming out-of-range visible task=" + task); + } + } + } else { + // Fall through to trim inactive tasks + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task); + } + + // Task is no longer active, trim it from the list + mTasks.remove(task); + notifyTaskRemoved(task, TRIMMED); + notifyTaskPersisterLocked(task, false /* flush */); + } + } + + /** + * @return whether the given task should be considered active. + */ + private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) { + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task + + " globalMax=" + mGlobalMaxNumTasks); + + if (quietProfileUserIds.get(task.userId)) { + // Quiet profile user's tasks are never active + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true"); + return false; + } + + if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) { + // Keep the task active if its affiliated task is also active + final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId); + if (affiliatedTask != null) { + if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) { + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, + "\taffiliatedWithTask=" + affiliatedTask + " is not active"); + return false; + } + } + } + + // All other tasks are considered active + return true; + } + + /** + * @return whether the given active task should be presented to the user through SystemUI. + */ + private boolean isVisibleRecentTask(TaskRecord task) { + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task + + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks + + " sessionDuration=" + mActiveTasksSessionDurationMs + + " inactiveDuration=" + task.getInactiveDuration() + + " activityType=" + task.getActivityType() + + " windowingMode=" + task.getWindowingMode()); + + // Ignore certain activity types completely + switch (task.getActivityType()) { + case ACTIVITY_TYPE_HOME: + case ACTIVITY_TYPE_RECENTS: + return false; + } + + // Ignore certain windowing modes + switch (task.getWindowingMode()) { + case WINDOWING_MODE_PINNED: + return false; + case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask()); + final ActivityStack stack = task.getStack(); + if (stack != null && stack.topTask() == task) { + // Only the non-top task of the primary split screen mode is visible + return false; + } + } + + return true; + } + + /** + * @return whether the given visible task is within the policy range. + */ + private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) { + // Keep the last most task even if it is excluded from recents + final boolean isExcludeFromRecents = + (task.getBaseIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; + if (isExcludeFromRecents) { + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true"); + return numVisibleTasks == 1; + } + + if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) { + // Always keep up to the min number of recent tasks, after that fall through to the + // checks below + return true; + } + + if (mMaxNumVisibleTasks >= 0) { + // Always keep up to the max number of recent tasks, but return false afterwards + return numVisibleTasks <= mMaxNumVisibleTasks; + } + + if (mActiveTasksSessionDurationMs > 0) { + // Keep the task if the inactive time is within the session window, this check must come + // after the checks for the min/max visible task range + if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) { + return true; + } + } + + return false; } /** * If needed, remove oldest existing entries in recents that are for the same kind * of task as the given one. */ - int trimForTaskLocked(TaskRecord task, boolean doTrim) { - int recentsCount = size(); + private void trimForAddTask(TaskRecord task) { + final int removeIndex = findTrimIndexForAddTask(task); + if (removeIndex == -1) { + // Nothing to trim + return; + } + + // There is a similar task that will be removed for the addition of {@param task}, but it + // can be the same task, and if so, the task will be re-added in add(), so skip the + // callbacks here. + final TaskRecord removedTask = mTasks.remove(removeIndex); + if (removedTask != task) { + notifyTaskRemoved(removedTask, TRIMMED); + if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask + + " for addition of task=" + task); + } + notifyTaskPersisterLocked(removedTask, false /* flush */); + } + + /** + * Find the task that would be removed if the given {@param task} is added to the recent tasks + * list (if any). + */ + private int findTrimIndexForAddTask(TaskRecord task) { + int recentsCount = mTasks.size(); final Intent intent = task.intent; final boolean document = intent != null && intent.isDocument(); int maxRecents = task.maxRecents - 1; final ActivityStack stack = task.getStack(); for (int i = 0; i < recentsCount; i++) { - final TaskRecord tr = get(i); + final TaskRecord tr = mTasks.get(i); final ActivityStack trStack = tr.getStack(); + if (task != tr) { if (stack != null && trStack != null && stack != trStack) { continue; @@ -650,7 +1108,7 @@ class RecentTasks extends ArrayList<TaskRecord> { final Intent trIntent = tr.intent; final boolean sameAffinity = task.affinity != null && task.affinity.equals(tr.affinity); - final boolean sameIntentFilter = intent != null && intent.filterEquals(trIntent); + final boolean sameIntent = intent != null && intent.filterEquals(trIntent); boolean multiTasksAllowed = false; final int flags = intent.getFlags(); if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0 @@ -659,7 +1117,7 @@ class RecentTasks extends ArrayList<TaskRecord> { } final boolean trIsDocument = trIntent != null && trIntent.isDocument(); final boolean bothDocuments = document && trIsDocument; - if (!sameAffinity && !sameIntentFilter && !bothDocuments) { + if (!sameAffinity && !sameIntent && !bothDocuments) { continue; } @@ -668,17 +1126,17 @@ class RecentTasks extends ArrayList<TaskRecord> { final boolean sameActivity = task.realActivity != null && tr.realActivity != null && task.realActivity.equals(tr.realActivity); - // If the document is open in another app or is not the same - // document, we don't need to trim it. if (!sameActivity) { + // If the document is open in another app or is not the same document, we + // don't need to trim it. continue; - // Otherwise only trim if we are over our max recents for this task } else if (maxRecents > 0) { + // Otherwise only trim if we are over our max recents for this task --maxRecents; - if (!doTrim || !sameIntentFilter || multiTasksAllowed) { + if (!sameIntent || multiTasksAllowed) { // We don't want to trim if we are not over the max allowed entries and - // the caller doesn't want us to trim, the tasks are not of the same - // intent filter, or multiple entries fot the task is allowed. + // the tasks are not of the same intent filter, or multiple entries for + // the task is allowed. continue; } } @@ -689,44 +1147,14 @@ class RecentTasks extends ArrayList<TaskRecord> { continue; } } - - if (!doTrim) { - // If the caller is not actually asking for a trim, just tell them we reached - // a point where the trim would happen. - return i; - } - - // Either task and tr are the same or, their affinities match or their intents match - // and neither of them is a document, or they are documents using the same activity - // and their maxRecents has been reached. - remove(i); - if (task != tr) { - tr.removedFromRecents(); - } - i--; - recentsCount--; - if (task.intent == null) { - // If the new recent task we are adding is not fully - // specified, then replace it with the existing recent task. - task = tr; - } - notifyTaskPersisterLocked(tr, false); + return i; } - return -1; } - // Sort by taskId - private static Comparator<TaskRecord> sTaskRecordComparator = new Comparator<TaskRecord>() { - @Override - public int compare(TaskRecord lhs, TaskRecord rhs) { - return rhs.taskId - lhs.taskId; - } - }; - // Extract the affiliates of the chain containing recent at index start. private int processNextAffiliateChainLocked(int start) { - final TaskRecord startTask = get(start); + final TaskRecord startTask = mTasks.get(start); final int affiliateId = startTask.mAffiliatedTaskId; // Quick identification of isolated tasks. I.e. those not launched behind. @@ -741,17 +1169,17 @@ class RecentTasks extends ArrayList<TaskRecord> { // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents. mTmpRecents.clear(); - for (int i = size() - 1; i >= start; --i) { - final TaskRecord task = get(i); + for (int i = mTasks.size() - 1; i >= start; --i) { + final TaskRecord task = mTasks.get(i); if (task.mAffiliatedTaskId == affiliateId) { - remove(i); + mTasks.remove(i); mTmpRecents.add(task); } } // Sort them all by taskId. That is the order they were create in and that order will // always be correct. - Collections.sort(mTmpRecents, sTaskRecordComparator); + Collections.sort(mTmpRecents, TASK_ID_COMPARATOR); // Go through and fix up the linked list. // The first one is the end of the chain and has no next. @@ -789,11 +1217,197 @@ class RecentTasks extends ArrayList<TaskRecord> { notifyTaskPersisterLocked(last, false); } - // Insert the group back into mRecentTasks at start. - addAll(start, mTmpRecents); + // Insert the group back into mTmpTasks at start. + mTasks.addAll(start, mTmpRecents); mTmpRecents.clear(); // Let the caller know where we left off. return start + tmpSize; } + + private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) { + int recentsCount = mTasks.size(); + TaskRecord top = task; + int topIndex = taskIndex; + while (top.mNextAffiliate != null && topIndex > 0) { + top = top.mNextAffiliate; + topIndex--; + } + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at " + + topIndex + " from intial " + taskIndex); + // Find the end of the chain, doing a sanity check along the way. + boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId; + int endIndex = topIndex; + TaskRecord prev = top; + while (endIndex < recentsCount) { + TaskRecord cur = mTasks.get(endIndex); + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @" + + endIndex + " " + cur); + if (cur == top) { + // Verify start of the chain. + if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": first task has next affiliate: " + prev); + sane = false; + break; + } + } else { + // Verify middle of the chain's next points back to the one before. + if (cur.mNextAffiliate != prev + || cur.mNextAffiliateTaskId != prev.taskId) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": middle task " + cur + " @" + endIndex + + " has bad next affiliate " + + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId + + ", expected " + prev); + sane = false; + break; + } + } + if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) { + // Chain ends here. + if (cur.mPrevAffiliate != null) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": last task " + cur + " has previous affiliate " + + cur.mPrevAffiliate); + sane = false; + } + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex); + break; + } else { + // Verify middle of the chain's prev points to a valid item. + if (cur.mPrevAffiliate == null) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": task " + cur + " has previous affiliate " + + cur.mPrevAffiliate + " but should be id " + + cur.mPrevAffiliate); + sane = false; + break; + } + } + if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": task " + cur + " has affiliated id " + + cur.mAffiliatedTaskId + " but should be " + + task.mAffiliatedTaskId); + sane = false; + break; + } + prev = cur; + endIndex++; + if (endIndex >= recentsCount) { + Slog.wtf(TAG, "Bad chain ran off index " + endIndex + + ": last task " + prev); + sane = false; + break; + } + } + if (sane) { + if (endIndex < taskIndex) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": did not extend to task " + task + " @" + taskIndex); + sane = false; + } + } + if (sane) { + // All looks good, we can just move all of the affiliated tasks + // to the top. + for (int i=topIndex; i<=endIndex; i++) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task + + " from " + i + " to " + (i-topIndex)); + TaskRecord cur = mTasks.remove(i); + mTasks.add(i - topIndex, cur); + } + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex + + " to " + endIndex); + return true; + } + + // Whoops, couldn't do it. + return false; + } + + void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { + pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)"); + if (mTasks.isEmpty()) { + return; + } + + final MutableBoolean printedAnything = new MutableBoolean(false); + final MutableBoolean printedHeader = new MutableBoolean(false); + final int size = mTasks.size(); + for (int i = 0; i < size; i++) { + final TaskRecord tr = mTasks.get(i); + if (dumpPackage != null && (tr.realActivity == null || + !dumpPackage.equals(tr.realActivity.getPackageName()))) { + continue; + } + + if (!printedHeader.value) { + pw.println(" Recent tasks:"); + printedHeader.value = true; + printedAnything.value = true; + } + pw.print(" * Recent #"); pw.print(i); pw.print(": "); + pw.println(tr); + if (dumpAll) { + tr.dump(pw, " "); + } + } + + if (!printedAnything.value) { + pw.println(" (nothing)"); + } + } + + /** + * Creates a new RecentTaskInfo from a TaskRecord. + */ + static ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) { + // Update the task description to reflect any changes in the task stack + tr.updateTaskDescription(); + + // Compose the recent task info + ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); + rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId; + rti.persistentId = tr.taskId; + rti.baseIntent = new Intent(tr.getBaseIntent()); + rti.origActivity = tr.origActivity; + rti.realActivity = tr.realActivity; + rti.description = tr.lastDescription; + rti.stackId = tr.getStackId(); + rti.userId = tr.userId; + rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription); + rti.lastActiveTime = tr.lastActiveTime; + rti.affiliatedTaskId = tr.mAffiliatedTaskId; + rti.affiliatedTaskColor = tr.mAffiliatedTaskColor; + rti.numActivities = 0; + if (tr.mBounds != null) { + rti.bounds = new Rect(tr.mBounds); + } + rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode(); + rti.resizeMode = tr.mResizeMode; + rti.configuration.setTo(tr.getConfiguration()); + + ActivityRecord base = null; + ActivityRecord top = null; + ActivityRecord tmp; + + for (int i = tr.mActivities.size() - 1; i >= 0; --i) { + tmp = tr.mActivities.get(i); + if (tmp.finishing) { + continue; + } + base = tmp; + if (top == null || (top.state == ActivityState.INITIALIZING)) { + top = base; + } + rti.numActivities++; + } + + rti.baseActivity = (base != null) ? base.intent.getComponent() : null; + rti.topActivity = (top != null) ? top.intent.getComponent() : null; + + return rti; + } } diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index 61994b55dcd1..2689d6a4edec 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -567,7 +567,7 @@ public class TaskPersister { SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>(); synchronized (mService) { for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) { - SparseBooleanArray taskIdsToSave = mRecentTasks.mPersistedTaskIds.get(userId); + SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId); SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId); if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIdsToSave)) { continue; @@ -640,7 +640,7 @@ public class TaskPersister { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>(); + ArraySet<Integer> persistentTaskIds = new ArraySet<>(); while (true) { // We can't lock mService while holding TaskPersister.this, but we don't want to // call removeObsoleteFiles every time through the loop, only the last time before @@ -654,20 +654,7 @@ public class TaskPersister { persistentTaskIds.clear(); synchronized (mService) { if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks); - for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) { - final TaskRecord task = mRecentTasks.get(taskNdx); - if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + - " persistable=" + task.isPersistable); - final ActivityStack stack = task.getStack(); - if ((task.isPersistable || task.inRecents) - && (stack == null || !stack.isHomeOrRecentsStack())) { - if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task); - persistentTaskIds.add(task.taskId); - } else { - if (DEBUG) Slog.d(TAG, - "omitting from persistentTaskIds task=" + task); - } - } + mRecentTasks.getPersistableTaskIds(persistentTaskIds); mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds, mRecentTasks.usersWithRecentsLoadedLocked()); } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 5491da103b91..7817f1a8a30a 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -100,6 +100,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.Debug; import android.os.RemoteException; +import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; @@ -153,8 +154,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi private static final String ATTR_EFFECTIVE_UID = "effective_uid"; @Deprecated private static final String ATTR_TASKTYPE = "task_type"; - private static final String ATTR_FIRSTACTIVETIME = "first_active_time"; - private static final String ATTR_LASTACTIVETIME = "last_active_time"; private static final String ATTR_LASTDESCRIPTION = "last_description"; private static final String ATTR_LASTTIMEMOVED = "last_time_moved"; private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity"; @@ -210,9 +209,10 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi ComponentName realActivity; // The actual activity component that started the task. boolean realActivitySuspended; // True if the actual activity component that started the // task is suspended. - long firstActiveTime; // First time this task was active. - long lastActiveTime; // Last time this task was active, including sleep. boolean inRecents; // Actually in the recents list? + long lastActiveTime; // Last time this task was active in the current device session, + // including sleep. This time is initialized to the elapsed time when + // restored from disk. boolean isAvailable; // Is the activity available to be launched? boolean rootWasReset; // True if the intent at the root of the task had // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. @@ -337,6 +337,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi TaskPersister.IMAGE_EXTENSION; userId = UserHandle.getUserId(info.applicationInfo.uid); taskId = _taskId; + lastActiveTime = SystemClock.elapsedRealtime(); mAffiliatedTaskId = _taskId; voiceSession = _voiceSession; voiceInteractor = _voiceInteractor; @@ -357,6 +358,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi TaskPersister.IMAGE_EXTENSION; userId = UserHandle.getUserId(info.applicationInfo.uid); taskId = _taskId; + lastActiveTime = SystemClock.elapsedRealtime(); mAffiliatedTaskId = _taskId; voiceSession = null; voiceInteractor = null; @@ -383,12 +385,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities, - long _firstActiveTime, long _lastActiveTime, long lastTimeMoved, - boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription, - int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor, - int callingUid, String callingPackage, int resizeMode, boolean supportsPictureInPicture, - boolean privileged, boolean _realActivitySuspended, boolean userSetupComplete, - int minWidth, int minHeight) { + long lastTimeMoved, boolean neverRelinquishIdentity, + TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId, + int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage, + int resizeMode, boolean supportsPictureInPicture, boolean privileged, + boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, + int minHeight) { mService = service; mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION; @@ -410,8 +412,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi userId = _userId; mUserSetupComplete = userSetupComplete; effectiveUid = _effectiveUid; - firstActiveTime = _firstActiveTime; - lastActiveTime = _lastActiveTime; + lastActiveTime = SystemClock.elapsedRealtime(); lastDescription = _lastDescription; mActivities = activities; mLastTimeMoved = lastTimeMoved; @@ -789,14 +790,11 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } void touchActiveTime() { - lastActiveTime = System.currentTimeMillis(); - if (firstActiveTime == 0) { - firstActiveTime = lastActiveTime; - } + lastActiveTime = SystemClock.elapsedRealtime(); } long getInactiveDuration() { - return System.currentTimeMillis() - lastActiveTime; + return SystemClock.elapsedRealtime() - lastActiveTime; } /** Sets the original intent, and the calling uid and package. */ @@ -1656,8 +1654,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi out.attribute(null, ATTR_USERID, String.valueOf(userId)); out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete)); out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid)); - out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime)); - out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime)); out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved)); out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity)); if (lastDescription != null) { @@ -1730,8 +1726,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi boolean userSetupComplete = true; int effectiveUid = -1; String lastDescription = null; - long firstActiveTime = -1; - long lastActiveTime = -1; long lastTimeOnTop = 0; boolean neverRelinquishIdentity = true; int taskId = INVALID_TASK_ID; @@ -1783,10 +1777,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi effectiveUid = Integer.parseInt(attrValue); } else if (ATTR_TASKTYPE.equals(attrName)) { taskType = Integer.parseInt(attrValue); - } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) { - firstActiveTime = Long.parseLong(attrValue); - } else if (ATTR_LASTACTIVETIME.equals(attrName)) { - lastActiveTime = Long.parseLong(attrValue); } else if (ATTR_LASTDESCRIPTION.equals(attrName)) { lastDescription = attrValue; } else if (ATTR_LASTTIMEMOVED.equals(attrName)) { @@ -1897,9 +1887,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent, affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset, autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription, - activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, - taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, - callingUid, callingPackage, resizeMode, supportsPictureInPicture, privileged, + activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription, + taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid, + callingPackage, resizeMode, supportsPictureInPicture, privileged, realActivitySuspended, userSetupComplete, minWidth, minHeight); task.updateOverrideConfiguration(bounds); @@ -2229,7 +2219,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode)); pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture); pw.print(" isResizeable=" + isResizeable()); - pw.print(" firstActiveTime=" + firstActiveTime); pw.print(" lastActiveTime=" + lastActiveTime); pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)"); } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index cc8bd69eac3f..20077f3e94b0 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -103,15 +103,21 @@ public class ActivityTestsBase { protected static TaskRecord createTask(ActivityStackSupervisor supervisor, ComponentName component, ActivityStack stack) { + return createTask(supervisor, component, 0 /* flags */, 0 /* taskId */, stack); + } + + protected static TaskRecord createTask(ActivityStackSupervisor supervisor, + ComponentName component, int flags, int taskId, ActivityStack stack) { final ActivityInfo aInfo = new ActivityInfo(); aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = component.getPackageName(); Intent intent = new Intent(); intent.setComponent(component); + intent.setFlags(flags); - final TaskRecord task = new TaskRecord(supervisor.mService, 0, aInfo, intent /*intent*/, - null /*_taskDescription*/); + final TaskRecord task = new TaskRecord(supervisor.mService, taskId, aInfo, + intent /*intent*/, null /*_taskDescription*/); supervisor.setFocusStackUnchecked("test", stack); stack.addTask(task, true, "creating test task"); task.setStack(stack); diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java new file mode 100644 index 000000000000..e607228efa24 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; +import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.UserInfo; +import android.os.Debug; +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.MediumTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.MutableLong; +import android.util.SparseBooleanArray; + +import com.android.server.am.RecentTasks.Callbacks; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java + */ +@MediumTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class RecentTasksTest extends ActivityTestsBase { + private static final int TEST_USER_0_ID = 0; + private static final int TEST_USER_1_ID = 10; + private static final int TEST_QUIET_USER_ID = 20; + private static final UserInfo DEFAULT_USER_INFO = new UserInfo(); + private static final UserInfo QUIET_USER_INFO = new UserInfo(); + private static int LAST_TASK_ID = 1; + + private Context mContext = InstrumentationRegistry.getContext(); + private ActivityManagerService mService; + private ActivityStack mStack; + private TestTaskPersister mTaskPersister; + private RecentTasks mRecentTasks; + + private static ArrayList<TaskRecord> mTasks = new ArrayList<>(); + private static ArrayList<TaskRecord> mSameDocumentTasks = new ArrayList<>(); + + private CallbacksRecorder mCallbacksRecorder; + + class TestUserController extends UserController { + TestUserController(ActivityManagerService service) { + super(service); + } + + @Override + int[] getCurrentProfileIds() { + return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID }; + } + + @Override + UserInfo getUserInfo(int userId) { + switch (userId) { + case TEST_USER_0_ID: + case TEST_USER_1_ID: + return DEFAULT_USER_INFO; + case TEST_QUIET_USER_ID: + return QUIET_USER_INFO; + } + return null; + } + } + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + + mService = createActivityManagerService(); + mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + mTaskPersister = new TestTaskPersister(mContext.getFilesDir()); + mRecentTasks = new RecentTasks(mService, mTaskPersister, new TestUserController(mService)); + mRecentTasks.loadParametersFromResources(mContext.getResources()); + mCallbacksRecorder = new CallbacksRecorder(); + mRecentTasks.registerCallback(mCallbacksRecorder); + QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE; + + mTasks.add(createTask(".Task1")); + mTasks.add(createTask(".Task2")); + mTasks.add(createTask(".Task3")); + mTasks.add(createTask(".Task4")); + mTasks.add(createTask(".Task5")); + + mSameDocumentTasks.add(createDocumentTask(".DocumentTask1", null /* affinity */)); + mSameDocumentTasks.add(createDocumentTask(".DocumentTask1", null /* affinity */)); + } + + @Test + public void testCallbacks() throws Exception { + // Add some tasks + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.add(mTasks.get(1)); + assertTrue(mCallbacksRecorder.added.contains(mTasks.get(0)) + && mCallbacksRecorder.added.contains(mTasks.get(1))); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.isEmpty()); + mCallbacksRecorder.clear(); + + // Remove some tasks + mRecentTasks.remove(mTasks.get(0)); + mRecentTasks.remove(mTasks.get(1)); + assertTrue(mCallbacksRecorder.added.isEmpty()); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(0))); + assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(1))); + mCallbacksRecorder.clear(); + + // Add a task which will trigger the trimming of another + TaskRecord documentTask1 = createDocumentTask(".DocumentTask1", null /* affinity */); + documentTask1.maxRecents = 1; + TaskRecord documentTask2 = createDocumentTask(".DocumentTask1", null /* affinity */); + mRecentTasks.add(documentTask1); + mRecentTasks.add(documentTask2); + assertTrue(mCallbacksRecorder.added.contains(documentTask1)); + assertTrue(mCallbacksRecorder.added.contains(documentTask2)); + assertTrue(mCallbacksRecorder.trimmed.contains(documentTask1)); + assertTrue(mCallbacksRecorder.removed.contains(documentTask1)); + mCallbacksRecorder.clear(); + + // Remove the callback, ensure we don't get any calls + mRecentTasks.unregisterCallback(mCallbacksRecorder); + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.remove(mTasks.get(0)); + assertTrue(mCallbacksRecorder.added.isEmpty()); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.isEmpty()); + } + + @Test + public void testUsersTasks() throws Exception { + // Setup some tasks for the users + mTaskPersister.userTaskIdsOverride = new SparseBooleanArray(); + mTaskPersister.userTaskIdsOverride.put(1, true); + mTaskPersister.userTaskIdsOverride.put(2, true); + mTaskPersister.userTasksOverride = new ArrayList<>(); + mTaskPersister.userTasksOverride.add(createTask(".UserTask1")); + mTaskPersister.userTasksOverride.add(createTask(".UserTask2")); + + // Assert no user tasks are initially loaded + assertTrue(mRecentTasks.usersWithRecentsLoadedLocked().length == 0); + + // Load user 0 tasks + mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID); + assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); + assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID)); + assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID)); + + // Load user 1 tasks + mRecentTasks.loadUserRecentsLocked(TEST_USER_1_ID); + assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); + assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID)); + assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID)); + assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID)); + assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_1_ID)); + assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_1_ID)); + + // Unload user 1 tasks + mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_1_ID); + assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); + assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID)); + assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID)); + assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID)); + + // Unload user 0 tasks + mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID); + assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); + assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID)); + } + + @Test + public void testOrderedIteration() throws Exception { + MutableLong prevLastActiveTime = new MutableLong(0); + final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks(); + for (int i = 0; i < tasks.size(); i++) { + final TaskRecord task = tasks.get(i); + assertTrue(task.lastActiveTime >= prevLastActiveTime.value); + prevLastActiveTime.value = task.lastActiveTime; + } + } + + @Test + public void testTrimToGlobalMaxNumRecents() throws Exception { + // Limit the global maximum number of recent tasks to a fixed size + mRecentTasks.setGlobalMaxNumTasks(2 /* globalMaxNumTasks */); + + // Add N+1 tasks + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.add(mTasks.get(1)); + mRecentTasks.add(mTasks.get(2)); + + // Ensure that the last task was trimmed as an inactive task + assertTrimmed(mTasks.get(0)); + } + + @Test + public void testTrimQuietProfileTasks() throws Exception { + TaskRecord qt1 = createTask(".QuietTask1", TEST_QUIET_USER_ID); + TaskRecord qt2 = createTask(".QuietTask2", TEST_QUIET_USER_ID); + mRecentTasks.add(qt1); + mRecentTasks.add(qt2); + + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.add(mTasks.get(1)); + + // Ensure that the quiet user's tasks was trimmed once the new tasks were added + assertTrimmed(qt1, qt2); + } + + @Test + public void testSessionDuration() throws Exception { + mRecentTasks.setParameters(-1 /* min */, -1 /* max */, 50 /* ms */); + + TaskRecord t1 = createTask(".Task1"); + t1.touchActiveTime(); + mRecentTasks.add(t1); + + // Force a small sleep just beyond the session duration + SystemClock.sleep(75); + + TaskRecord t2 = createTask(".Task2"); + t2.touchActiveTime(); + mRecentTasks.add(t2); + + // Assert that the old task has been removed due to being out of the active session + assertTrimmed(t1); + } + + @Test + public void testVisibleTasks_excludedFromRecents() throws Exception { + mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */); + + TaskRecord excludedTask1 = createTask(".ExcludedTask1", FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, + TEST_USER_0_ID); + TaskRecord excludedTask2 = createTask(".ExcludedTask2", FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, + TEST_USER_0_ID); + + mRecentTasks.add(excludedTask1); + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.add(mTasks.get(1)); + mRecentTasks.add(mTasks.get(2)); + mRecentTasks.add(excludedTask2); + + // The last excluded task should be trimmed, while the first-most excluded task should not + assertTrimmed(excludedTask1); + } + + @Test + public void testVisibleTasks_minNum() throws Exception { + mRecentTasks.setParameters(5 /* min */, -1 /* max */, 25 /* ms */); + + for (int i = 0; i < 4; i++) { + final TaskRecord task = mTasks.get(i); + task.touchActiveTime(); + mRecentTasks.add(task); + } + + // Force a small sleep just beyond the session duration + SystemClock.sleep(50); + + // Add a new task to trigger tasks to be trimmed + mRecentTasks.add(mTasks.get(4)); + + // Ensure that there are a minimum number of tasks regardless of session length + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.isEmpty()); + } + + @Test + public void testVisibleTasks_maxNum() throws Exception { + mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */); + + for (int i = 0; i < 5; i++) { + final TaskRecord task = mTasks.get(i); + task.touchActiveTime(); + mRecentTasks.add(task); + } + + // Ensure that only the last number of max tasks are kept + assertTrimmed(mTasks.get(0), mTasks.get(1)); + } + + private ComponentName createComponent(String className) { + return new ComponentName(mContext.getPackageName(), className); + } + + private TaskRecord createTask(String className) { + return createTask(className, TEST_USER_0_ID); + } + + private TaskRecord createTask(String className, int userId) { + return createTask(className, 0 /* flags */, userId); + } + + private TaskRecord createTask(String className, int flags, int userId) { + TaskRecord task = createTask(mService.mStackSupervisor, createComponent(className), flags, + LAST_TASK_ID++, mStack); + task.userId = userId; + task.touchActiveTime(); + return task; + } + + private TaskRecord createDocumentTask(String className, String affinity) { + TaskRecord task = createTask(className, FLAG_ACTIVITY_NEW_DOCUMENT, TEST_USER_0_ID); + task.affinity = affinity; + return task; + } + + private boolean arrayContainsUser(int[] userIds, int targetUserId) { + Arrays.sort(userIds); + return Arrays.binarySearch(userIds, targetUserId) >= 0; + } + + private void assertTrimmed(TaskRecord... tasks) { + final ArrayList<TaskRecord> trimmed = mCallbacksRecorder.trimmed; + final ArrayList<TaskRecord> removed = mCallbacksRecorder.removed; + assertTrue("Expected " + tasks.length + " trimmed tasks, got " + trimmed.size(), + trimmed.size() == tasks.length); + assertTrue("Expected " + tasks.length + " removed tasks, got " + removed.size(), + removed.size() == tasks.length); + for (TaskRecord task : tasks) { + assertTrue("Expected trimmed task: " + task, trimmed.contains(task)); + assertTrue("Expected removed task: " + task, removed.contains(task)); + } + } + + private static class CallbacksRecorder implements Callbacks { + ArrayList<TaskRecord> added = new ArrayList<>(); + ArrayList<TaskRecord> trimmed = new ArrayList<>(); + ArrayList<TaskRecord> removed = new ArrayList<>(); + + void clear() { + added.clear(); + trimmed.clear(); + removed.clear(); + } + + @Override + public void onRecentTaskAdded(TaskRecord task) { + added.add(task); + } + + @Override + public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) { + if (wasTrimmed) { + trimmed.add(task); + } + removed.add(task); + } + } + + private static class TestTaskPersister extends TaskPersister { + + SparseBooleanArray userTaskIdsOverride; + ArrayList<TaskRecord> userTasksOverride; + + TestTaskPersister(File workingDir) { + super(workingDir); + } + + @Override + SparseBooleanArray loadPersistedTaskIdsForUser(int userId) { + if (userTaskIdsOverride != null) { + return userTaskIdsOverride; + } + return super.loadPersistedTaskIdsForUser(userId); + } + + @Override + List<TaskRecord> restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks) { + if (userTasksOverride != null) { + return userTasksOverride; + } + return super.restoreTasksForUserLocked(userId, preaddedTasks); + } + } +}
\ No newline at end of file |