From 43e52ed32e2d55ef4aee18c4b4bc13b7fdef9cc4 Mon Sep 17 00:00:00 2001 From: Craig Mautner Date: Mon, 16 Jun 2014 17:18:52 -0700 Subject: [PATCH] Fix persistent tasks and expand scope - Fixed missing tag closure on the xml for storing Intent categories. - Shortened timeout for flushing tasks to persistent storage from one minute to ten seconds. - Made persistency the default except for those tasks on the home stack and those tasks that exclude themselves from the recent task list. - Fixed deletion of tasks after restoring. Tasks now survive a second reboot, not just the first reboot. - Fixed sort order so most recent tasks will be restored at front. Fixes bug 15672002. Change-Id: I16d87d58c6fd2e879cfd0c0b18b2694432a79b71 --- api/current.txt | 7 +++- core/java/android/app/Activity.java | 19 +++++---- core/java/android/app/ActivityThread.java | 2 +- core/java/android/content/Intent.java | 1 + .../java/android/content/pm/ActivityInfo.java | 42 +++++++++++++++---- .../android/content/pm/PackageParser.java | 7 ++-- core/res/res/values/attrs_manifest.xml | 38 ++++++++++++----- core/res/res/values/public.xml | 2 +- .../com/android/server/am/ActivityRecord.java | 5 ++- .../com/android/server/am/TaskPersister.java | 24 +++++++---- .../com/android/server/am/TaskRecord.java | 5 ++- 11 files changed, 108 insertions(+), 44 deletions(-) diff --git a/api/current.txt b/api/current.txt index a09a853a4419f..a2c9f7e67ac4c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -885,7 +885,7 @@ package android { field public static final int permissionFlags = 16843719; // 0x10103c7 field public static final int permissionGroup = 16842762; // 0x101000a field public static final int permissionGroupFlags = 16843717; // 0x10103c5 - field public static final int persistable = 16843823; // 0x101042f + field public static final int persistableMode = 16843823; // 0x101042f field public static final int persistent = 16842765; // 0x101000d field public static final int persistentDrawingCache = 16842990; // 0x10100ee field public static final deprecated int phoneNumber = 16843111; // 0x1010167 @@ -8041,6 +8041,7 @@ package android.content.pm { field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1 field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3 field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0 + field public static final int DO_NOT_PERSIST = 1; // 0x1 field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40 field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8 field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 @@ -8052,13 +8053,14 @@ package android.content.pm { field public static final int FLAG_IMMERSIVE = 2048; // 0x800 field public static final int FLAG_MULTIPROCESS = 1; // 0x1 field public static final int FLAG_NO_HISTORY = 128; // 0x80 - field public static final int FLAG_PERSISTABLE = 4096; // 0x1000 field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000 field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10 field public static final int LAUNCH_MULTIPLE = 0; // 0x0 field public static final int LAUNCH_SINGLE_INSTANCE = 3; // 0x3 field public static final int LAUNCH_SINGLE_TASK = 2; // 0x2 field public static final int LAUNCH_SINGLE_TOP = 1; // 0x1 + field public static final int PERSIST_ACROSS_REBOOTS = 2; // 0x2 + field public static final int PERSIST_ROOT_ONLY = 0; // 0x0 field public static final int SCREEN_ORIENTATION_BEHIND = 3; // 0x3 field public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10; // 0xa field public static final int SCREEN_ORIENTATION_FULL_USER = 13; // 0xd @@ -8083,6 +8085,7 @@ package android.content.pm { field public int maxRecents; field public java.lang.String parentActivityName; field public java.lang.String permission; + field public int persistableMode; field public int screenOrientation; field public int softInputMode; field public java.lang.String targetActivity; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f6883e2f53394..90615d3649f1f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -929,7 +929,8 @@ public class Activity extends ContextThemeWrapper /** * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with - * the attribute {@link android.R.attr#persistable} set true. + * the attribute {@link android.R.attr#persistableMode} set to + * persistAcrossReboots. * * @param savedInstanceState if the activity is being re-initialized after * previously being shut down then this Bundle contains the data it most @@ -1012,8 +1013,9 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. The {@link - * android.os.PersistableBundle} passed came from the restored PersistableBundle first + * created with the attribute {@link android.R.attr#persistableMode} set to + * persistAcrossReboots. The {@link android.os.PersistableBundle} passed + * came from the restored PersistableBundle first * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}. * *

This method is called between {@link #onStart} and @@ -1111,7 +1113,8 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onPostCreate(Bundle)} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. + * created with the attribute {@link android.R.attr#persistableMode} set to + * persistAcrossReboots. * * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState} * @param persistentState The data caming from the PersistableBundle first @@ -1352,10 +1355,10 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onSaveInstanceState} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. The {@link - * android.os.PersistableBundle} passed in will be saved and presented in - * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity - * is restarted following the next device reboot. + * created with the attribute {@link android.R.attr#persistableMode} set to + * persistAcrossReboots. The {@link android.os.PersistableBundle} passed + * in will be saved and presented in {@link #onCreate(Bundle, PersistableBundle)} + * the first time that this activity is restarted following the next device reboot. * * @param outState Bundle in which to place your saved state. * @param outPersistentState State which will be saved across reboots. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ea4604466a6b7..00cdbd08816d2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -317,7 +317,7 @@ public final class ActivityThread { } public boolean isPersistable() { - return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0; + return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS; } public String toString() { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 6e53a6fb59692..3dfa78b4804cd 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7375,6 +7375,7 @@ public class Intent implements Parcelable, Cloneable { for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) { out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx)); } + out.endTag(null, TAG_CATEGORIES); } } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 791e5aa46c7f6..abc8cde2c9ebd 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -103,6 +103,28 @@ public class ActivityInfo extends ComponentInfo */ public int documentLaunchMode; + /** + * Constant corresponding to persistRootOnly in + * the {@link android.R.attr#persistableMode} attribute. + */ + public static final int PERSIST_ROOT_ONLY = 0; + /** + * Constant corresponding to doNotPersist in + * the {@link android.R.attr#persistableMode} attribute. + */ + public static final int DO_NOT_PERSIST = 1; + /** + * Constant corresponding to persistAcrossReboots in + * the {@link android.R.attr#persistableMode} attribute. + */ + public static final int PERSIST_ACROSS_REBOOTS = 2; + /** + * Value indicating how this activity is to be persisted across + * reboots for restoring in the Recents list. + * {@link android.R.attr#persistableMode} + */ + public int persistableMode; + /** * The maximum number of tasks rooted at this activity that can be in the recent task list. * Refer to {@link android.R.attr#maxRecents}. @@ -229,12 +251,6 @@ public class ActivityInfo extends ComponentInfo * @see android.app.Activity#setImmersive(boolean) */ public static final int FLAG_IMMERSIVE = 0x0800; - /** - * Bit in {@link #flags} indicating that this activity is to be persisted across - * reboots for display in the Recents list. - * {@link android.R.attr#persistable} - */ - public static final int FLAG_PERSISTABLE = 0x1000; /** * Bit in {@link #flags} indicating that tasks started with this activity are to be * removed from the recent list of tasks when the last activity in the task is finished. @@ -641,13 +657,23 @@ public class ActivityInfo extends ComponentInfo return theme != 0 ? theme : applicationInfo.theme; } + private String persistableModeToString() { + switch(persistableMode) { + case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY"; + case DO_NOT_PERSIST: return "DO_NOT_PERSIST"; + case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS"; + default: return "UNKNOWN=" + persistableMode; + } + } + public void dump(Printer pw, String prefix) { super.dumpFront(pw, prefix); if (permission != null) { pw.println(prefix + "permission=" + permission); } pw.println(prefix + "taskAffinity=" + taskAffinity - + " targetActivity=" + targetActivity); + + " targetActivity=" + targetActivity + + " persistableMode=" + persistableModeToString()); if (launchMode != 0 || flags != 0 || theme != 0) { pw.println(prefix + "launchMode=" + launchMode + " flags=0x" + Integer.toHexString(flags) @@ -688,6 +714,7 @@ public class ActivityInfo extends ComponentInfo dest.writeInt(softInputMode); dest.writeInt(uiOptions); dest.writeString(parentActivityName); + dest.writeInt(persistableMode); } public static final Parcelable.Creator CREATOR @@ -713,5 +740,6 @@ public class ActivityInfo extends ComponentInfo softInputMode = source.readInt(); uiOptions = source.readInt(); parentActivityName = source.readString(); + persistableMode = source.readInt(); } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 4cac7fdaaeccb..ffb870aac687d 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2524,10 +2524,9 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode, 0); - if (sa.getBoolean( - com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) { - a.info.flags |= ActivityInfo.FLAG_PERSISTABLE; - } + a.info.persistableMode = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestActivity_persistableMode, + ActivityInfo.PERSIST_ROOT_ONLY); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded, diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 814d8fc0c8000..a8735cb21f5a8 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -873,17 +873,33 @@ - + + + + + + - +

Activities that are declared with the persistAcrossReboots attribute will be + provided with a PersistableBundle in onSavedInstanceState(), These activities may + use this PeristableBundle to save their state. Then, following a reboot, that + PersistableBundle will be provided back to the activity in its onCreate() method. --> + + - + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 202c1271ec204..1433ab000a22e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2136,7 +2136,7 @@ - + diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 11f855e2eb462..42f224ffb8170 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -568,7 +568,10 @@ final class ActivityRecord { } boolean isPersistable() { - return (info.flags & ActivityInfo.FLAG_PERSISTABLE) != 0; + return (info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || + info.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS) && + (intent == null || + (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0); } void makeFinishing() { diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index bb289fab3f587..c79b33d1f3015 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -45,7 +45,7 @@ public class TaskPersister { static final boolean DEBUG = false; /** When in slow mode don't write tasks out faster than this */ - private static final long INTER_TASK_DELAY_MS = 60000; + private static final long INTER_TASK_DELAY_MS = 10000; private static final long DEBUG_INTER_TASK_DELAY_MS = 5000; private static final String RECENTS_FILENAME = "_task"; @@ -69,6 +69,7 @@ public class TaskPersister { TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) { sTasksDir = new File(systemDir, TASKS_DIRNAME); if (!sTasksDir.exists()) { + if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir); if (!sTasksDir.mkdir()) { Slog.e(TAG, "Failure creating tasks directory " + sTasksDir); } @@ -76,6 +77,7 @@ public class TaskPersister { sImagesDir = new File(systemDir, IMAGES_DIRNAME); if (!sImagesDir.exists()) { + if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir); if (!sImagesDir.mkdir()) { Slog.e(TAG, "Failure creating images directory " + sImagesDir); } @@ -172,14 +174,15 @@ public class TaskPersister { TaskRecord.restoreFromXml(in, mStackSupervisor); if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task); if (task != null) { + task.isPersistable = true; tasks.add(task); final int taskId = task.taskId; recoveredTaskIds.add(taskId); mStackSupervisor.setNextTaskId(taskId); } } else { - Slog.e(TAG, "restoreTasksLocked Unknown xml event=" + event + " name=" - + name); + Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event + + " name=" + name); } } XmlUtils.skipCurrentTag(in); @@ -195,6 +198,7 @@ public class TaskPersister { } } if (!DEBUG && deleteFile) { + if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName()); taskFile.delete(); } } @@ -209,7 +213,7 @@ public class TaskPersister { Arrays.sort(tasksArray, new Comparator() { @Override public int compare(TaskRecord lhs, TaskRecord rhs) { - final long diff = lhs.mLastTimeMoved - rhs.mLastTimeMoved; + final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved; if (diff < 0) { return -1; } else if (diff > 0) { @@ -233,8 +237,7 @@ public class TaskPersister { try { taskId = Integer.valueOf(filename.substring(0, taskIdEnd)); } catch (Exception e) { - if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Can't parse file=" + - file.getName()); + Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName()); file.delete(); continue; } @@ -288,15 +291,18 @@ public class TaskPersister { synchronized(mService) { final ArrayList tasks = mService.mRecentTasks; persistentTaskIds.clear(); + if (DEBUG) Slog.d(TAG, "mRecents=" + tasks); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { task = tasks.get(taskNdx); if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" + task.isPersistable + " needsPersisting=" + task.needsPersisting); - if (task.isPersistable) { + if (task.isPersistable && !task.stack.isHomeStack()) { + if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task); persistentTaskIds.add(task.taskId); if (task.needsPersisting) { try { + if (DEBUG) Slog.d(TAG, "Saving task=" + task); stringWriter = saveToXml(task); break; } catch (IOException e) { @@ -305,6 +311,8 @@ public class TaskPersister { task.needsPersisting = false; } } + } else { + if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task); } } } @@ -330,6 +338,8 @@ public class TaskPersister { // Made it through the entire list and didn't find anything new that needed // persisting. if (!DEBUG) { + if (DEBUG) Slog.d(TAG, "Calling removeObsoleteFiles persistentTaskIds=" + + persistentTaskIds); removeObsoleteFiles(persistentTaskIds); } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 81a0b363cac1d..1cde41fb92ad6 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -652,8 +652,9 @@ final class TaskRecord extends ThumbnailHolder { final int numActivities = activities.size(); for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { final ActivityRecord r = activities.get(activityNdx); - if (!r.isPersistable() || (activityNdx > 0 && - (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0)) { + if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() || + ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) && + activityNdx > 0) { // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET). break; }