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
diff --git a/api/current.txt b/api/current.txt
index a09a853..a2c9f7e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -885,7 +885,7 @@
     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 @@
     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 @@
     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 @@
     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 f6883e2..90615d3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -929,7 +929,8 @@
 
     /**
      * 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
+     * <code>persistAcrossReboots</code>.
      *
      * @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 @@
 
     /**
      * 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
+     * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+     * came from the restored PersistableBundle first
      * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
      *
      * <p>This method is called between {@link #onStart} and
@@ -1111,7 +1113,8 @@
 
     /**
      * 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
+     * <code>persistAcrossReboots</code>.
      *
      * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState}
      * @param persistentState The data caming from the PersistableBundle first
@@ -1352,10 +1355,10 @@
 
     /**
      * 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
+     * <code>persistAcrossReboots</code>. 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 ea46044..00cdbd0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -317,7 +317,7 @@
         }
 
         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 6e53a6fb..3dfa78b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7375,6 +7375,7 @@
             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 791e5aa..abc8cde 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -104,6 +104,28 @@
     public int documentLaunchMode;
 
     /**
+     * Constant corresponding to <code>persistRootOnly</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_ROOT_ONLY = 0;
+    /**
+     * Constant corresponding to <code>doNotPersist</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int DO_NOT_PERSIST = 1;
+    /**
+     * Constant corresponding to <code>persistAcrossReboots</code> 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}.
      */
@@ -230,12 +252,6 @@
      */
     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.
      * {@link android.R.attr#autoRemoveFromRecents}
@@ -641,13 +657,23 @@
         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 @@
         dest.writeInt(softInputMode);
         dest.writeInt(uiOptions);
         dest.writeString(parentActivityName);
+        dest.writeInt(persistableMode);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -713,5 +740,6 @@
         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 4cac7fd..ffb870a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2524,10 +2524,9 @@
                     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 814d8fc..a8735cb 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -873,17 +873,33 @@
     <!-- The name of the logical parent of the activity as it appears in the manifest. -->
     <attr name="parentActivityName" format="string" />
 
-    <!-- Define an activity that will persist across reboots. If such an activity is in the
-         Recents list when the device is shut off it will appear in the Recents list when
-         the device is next powered on. To be persisted all activities in the task from the
-         root activity up to the last activity before a <em>break</em> must be declared with
-         the persistable attribute. A <em>break</em> is the first activity after the root
-         started with Intent.FLAG_CLEAR_TASK_WHEN_RESET.
+    <!-- Define how an activity persist across reboots. Activities defined as "never" will not
+         be persisted. Those defined as "always" will be persisted. Those defined as "taskOnly"
+         will persist the root activity of the task only. See below for more detail as to
+         what gets persisted. -->
+    <attr name="persistableMode">
+        <!-- The default. If this activity forms the root of a task then that task will be
+             persisted across reboots but only the launching intent will be used. All
+             activities above this activity in the task will not be persisted. In addition
+             this activity will not be passed a PersistableBundle into which it could have
+             stored its state. -->
+        <enum name="persistRootOnly" value="0" />
+        <!-- If this activity forms the root of a task then that task will not be persisted
+             across reboots -->
+        <enum name="doNotPersist" value="1" />
+        <!-- If this activity forms the root of a task then the task and this activity will
+             be persisted across reboots. If the activity above this activity is also
+             tagged with the attribute <code>"persist"</code> then it will be persisted as well.
+             And so on up the task stack until either an activity without the
+             <code>persistableMode="persistAcrossReboots"</code> attribute or one that was launched
+             with the flag Intent.FLAG_CLEAR_TASK_WHEN_RESET is encountered.
 
-         <p>Activities that are declared with the persistable attribute will be provided with a
-         forced-persistable Bundle in onCreate() and onSavedInstanceState(), and must only
-         be passed a persistable Bundle in their Intent.extras. -->
-    <attr name="persistable" format="boolean" />
+             <p>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. -->
+        <enum name="persistAcrossReboots" value="2" />
+    </attr>
 
     <!-- This attribute specifies that an activity shall become the root activity of a
          new task each time it is launched. Using this attribute permits the user to
@@ -1623,7 +1639,7 @@
         <!-- @hide This broacast receiver will only receive broadcasts for the
              primary user.  Can only be used with receivers. -->
         <attr name="primaryUserOnly" format="boolean" />
-        <attr name="persistable" />
+        <attr name="persistableMode" />
         <attr name="allowEmbedded" />
         <attr name="documentLaunchMode" />
         <attr name="maxRecents" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 202c127..1433ab0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2136,7 +2136,7 @@
   <public type="attr" name="colorControlActivated" />
   <public type="attr" name="colorButtonNormal" />
   <public type="attr" name="colorControlHighlight" />
-  <public type="attr" name="persistable" />
+  <public type="attr" name="persistableMode" />
   <public type="attr" name="titleTextAppearance" />
   <public type="attr" name="subtitleTextAppearance" />
   <public type="attr" name="slideEdge" />
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 11f855e..42f224f 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 @@
     }
 
     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 bb289fa..c79b33d 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 @@
     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 @@
     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 @@
 
         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 @@
                                     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 @@
                     }
                 }
                 if (!DEBUG && deleteFile) {
+                    if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
                     taskFile.delete();
                 }
             }
@@ -209,7 +213,7 @@
         Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
             @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 @@
                 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 @@
                 synchronized(mService) {
                     final ArrayList<TaskRecord> 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 @@
                                     task.needsPersisting = false;
                                 }
                             }
+                        } else {
+                            if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task);
                         }
                     }
                 }
@@ -330,6 +338,8 @@
                     // 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 81a0b36..1cde41f 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 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;
             }