summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityManager.java62
-rw-r--r--core/java/android/app/IActivityManager.aidl4
-rwxr-xr-xcore/java/android/provider/Settings.java8
-rw-r--r--core/res/res/values/config.xml30
-rw-r--r--core/res/res/values/symbols.xml7
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/Task.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java439
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java29
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java52
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/am/AppTaskImpl.java150
-rw-r--r--services/core/java/com/android/server/am/RecentTasks.java1042
-rw-r--r--services/core/java/com/android/server/am/TaskPersister.java19
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java419
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