背景
Android12项目中,我们的LowRam设备被测试组报了个bug,说打开了十几个应用,但最近应用列表中最多只能有9个应用历史,而对比机pixel上则无此限制。
最初我以为是Launcher3的问题,因为launcher3分了好几个版本,其中就有针对GO设备的,因此从launcher3上入手追查了一下。从显示最近应用历史的view上入手,到获取最近应用列表的路线查。
分析
先从launcher3的RecentTasksList代码上入手,launcher显示最近应用列表的来源在:
// packages/apps/Launcher3/quickstep/src/com/android/quickstep/RecentTasksList.java
@VisibleForTesting
TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
int currentUserId = Process.myUserHandle().getIdentifier();
List<ActivityManager.RecentTaskInfo> rawTasks =
mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(rawTasks);
……
}
RecentTasksList调用loadTasksInBackground在后台从AMS中获取RecentTasks,所以最近应用列表实际上是AMS进行管理的,这个非常好理解,毕竟AMS是所有Activity的管理者,最近所启动的应用在AMS中必定能监控到。
getRecentTasks方法第一个参数是获取最近应用的个数,那么最初是怀疑此处numTasks的值限制了low ram设备的launcher3最多只能显示9个最近应用,因此查了一下这个参数的来源,其来源如下:
// packages/apps/Launcher3/quickstep/src/com/android/quickstep/RecentTasksList.java
public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
// Kick off task loading in the background
mLoadingTasksInBackground = true;
UI_HELPER_EXECUTOR.execute(() -> {
if (!mResultsBg.isValidForRequest(requestLoadId, loadKeysOnly)) {
mResultsBg = loadTasksInBackground(Integer.MAX_VALUE, requestLoadId, loadKeysOnly);
}
TaskLoadResult loadResult = mResultsBg;
mMainThreadExecutor.execute(() -> {
mLoadingTasksInBackground = false;
mResultsUi = loadResult;
if (callback != null) {
ArrayList<Task> result = copyOf(mResultsUi);
callback.accept(result);
}
});
});
return requestLoadId;
}
同样是RecentTasksList中,getTasks方法会去调用loadTasksInBackground方法去加载RecentTasks,而传入的第一个参数是int类型数据的最大值,即不做任何限制,因此可以排除掉是launcher3自身的限制。
排除掉launcher3的嫌疑后,只能把目光投向到AMS中。针对于GO设备而言,AMS可能为了节省内存,会去对RecentTasks的个数做了限制。因此我们继续去看AMS.getRecentTasks这个方法的具体实现。
AMS.getRecentTasks最终是调用到RecentTask.getRecentTasksImpl中:
// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
boolean getTasksAllowed, int userId, int callingUid) {
final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
if (!isUserRunning(userId, FLAG_AND_UNLOCKED)) {
Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
return new ArrayList<>();
}
loadUserRecentsLocked(userId);
final Set<Integer> includedUsers = 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 Task task = mTasks.get(i);
if (isVisibleRecentTask(task)) {
numVisibleTasks++;
if (isInVisibleRange(task, i, numVisibleTasks, withExcluded)) {
// Fall through
} else {
// Not in visible range
continue;
}
} else {
// Not visible
continue;
}
// Skip remaining tasks once we reach the requested size
if (res.size() >= maxNum) {
continue;
}
// Only add calling user or related users recent tasks
if (!includedUsers.contains(Integer.valueOf(task.mUserId))) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + task);
continue;
}
if (task.realActivitySuspended) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + task);
continue;
}
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 (!task.isActivityTypeHome() && task.effectiveUid != callingUid) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + task);
continue;
}
}
if (task.autoRemoveRecents && task.getTopNonFinishingActivity() == null) {
// Don't include auto remove tasks that are finished or finishing.
if (DEBUG_RECENTS) {
Slog.d(TAG_RECENTS, "Skipping, auto-remove without activity: " + task);
}
continue;
}
if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !task.isAvailable) {
if (DEBUG_RECENTS) {
Slog.d(TAG_RECENTS, "Skipping, unavail real act: " + task);
}
continue;
}
if (!task.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: " + task);
}
continue;
}
res.add(createRecentTaskInfo(task, true /* stripExtras */));
}
return res;
}
从getRecentTasksImpl的源码中推测,此方法并没有什么明显的限制,最多只是判断了Task的合法性,那么只能怀疑是mTasks中的个数可能真的是最多只有9个,因此,在此处加了个打印,在调用进来是将mTasks的元素个数值打印出来,发现确实到9后,再打开新的应用,此时该值仍是9。因此继续追查mTasks会在哪有remove的操作,将超出限制的元素清除掉。在源码中搜索mTasks.remove,排查所有调用的地方。
下面就是可疑之处,然后加上打印,发现确实每次打印新的应用,增加新的RecentTask时,就会被触发,trimInactiveRecentTasks方法中有两处mTasks.remove调用:
// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
private void trimInactiveRecentTasks() {
if (mFreezeTaskListReordering) {
// Defer trimming inactive recent tasks until we are unfrozen
return;
}
int recentsCount = mTasks.size();
// Remove from the end of the list until we reach the max number of recents
while (recentsCount > mGlobalMaxNumTasks) {
final Task task = mTasks.remove(recentsCount - 1);
notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
recentsCount--;
if (DEBUG_RECENTS_TRIM_TASKS) {
Slog.d(TAG, "Trimming over max-recents task=" + task
+ " max=" + mGlobalMaxNumTasks);
}
}
// Remove any tasks that belong to currently quiet profiles
final int[] profileUserIds = getCurrentProfileIds();
mTmpQuietProfileUserIds.clear();
for (int userId : profileUserIds) {
final UserInfo userInfo = getUserInfo(userId);
if (userInfo != null && 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 Task 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, i, numVisibleTasks, false /* skipExcludedCheck */)
|| !isTrimmable(task)) {
// Keep visible tasks in range
i++;
continue;
} else {
// Fall through to trim visible tasks that are no longer in range and
// trimmable
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, true /* wasTrimmed */, false /* killProcess */);
notifyTaskPersisterLocked(task, false /* flush */);
}
}
其中一处是由mGlobalMaxNumTasks这个值来控制,当mTasks元素个数超过这个值时,则会remove掉超出的Task。经过打印,mGlobalMaxNumTasks这个值并不是9,这个值得来源如下:
mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
// frameworks/base/core/java/android/app/ActivityTaskManager.java
public static int getMaxRecentTasksStatic() {
if (sMaxRecentTasks < 0) {
return sMaxRecentTasks = ActivityManager.isLowRamDeviceStatic() ? 36 : 48;
}
return sMaxRecentTasks;
}
对于LowRam设备而言,此值为36,也就是说,GO设置最多只能有36条RecentTask记录,而非GO最多只能有48个。
但这个值仍不是这个问题的限制值,在加上打印是,可以得知trimInactiveRecentTasks方法remove超出的Task是方法的结束地方,回到trimInactiveRecentTasks方法,可以看到,这个方法会遍历mTasks的所有元素,然后将不符合要求的Task remove掉,而这些条件中,有一个方法是最符合我们的想法的 -- isInVisibleRange:
// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
private boolean isInVisibleRange(Task task, int taskIndex, int numVisibleTasks,
boolean skipExcludedCheck) {
if (!skipExcludedCheck) {
// Keep the most recent task even if it is excluded from recents
final boolean isExcludeFromRecents =
(task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
== FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
if (isExcludeFromRecents) {
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
return taskIndex == 0;
}
}
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;
}
// The given task if always treated as in visible range if it is the origin of pinned task.
if (task.mChildPipActivity != null) 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;
}
上面的isInVisibleRange方法中,可以看到一个很显眼的变量 -- mMaxNumVisibleTasks,当mMaxNumVisibleTasks这个值大于等于0时,则表明生效,如果可见的RecentTasks个数小于mMaxNumVisibleTasks的值,则跳过mMaxNumVisibleTasks中的remove操作,但如果超出了,则会执行remove操作。
再看mMaxNumVisibleTasks这个值的来源:
1.RecentTasks.setParameters,通过源码的搜索及注解,可以认为此接口只为测试所用。
2.RecentTasks.loadParametersFromResources,这个是根据不同的方案类型读取了不同的值:
// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
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;
}
可以看到LowRam设备的值来源于config_maxNumVisibleRecentTasks_lowRam这个配置。原生配置中,此值就是9:
./core/res/res/values/config.xml:2915: <integer name="config_maxNumVisibleRecentTasks_lowRam">9</integer>
问题澄清。