diff options
author | Dianne Hackborn <hackbod@google.com> | 2014-08-22 17:42:43 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2014-08-26 21:24:42 -0700 |
commit | 852975d5377bfe5f4abc9d2a28e301aa2fa99994 (patch) | |
tree | f993644fd2a52f53268e679078428f23bb7d13ab | |
parent | 330459d685bdc77780e8b77f6cbd6f94ecdd3268 (diff) | |
download | frameworks_base-852975d5377bfe5f4abc9d2a28e301aa2fa99994.zip frameworks_base-852975d5377bfe5f4abc9d2a28e301aa2fa99994.tar.gz frameworks_base-852975d5377bfe5f4abc9d2a28e301aa2fa99994.tar.bz2 |
Fix issue #17179314: Make recents limits consistent
The max limit is now 100 (or 50 on svelte devices), and that is
what everyone used.
Re-arranged things so we have a big expensive "fix the world!"
function for recents that we go in to at only select points:
when first initializing the system, when external storage comes
and goes, and if we detect something wrong with the recents
structure.
With that, now getRecentTasks() and addRecentTaskLocked() are
generally much simpler, doing very little work in most cases.
This will help a lot with scaling up to many more recents
entries.
Change-Id: I7b5ae89edc06568f68c8af54a4420aff7635581c
9 files changed, 493 insertions, 129 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 586e7d4..2eeacae 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -67,6 +67,8 @@ public class ActivityManager { private static String TAG = "ActivityManager"; private static boolean localLOGV = false; + private static int gMaxRecentTasks = -1; + private final Context mContext; private final Handler mHandler; @@ -449,7 +451,7 @@ public class ActivityManager { // Really brain dead right now -- just take this from the configured // vm heap size, and assume it is in megabytes and thus ends with "m". String vmHeapSize = SystemProperties.get("dalvik.vm.heapsize", "16m"); - return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1)); + return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length() - 1)); } /** @@ -480,6 +482,33 @@ public class ActivityManager { } /** + * Return the maximum number of recents entries that we will maintain and show. + * @hide + */ + static public int getMaxRecentTasksStatic() { + if (gMaxRecentTasks < 0) { + return gMaxRecentTasks = isLowRamDeviceStatic() ? 50 : 100; + } + return gMaxRecentTasks; + } + + /** + * Return the default limit on the number of recents that an app can make. + * @hide + */ + static public int getDefaultAppRecentsLimitStatic() { + return getMaxRecentTasksStatic() / 6; + } + + /** + * Return the maximum limit on the number of recents that an app can make. + * @hide + */ + static public int getMaxAppRecentsLimitStatic() { + return getMaxRecentTasksStatic() / 2; + } + + /** * Information you can set and retrieve about the current activity within the recent task list. */ public static class TaskDescription implements Parcelable { @@ -1191,13 +1220,13 @@ public class ActivityManager { public void writeToParcel(Parcel dest, int flags) { if (mainThumbnail != null) { dest.writeInt(1); - mainThumbnail.writeToParcel(dest, 0); + mainThumbnail.writeToParcel(dest, flags); } else { dest.writeInt(0); } if (thumbnailFileDescriptor != null) { dest.writeInt(1); - thumbnailFileDescriptor.writeToParcel(dest, 0); + thumbnailFileDescriptor.writeToParcel(dest, flags); } else { dest.writeInt(0); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index b09d3ac..6d40dcf 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -25,6 +25,7 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -3077,7 +3078,7 @@ public class PackageParser { ActivityInfo.DOCUMENT_LAUNCH_NONE); a.info.maxRecents = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_maxRecents, - 15); + ActivityManager.getDefaultAppRecentsLimitStatic()); a.info.screenOrientation = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index fa00ebf..e940b18 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -145,8 +145,6 @@ <integer name="recents_animate_task_view_remove_duration">250</integer> <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. --> <integer name="recents_max_task_stack_view_dim">96</integer> - <!-- The number of tasks that RecentsTaskLoader should load. --> - <integer name="recents_max_num_tasks_to_load">50</integer> <!-- The delay to enforce between each alt-tab key press. --> <integer name="recents_alt_tab_key_delay">200</integer> <!-- Transposes the recents layout in landscape. --> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index b7f6451..9803687 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -16,6 +16,7 @@ package com.android.systemui.recents; +import android.app.ActivityManager; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -187,7 +188,7 @@ public class RecentsConfiguration { res.getInteger(R.integer.recents_filter_animate_new_views_duration); // Loading - maxNumTasksToLoad = res.getInteger(R.integer.recents_max_num_tasks_to_load); + maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic(); // Search Bar searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e2a6534..eff420f 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -283,9 +283,6 @@ public final class ActivityManagerService extends ActivityManagerNative static final boolean IS_USER_BUILD = "user".equals(Build.TYPE); - // Maximum number of recent tasks that we can remember. - static final int MAX_RECENT_TASKS = ActivityManager.isLowRamDeviceStatic() ? 100 : 200; - // Maximum number recent bitmaps to keep in memory. static final int MAX_RECENT_BITMAPS = 5; @@ -3808,8 +3805,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (tr.userId == userId) { if(DEBUG_TASKS) Slog.i(TAG, "remove RecentTask " + tr + " when finishing user" + userId); - tr.disposeThumbnail(); mRecentTasks.remove(i); + tr.removedFromRecents(mTaskPersister); } } @@ -3817,25 +3814,385 @@ public final class ActivityManagerService extends ActivityManagerNative mTaskPersister.wakeup(null, true); } + /** + * Update the recent tasks lists: make sure tasks should still be here (their + * applications / activities still exist), update their availability, fixup ordering + * of affiliations. + */ + void cleanupRecentTasksLocked(int userId) { + final HashMap<ComponentName, ActivityInfo> availActCache = new HashMap<>(); + final HashMap<String, ApplicationInfo> availAppCache = new HashMap<>(); + final IPackageManager pm = AppGlobals.getPackageManager(); + final ActivityInfo dummyAct = new ActivityInfo(); + final ApplicationInfo dummyApp = new ApplicationInfo(); + + int N = mRecentTasks.size(); + + int[] users = userId == UserHandle.USER_ALL + ? getUsersLocked() : new int[] { userId }; + for (int user : users) { + for (int i = 0; i < N; i++) { + TaskRecord task = mRecentTasks.get(i); + if (task.userId != user) { + // 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. + mRecentTasks.remove(i); + task.removedFromRecents(mTaskPersister); + i--; + N--; + Slog.w(TAG, "Removing auto-remove without activity: " + task); + continue; + } + // Check whether this activity is currently available. + if (task.realActivity != null) { + ActivityInfo ai = availActCache.get(task.realActivity); + if (ai == null) { + try { + ai = pm.getActivityInfo(task.realActivity, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS, user); + } catch (RemoteException e) { + // Will never happen. + continue; + } + if (ai == null) { + ai = dummyAct; + } + availActCache.put(task.realActivity, ai); + } + if (ai == dummyAct) { + // 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. + ApplicationInfo app = availAppCache.get(task.realActivity.getPackageName()); + if (app == null) { + try { + app = pm.getApplicationInfo(task.realActivity.getPackageName(), + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS, user); + } catch (RemoteException e) { + // Will never happen. + continue; + } + if (app == null) { + app = dummyApp; + } + availAppCache.put(task.realActivity.getPackageName(), app); + } + if (app == dummyApp || (app.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { + // Doesn't exist any more! Good-bye. + mRecentTasks.remove(i); + task.removedFromRecents(mTaskPersister); + i--; + N--; + Slog.w(TAG, "Removing no longer valid recent: " + task); + continue; + } else { + // Otherwise just not available for now. + if (task.isAvailable) { + if (DEBUG_RECENTS) Slog.d(TAG, "Making recent unavailable: " + + task); + } + task.isAvailable = false; + } + } else { + if (!ai.enabled || !ai.applicationInfo.enabled + || (ai.applicationInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { + if (task.isAvailable) { + if (DEBUG_RECENTS) Slog.d(TAG, "Making recent unavailable: " + + task + " (enabled=" + ai.enabled + "/" + + ai.applicationInfo.enabled + " flags=" + + Integer.toHexString(ai.applicationInfo.flags) + ")"); + } + task.isAvailable = false; + } else { + if (!task.isAvailable) { + if (DEBUG_RECENTS) Slog.d(TAG, "Making recent available: " + + task); + } + task.isAvailable = true; + } + } + } + } + } + + // Verify the affiliate chain for each task. + for (int i = 0; i < N; ) { + TaskRecord task = mRecentTasks.remove(i); + if (mTmpRecents.contains(task)) { + continue; + } + int affiliatedTaskId = task.mAffiliatedTaskId; + while (true) { + TaskRecord next = task.mNextAffiliate; + if (next == null) { + break; + } + if (next.mAffiliatedTaskId != affiliatedTaskId) { + Slog.e(TAG, "Error in Recents: next.affiliatedTaskId=" + + next.mAffiliatedTaskId + " affiliatedTaskId=" + affiliatedTaskId); + task.setNextAffiliate(null); + if (next.mPrevAffiliate == task) { + next.setPrevAffiliate(null); + } + break; + } + if (next.mPrevAffiliate != task) { + Slog.e(TAG, "Error in Recents chain prev.mNextAffiliate=" + + next.mPrevAffiliate + " task=" + task); + next.setPrevAffiliate(null); + task.setNextAffiliate(null); + break; + } + if (!mRecentTasks.contains(next)) { + Slog.e(TAG, "Error in Recents: next=" + next + " not in mRecentTasks"); + task.setNextAffiliate(null); + // We know that next.mPrevAffiliate is always task, from above, so clear + // its previous affiliate. + next.setPrevAffiliate(null); + break; + } + task = next; + } + // task is now the end of the list + do { + mRecentTasks.remove(task); + mRecentTasks.add(i++, task); + mTmpRecents.add(task); + task.inRecents = true; + } while ((task = task.mPrevAffiliate) != null); + } + mTmpRecents.clear(); + // mRecentTasks is now in sorted, affiliated order. + } + + private final boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) { + int N = mRecentTasks.size(); + TaskRecord top = task; + int topIndex = taskIndex; + while (top.mNextAffiliate != null && topIndex > 0) { + top = top.mNextAffiliate; + topIndex--; + } + if (DEBUG_RECENTS) Slog.d(TAG, "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 < N) { + TaskRecord cur = mRecentTasks.get(endIndex); + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: looking at next chain @" + + endIndex + " " + cur); + if (cur == top) { + // Verify start of the chain. + if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != -1) { + 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 == -1) { + // 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, "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 >= N) { + 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, "addRecent: moving affiliated " + task + + " from " + i + " to " + (i-topIndex)); + TaskRecord cur = mRecentTasks.remove(i); + mRecentTasks.add(i-topIndex, cur); + } + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: done moving tasks " + topIndex + + " to " + endIndex); + return true; + } + + // Whoops, couldn't do it. + return false; + } + final void addRecentTaskLocked(TaskRecord task) { - final int N = mRecentTasks.size(); + final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId + || task.mNextAffiliateTaskId != -1 || task.mPrevAffiliateTaskId != -1; + + int N = mRecentTasks.size(); // Quick case: check if the top-most recent task is the same. - if (N > 0 && mRecentTasks.get(0) == task) { + if (!isAffiliated && N > 0 && mRecentTasks.get(0) == task) { + if (DEBUG_RECENTS) Slog.d(TAG, "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 && N > 0 && task.inRecents + && task.mAffiliatedTaskId == mRecentTasks.get(0).mAffiliatedTaskId) { + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: affiliated " + mRecentTasks.get(0) + + " at top when adding " + task); return; } // Another quick case: never add voice sessions. if (task.voiceSession != null) { + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: not adding voice interaction " + task); return; } + boolean needAffiliationFix = false; + + // Slightly less quick case: the task is already in recents, so all we need + // to do is move it. + if (task.inRecents) { + int taskIndex = mRecentTasks.indexOf(task); + if (taskIndex >= 0) { + if (!isAffiliated) { + // Simple case: this is not an affiliated task, so we just move it to the front. + mRecentTasks.remove(taskIndex); + mRecentTasks.add(0, task); + notifyTaskPersisterLocked(task, false); + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: moving to top " + task + + " from " + taskIndex); + return; + } else { + // More complicated: need to keep all affiliated tasks together. + if (moveAffiliatedTasksToFront(task, taskIndex)) { + // All went well. + return; + } + + // Uh oh... something bad in the affiliation chain, try to rebuild + // everything and then go through our general path of adding a new task. + needAffiliationFix = true; + } + } else { + Slog.wtf(TAG, "Task with inRecent not in recents: " + task); + needAffiliationFix = true; + } + } + + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: trimming tasks for " + task); trimRecentsForTask(task, true); - if (N >= MAX_RECENT_TASKS) { + N = mRecentTasks.size(); + while (N >= ActivityManager.getMaxRecentTasksStatic()) { final TaskRecord tr = mRecentTasks.remove(N - 1); - tr.disposeThumbnail(); - tr.closeRecentsChain(); + tr.removedFromRecents(mTaskPersister); + N--; + } + 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. + mRecentTasks.add(0, task); + } else if (isAffiliated) { + // If this is a new affiliated task, then move all of the affiliated tasks + // to the front and insert this new one. + TaskRecord other = task.mNextAffiliate; + if (other == null) { + other = task.mPrevAffiliate; + } + if (other != null) { + int otherIndex = mRecentTasks.indexOf(other); + if (otherIndex >= 0) { + // Insert new task at appropriate location. + int taskIndex; + if (other == task.mNextAffiliate) { + // We found the index of our next affiliation, which is who is + // before us in the list, so add after that point. + taskIndex = otherIndex+1; + } else { + // We found the index of our previous affiliation, which is who is + // after us in the list, so add at their position. + taskIndex = otherIndex; + } + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: new affiliated task added at " + + taskIndex + ": " + task); + mRecentTasks.add(taskIndex, task); + + // Now move everything to the front. + if (moveAffiliatedTasksToFront(task, taskIndex)) { + // All went well. + return; + } + + // Uh oh... something bad in the affiliation chain, try to rebuild + // everything and then go through our general path of adding a new task. + needAffiliationFix = true; + } else { + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: couldn't find other affiliation " + + other); + needAffiliationFix = true; + } + } else { + if (DEBUG_RECENTS) Slog.d(TAG, + "addRecent: adding affiliated task without next/prev:" + task); + needAffiliationFix = true; + } + } + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: adding " + task); + + if (needAffiliationFix) { + if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: regrouping affiliations"); + cleanupRecentTasksLocked(task.userId); } - mRecentTasks.add(0, task); } /** @@ -3889,7 +4246,7 @@ public final class ActivityManagerService extends ActivityManagerNative tr.disposeThumbnail(); mRecentTasks.remove(i); if (task != tr) { - tr.closeRecentsChain(); + tr.removedFromRecents(mTaskPersister); } i--; N--; @@ -7689,8 +8046,6 @@ public final class ActivityManagerService extends ActivityManagerNative android.Manifest.permission.GET_DETAILED_TASKS) == PackageManager.PERMISSION_GRANTED; - IPackageManager pm = AppGlobals.getPackageManager(); - final int N = mRecentTasks.size(); ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<ActivityManager.RecentTaskInfo>( @@ -7704,53 +8059,6 @@ public final class ActivityManagerService extends ActivityManagerNative } includedUsers.add(Integer.valueOf(userId)); - // Regroup affiliated tasks together. - for (int i = 0; i < N; ) { - TaskRecord task = mRecentTasks.remove(i); - if (mTmpRecents.contains(task)) { - continue; - } - int affiliatedTaskId = task.mAffiliatedTaskId; - while (true) { - TaskRecord next = task.mNextAffiliate; - if (next == null) { - break; - } - if (next.mAffiliatedTaskId != affiliatedTaskId) { - Slog.e(TAG, "Error in Recents: next.affiliatedTaskId=" + - next.mAffiliatedTaskId + " affiliatedTaskId=" + affiliatedTaskId); - task.setNextAffiliate(null); - if (next.mPrevAffiliate == task) { - next.setPrevAffiliate(null); - } - break; - } - if (next.mPrevAffiliate != task) { - Slog.e(TAG, "Error in Recents chain prev.mNextAffiliate=" + - next.mPrevAffiliate + " task=" + task); - next.setPrevAffiliate(null); - break; - } - if (!mRecentTasks.contains(next)) { - Slog.e(TAG, "Error in Recents: next=" + next + " not in mRecentTasks"); - task.setNextAffiliate(null); - if (next.mPrevAffiliate == task) { - next.setPrevAffiliate(null); - } - break; - } - task = next; - } - // task is now the end of the list - do { - mRecentTasks.remove(task); - mRecentTasks.add(i++, task); - mTmpRecents.add(task); - } while ((task = task.mPrevAffiliate) != null); - } - mTmpRecents.clear(); - // mRecentTasks is now in sorted, affiliated order. - for (int i=0; i<N && maxNum > 0; i++) { TaskRecord tr = mRecentTasks.get(i); // Only add calling user or related users recent tasks @@ -7790,35 +8098,17 @@ public final class ActivityManagerService extends ActivityManagerNative + tr); continue; } + if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0 + && !tr.isAvailable) { + if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, unavail real act: " + tr); + continue; + } ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr); if (!detailed) { rti.baseIntent.replaceExtras((Bundle)null); } - if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) { - // Check whether this activity is currently available. - try { - if (rti.origActivity != null) { - if (pm.getActivityInfo(rti.origActivity, 0, userId) - == null) { - if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, unavail orig act: " - + tr); - continue; - } - } else if (rti.baseIntent != null) { - if (pm.queryIntentActivities(rti.baseIntent, - null, 0, userId) == null) { - if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, unavail intent: " - + tr); - continue; - } - } - } catch (RemoteException e) { - // Will never happen. - } - } - res.add(rti); maxNum--; } @@ -7916,12 +8206,12 @@ public final class ActivityManagerService extends ActivityManagerNative } final int N = mRecentTasks.size(); - if (N >= (MAX_RECENT_TASKS-1)) { + if (N >= (ActivityManager.getMaxRecentTasksStatic()-1)) { final TaskRecord tr = mRecentTasks.remove(N - 1); - tr.disposeThumbnail(); - tr.closeRecentsChain(); + tr.removedFromRecents(mTaskPersister); } + task.inRecents = true; mRecentTasks.add(task); r.task.stack.addTask(task, false, false); @@ -7954,9 +8244,8 @@ public final class ActivityManagerService extends ActivityManagerNative } private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) { - tr.disposeThumbnail(); mRecentTasks.remove(tr); - tr.closeRecentsChain(); + tr.removedFromRecents(mTaskPersister); final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0; Intent baseIntent = new Intent( tr.intent != null ? tr.intent : tr.affinityIntent); @@ -10566,6 +10855,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (!mRecentTasks.isEmpty()) { mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks); } + cleanupRecentTasksLocked(UserHandle.USER_ALL); mTaskPersister.startPersisting(); } @@ -14799,6 +15089,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) || Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction()) || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction()) + || Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction()) || uidRemoved) { if (checkComponentPermission( android.Manifest.permission.BROADCAST_PACKAGE_REMOVED, @@ -14825,9 +15116,13 @@ public final class ActivityManagerService extends ActivityManagerNative forceStopPackageLocked(pkg, -1, false, true, true, false, false, userId, "storage unmount"); } + cleanupRecentTasksLocked(UserHandle.USER_ALL); sendPackageBroadcastLocked( IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, userId); } + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals( + intent.getAction())) { + cleanupRecentTasksLocked(UserHandle.USER_ALL); } else { Uri data = intent.getData(); String ssp; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index fd1474f..be0afbb 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -4040,7 +4040,7 @@ final class ActivityStack { // 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. mService.mRecentTasks.remove(task); - task.closeRecentsChain(); + task.removedFromRecents(mService.mTaskPersister); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index a9898ee..9d7af03 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1688,7 +1688,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // If the caller is not coming from another activity, but has given us an // explicit task into which they would like us to launch the new activity, // then let's see about doing that. - if (sourceRecord == null && inTask != null && inTask.stack != null) { + if (sourceRecord == null && inTask != null && inTask.stack != null && inTask.inRecents) { // If this task is empty, then we are adding the first activity -- it // determines the root, and must be launching as a NEW_TASK. if (inTask.getRootActivity() == null) { diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index 0180db3..3cc406b 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -273,11 +273,14 @@ public class TaskPersister { if (TAG_TASK.equals(name)) { final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor); - if (true || DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + + if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task); if (task != null) { task.isPersistable = true; - mWriteQueue.add(new TaskWriteQueueItem(task)); + // XXX Don't add to write queue... there is no reason to write + // out the stuff we just read, if we don't write it we will + // read the same thing again. + //mWriteQueue.add(new TaskWriteQueueItem(task)); tasks.add(task); final int taskId = task.taskId; recoveredTaskIds.add(taskId); @@ -486,24 +489,24 @@ public class TaskPersister { } catch (XmlPullParserException e) { } } - if (stringWriter != null) { - // Write out xml file while not holding mService lock. - FileOutputStream file = null; - AtomicFile atomicFile = null; - try { - atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf( - task.taskId) + RECENTS_FILENAME + TASK_EXTENSION)); - file = atomicFile.startWrite(); - file.write(stringWriter.toString().getBytes()); - file.write('\n'); - atomicFile.finishWrite(file); - } catch (IOException e) { - if (file != null) { - atomicFile.failWrite(file); - } - Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " + - e); + } + if (stringWriter != null) { + // Write out xml file while not holding mService lock. + FileOutputStream file = null; + AtomicFile atomicFile = null; + try { + atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf( + task.taskId) + RECENTS_FILENAME + TASK_EXTENSION)); + file = atomicFile.startWrite(); + file.write(stringWriter.toString().getBytes()); + file.write('\n'); + atomicFile.finishWrite(file); + } catch (IOException e) { + if (file != null) { + atomicFile.failWrite(file); } + Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " + + e); } } } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 7e42232..071db97 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -94,6 +94,8 @@ final class TaskRecord { ComponentName realActivity; // The actual activity component that started the task. 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? + 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. boolean autoRemoveRecents; // If true, we should automatically remove the task from @@ -170,6 +172,7 @@ final class TaskRecord { mAffiliatedTaskId = _taskId; voiceSession = _voiceSession; voiceInteractor = _voiceInteractor; + isAvailable = true; mActivities = new ArrayList<ActivityRecord>(); setIntent(_intent, info); } @@ -184,6 +187,7 @@ final class TaskRecord { mAffiliatedTaskId = _taskId; voiceSession = null; voiceInteractor = null; + isAvailable = true; mActivities = new ArrayList<ActivityRecord>(); setIntent(_intent, info); @@ -191,8 +195,9 @@ final class TaskRecord { isPersistable = true; mCallingUid = info.applicationInfo.uid; mCallingPackage = info.packageName; - // Clamp to [1, 100]. - maxRecents = Math.min(Math.max(info.maxRecents, 1), 100); + // Clamp to [1, max]. + maxRecents = Math.min(Math.max(info.maxRecents, 1), + ActivityManager.getMaxAppRecentsLimitStatic()); taskType = APPLICATION_ACTIVITY_TYPE; mTaskToReturnTo = HOME_ACTIVITY_TYPE; @@ -224,6 +229,7 @@ final class TaskRecord { realActivity = _realActivity; origActivity = _origActivity; rootWasReset = _rootWasReset; + isAvailable = true; autoRemoveRecents = _autoRemoveRecents; askedCompatMode = _askedCompatMode; taskType = _taskType; @@ -371,6 +377,15 @@ final class TaskRecord { setNextAffiliate(null); } + void removedFromRecents(TaskPersister persister) { + disposeThumbnail(); + closeRecentsChain(); + if (inRecents) { + inRecents = false; + persister.wakeup(this, false); + } + } + void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) { closeRecentsChain(); mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId; @@ -531,8 +546,9 @@ final class TaskRecord { isPersistable = r.isPersistable(); mCallingUid = r.launchedFromUid; mCallingPackage = r.launchedFromPackage; - // Clamp to [1, 100]. - maxRecents = Math.min(Math.max(r.info.maxRecents, 1), 100); + // Clamp to [1, max]. + maxRecents = Math.min(Math.max(r.info.maxRecents, 1), + ActivityManager.getMaxAppRecentsLimitStatic()); } else { // Otherwise make all added activities match this one. r.mActivityType = taskType; @@ -1062,8 +1078,10 @@ final class TaskRecord { pw.print(prefix); pw.print("realActivity="); pw.println(realActivity.flattenToShortString()); } - if (autoRemoveRecents || taskType != 0 || mTaskToReturnTo != 0 || numFullscreen != 0) { + if (autoRemoveRecents || isPersistable || taskType != 0 || mTaskToReturnTo != 0 + || numFullscreen != 0) { pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents); + pw.print(" isPersistable="); pw.print(isPersistable); pw.print(" numFullscreen="); pw.print(numFullscreen); pw.print(" taskType="); pw.print(taskType); pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo); @@ -1073,22 +1091,41 @@ final class TaskRecord { pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity); pw.print(" mReuseTask="); pw.println(mReuseTask); } + if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != -1 || mPrevAffiliate != null + || mNextAffiliateTaskId != -1 || mNextAffiliate != null) { + pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId); + pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId); + pw.print(" ("); + if (mPrevAffiliate == null) { + pw.print("null"); + } else { + pw.print(Integer.toHexString(System.identityHashCode(mPrevAffiliate))); + } + pw.print(") nextAffiliation="); pw.print(mNextAffiliateTaskId); + pw.print(" ("); + if (mNextAffiliate == null) { + pw.print("null"); + } else { + pw.print(Integer.toHexString(System.identityHashCode(mNextAffiliate))); + } + pw.println(")"); + } pw.print(prefix); pw.print("Activities="); pw.println(mActivities); - if (!askedCompatMode) { - pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); + if (!askedCompatMode || !inRecents || !isAvailable) { + pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode); + pw.print(" inRecents="); pw.print(inRecents); + pw.print(" isAvailable="); pw.println(isAvailable); } pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail); - pw.print(" lastThumbnailFile="); pw.print(mLastThumbnailFile); - pw.print(" lastDescription="); pw.println(lastDescription); + pw.print(" lastThumbnailFile="); pw.println(mLastThumbnailFile); + if (lastDescription != null) { + pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription); + } pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible); pw.print(" firstActiveTime="); pw.print(lastActiveTime); pw.print(" lastActiveTime="); pw.print(lastActiveTime); pw.print(" (inactive for "); pw.print((getInactiveDuration()/1000)); pw.println("s)"); - pw.print(prefix); pw.print("isPersistable="); pw.print(isPersistable); - pw.print(" affiliation="); pw.print(mAffiliatedTaskId); - pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId); - pw.print(" nextAffiliation="); pw.println(mNextAffiliateTaskId); } @Override |