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
This commit is contained in:
Craig Mautner
2014-06-16 17:18:52 -07:00
parent 5d140e4b1b
commit 43e52ed32e
11 changed files with 108 additions and 44 deletions

View File

@ -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;

View File

@ -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
* <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 @@ 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
* <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 @@ 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
* <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 @@ 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
* <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.

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -103,6 +103,28 @@ public class ActivityInfo extends ComponentInfo
*/
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}.
@ -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<ActivityInfo> CREATOR
@ -713,5 +740,6 @@ public class ActivityInfo extends ComponentInfo
softInputMode = source.readInt();
uiOptions = source.readInt();
parentActivityName = source.readString();
persistableMode = source.readInt();
}
}

View File

@ -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,

View File

@ -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" />

View File

@ -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" />

View File

@ -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() {

View File

@ -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<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 @@ 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<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 @@ 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);
}

View File

@ -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;
}