summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWale Ogunwale <ogunwale@google.com>2014-12-05 21:41:03 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2014-12-05 21:41:03 +0000
commitaf3361bb264d9d037f0bd10532a752bbaeaec39c (patch)
tree5943c4dab0569f6012bd8506c327cbbac86160c7
parentc67d89588c17fc94b7b9088eb60f311b5dbf31ac (diff)
parent9a3f6ac63c9c11008b6dbb99d5213b3d3f51fca0 (diff)
downloadframeworks_base-af3361bb264d9d037f0bd10532a752bbaeaec39c.zip
frameworks_base-af3361bb264d9d037f0bd10532a752bbaeaec39c.tar.gz
frameworks_base-af3361bb264d9d037f0bd10532a752bbaeaec39c.tar.bz2
am 9a3f6ac6: am 6b54d38a: Merge "Add support from restoring recent\'s backup." into lmp-mr1-dev
* commit '9a3f6ac63c9c11008b6dbb99d5213b3d3f51fca0': Add support from restoring recent's backup.
-rw-r--r--core/java/android/app/ActivityManagerNative.java18
-rw-r--r--core/java/android/app/IActivityManager.java3
-rw-r--r--core/java/com/android/server/backup/SystemBackupAgent.java10
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityManagerService.java30
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java26
-rw-r--r--services/core/java/com/android/server/am/TaskPersister.java581
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java52
7 files changed, 633 insertions, 87 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 06a26ec..e8d08b8 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2363,6 +2363,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
+
+ case SYSTEM_BACKUP_RESTORED: {
+ data.enforceInterface(IActivityManager.descriptor);
+ systemBackupRestored();
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -5458,5 +5465,16 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ @Override
+ public void systemBackupRestored() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(SYSTEM_BACKUP_RESTORED, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 1ccbd27..e505d69 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -469,6 +469,8 @@ public interface IActivityManager extends IInterface {
public void notifyLaunchTaskBehindComplete(IBinder token) throws RemoteException;
public void notifyEnterAnimationComplete(IBinder token) throws RemoteException;
+ public void systemBackupRestored() throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -790,4 +792,5 @@ public interface IActivityManager extends IInterface {
int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240;
int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241;
int REGISTER_TASK_STACK_LISTENER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+242;
+ int SYSTEM_BACKUP_RESTORED = IBinder.FIRST_CALL_TRANSACTION+243;
}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index ed7ce63..35a1a5a 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -17,6 +17,7 @@
package com.android.server.backup;
+import android.app.ActivityManagerNative;
import android.app.IWallpaperManager;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
@@ -186,4 +187,13 @@ public class SystemBackupAgent extends BackupAgentHelper {
}
}
}
+
+ @Override
+ public void onRestoreFinished() {
+ try {
+ ActivityManagerNative.getDefault().systemBackupRestored();
+ } catch (RemoteException e) {
+ // Not possible since this code is running in the system process.
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e2e657..bde19d9 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@ import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import android.Manifest;
import android.app.AppOpsManager;
@@ -3907,7 +3908,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ endIndex + " " + cur);
if (cur == top) {
// Verify start of the chain.
- if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != -1) {
+ if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
Slog.wtf(TAG, "Bad chain @" + endIndex
+ ": first task has next affiliate: " + prev);
sane = false;
@@ -3926,7 +3927,7 @@ public final class ActivityManagerService extends ActivityManagerNative
break;
}
}
- if (cur.mPrevAffiliateTaskId == -1) {
+ if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
// Chain ends here.
if (cur.mPrevAffiliate != null) {
Slog.wtf(TAG, "Bad chain @" + endIndex
@@ -3991,7 +3992,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final void addRecentTaskLocked(TaskRecord task) {
final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
- || task.mNextAffiliateTaskId != -1 || task.mPrevAffiliateTaskId != -1;
+ || task.mNextAffiliateTaskId != INVALID_TASK_ID
+ || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
int N = mRecentTasks.size();
// Quick case: check if the top-most recent task is the same.
@@ -6222,6 +6224,17 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
+ public void systemBackupRestored() {
+ synchronized (this) {
+ if (mSystemReady) {
+ mTaskPersister.restoreTasksFromOtherDeviceLocked();
+ } else {
+ Slog.w(TAG, "System backup restored before system is ready");
+ }
+ }
+ }
+
final void ensureBootCompleted() {
boolean booting;
boolean enableScreen;
@@ -8036,7 +8049,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Compose the recent task info
ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
- rti.id = tr.getTopActivity() == null ? -1 : tr.taskId;
+ rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
rti.persistentId = tr.taskId;
rti.baseIntent = new Intent(tr.getBaseIntent());
rti.origActivity = tr.origActivity;
@@ -8259,7 +8272,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (trimIdx >= 0) {
// If this would have caused a trim, then we'll abort because that
// means it would be added at the end of the list but then just removed.
- return -1;
+ return INVALID_TASK_ID;
}
final int N = mRecentTasks.size();
@@ -11147,6 +11160,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mRecentTasks == null) {
mRecentTasks = mTaskPersister.restoreTasksLocked();
+ mTaskPersister.restoreTasksFromOtherDeviceLocked();
if (!mRecentTasks.isEmpty()) {
mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
}
@@ -15793,6 +15807,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
} else {
removeTasksByRemovedPackageComponentsLocked(ssp, userId);
+ if (userId == UserHandle.USER_OWNER) {
+ mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
+ }
}
}
break;
@@ -15810,6 +15827,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (replacing) {
removeTasksByRemovedPackageComponentsLocked(ssp, userId);
}
+ if (userId == UserHandle.USER_OWNER) {
+ mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
+ }
}
break;
case Intent.ACTION_TIMEZONE_CHANGED:
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index e37d5f3..912ca62 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,6 +16,10 @@
package com.android.server.am;
+import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
+import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+
import android.app.ActivityManager.TaskDescription;
import android.os.PersistableBundle;
import android.os.Trace;
@@ -1052,12 +1056,12 @@ final class ActivityRecord {
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
final ActivityRecord r = ActivityRecord.forToken(token);
if (r == null) {
- return -1;
+ return INVALID_TASK_ID;
}
final TaskRecord task = r.task;
final int activityNdx = task.mActivities.indexOf(r);
if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
- return -1;
+ return INVALID_TASK_ID;
}
return task.taskId;
}
@@ -1139,7 +1143,7 @@ final class ActivityRecord {
}
}
- static ActivityRecord restoreFromXml(XmlPullParser in, int taskId,
+ static ActivityRecord restoreFromXml(XmlPullParser in,
ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
Intent intent = null;
PersistableBundle persistentState = null;
@@ -1155,8 +1159,8 @@ final class ActivityRecord {
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
final String attrValue = in.getAttributeValue(attrNdx);
- if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: attribute name=" +
- attrName + " value=" + attrValue);
+ if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
+ "ActivityRecord: attribute name=" + attrName + " value=" + attrValue);
if (ATTR_ID.equals(attrName)) {
createTime = Long.valueOf(attrValue);
} else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
@@ -1181,15 +1185,15 @@ final class ActivityRecord {
(event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
if (event == XmlPullParser.START_TAG) {
final String name = in.getName();
- if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
- "ActivityRecord: START_TAG name=" + name);
+ if (DEBUG_PERSISTER || DEBUG_RESTORER)
+ Slog.d(TaskPersister.TAG, "ActivityRecord: START_TAG name=" + name);
if (TAG_INTENT.equals(name)) {
intent = Intent.restoreFromXml(in);
- if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
- "ActivityRecord: intent=" + intent);
+ if (DEBUG_PERSISTER || DEBUG_RESTORER)
+ Slog.d(TaskPersister.TAG, "ActivityRecord: intent=" + intent);
} else if (TAG_PERSISTABLEBUNDLE.equals(name)) {
persistentState = PersistableBundle.restoreFromXml(in);
- if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+ if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
"ActivityRecord: persistentState=" + persistentState);
} else {
Slog.w(TAG, "restoreActivity: unexpected name=" + name);
@@ -1232,7 +1236,7 @@ final class ActivityRecord {
@Override
public String toString() {
if (stringName != null) {
- return stringName + " t" + (task == null ? -1 : task.taskId) +
+ return stringName + " t" + (task == null ? INVALID_TASK_ID : task.taskId) +
(finishing ? " f}" : "}");
}
StringBuilder sb = new StringBuilder(128);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 9311f25..629a05d 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,16 +16,27 @@
package com.android.server.am;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.IPackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
+import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.Xml;
+
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -38,11 +49,18 @@ import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
+
+import libcore.io.IoUtils;
+
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
public class TaskPersister {
static final String TAG = "TaskPersister";
- static final boolean DEBUG = false;
+ static final boolean DEBUG_PERSISTER = false;
+ static final boolean DEBUG_RESTORER = false;
/** When not flushing don't write out files faster than this */
private static final long INTER_WRITE_DELAY_MS = 500;
@@ -67,12 +85,17 @@ public class TaskPersister {
// contains subdirs named after TASKS_DIRNAME and IMAGES_DIRNAME mirroring the
// ancestral device's dataset. This needs to match the RECENTS_TASK_RESTORE_DIR
// value in RecentsBackupHelper.
- private static final String RESTORED_TASKS = "restored_" + TASKS_DIRNAME;
+ private static final String RESTORED_TASKS_DIRNAME = "restored_" + TASKS_DIRNAME;
+
+ // Max time to wait for the application/package of a restored task to be installed
+ // before giving up.
+ private static final long MAX_INSTALL_WAIT_TIME = DateUtils.DAY_IN_MILLIS;
private static final String TAG_TASK = "task";
static File sImagesDir;
static File sTasksDir;
+ static File sRestoredTasksDir;
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
@@ -105,10 +128,20 @@ public class TaskPersister {
ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
+ // Map of tasks that were backed-up on a different device that can be restored on this device.
+ // Data organization: <packageNameOfAffiliateTask, listOfAffiliatedTasksChains>
+ private ArrayMap<String, List<List<OtherDeviceTask>>> mOtherDeviceTasksMap =
+ new ArrayMap<>(10);
+
+ // The next time in milliseconds we will remove expired task from
+ // {@link #mOtherDeviceTasksMap} and disk. Set to {@link Long.MAX_VALUE} to never clean-up
+ // tasks.
+ private long mExpiredTasksCleanupTime = Long.MAX_VALUE;
+
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
sTasksDir = new File(systemDir, TASKS_DIRNAME);
if (!sTasksDir.exists()) {
- if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
if (!sTasksDir.mkdir()) {
Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
}
@@ -116,12 +149,14 @@ public class TaskPersister {
sImagesDir = new File(systemDir, IMAGES_DIRNAME);
if (!sImagesDir.exists()) {
- if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "Creating images directory " + sTasksDir);
if (!sImagesDir.mkdir()) {
Slog.e(TAG, "Failure creating images directory " + sImagesDir);
}
}
+ sRestoredTasksDir = new File(systemDir, RESTORED_TASKS_DIRNAME);
+
mStackSupervisor = stackSupervisor;
mService = stackSupervisor.mService;
@@ -138,8 +173,8 @@ public class TaskPersister {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem &&
((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
- if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
- " from write queue");
+ if (DEBUG_PERSISTER) Slog.d(TAG, "Removing "
+ + ((ImageWriteQueueItem) item).mFilename + " from write queue");
mWriteQueue.remove(queueNdx);
}
}
@@ -184,9 +219,9 @@ public class TaskPersister {
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
- if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
- + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
- + " Callers=" + Debug.getCallers(4));
+ if (DEBUG_PERSISTER) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush
+ + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="
+ + mWriteQueue.size() + " Callers=" + Debug.getCallers(4));
notifyAll();
}
@@ -228,7 +263,7 @@ public class TaskPersister {
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
- if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
+ if (DEBUG_PERSISTER) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
SystemClock.uptimeMillis() + " mNextWriteTime=" +
mNextWriteTime + " Callers=" + Debug.getCallers(4));
notifyAll();
@@ -262,12 +297,12 @@ public class TaskPersister {
}
private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
- if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "saveToXml: task=" + task);
final XmlSerializer xmlSerializer = new FastXmlSerializer();
StringWriter stringWriter = new StringWriter();
xmlSerializer.setOutput(stringWriter);
- if (DEBUG) xmlSerializer.setFeature(
+ if (DEBUG_PERSISTER) xmlSerializer.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
// save task
@@ -326,7 +361,7 @@ public class TaskPersister {
for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
File taskFile = recentFiles[taskNdx];
- if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
+ if (DEBUG_PERSISTER) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
BufferedReader reader = null;
boolean deleteFile = false;
try {
@@ -339,11 +374,12 @@ public class TaskPersister {
event != XmlPullParser.END_TAG) {
final String name = in.getName();
if (event == XmlPullParser.START_TAG) {
- if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
+ if (DEBUG_PERSISTER)
+ Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
if (TAG_TASK.equals(name)) {
final TaskRecord task =
TaskRecord.restoreFromXml(in, mStackSupervisor);
- if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
+ if (DEBUG_PERSISTER) Slog.d(TAG, "restoreTasksLocked: restored task=" +
task);
if (task != null) {
task.isPersistable = true;
@@ -371,20 +407,16 @@ public class TaskPersister {
Slog.e(TAG, "Failing file: " + fileToString(taskFile));
deleteFile = true;
} finally {
- if (reader != null) {
- try {
- reader.close();
- } catch (IOException e) {
- }
- }
- if (!DEBUG && deleteFile) {
- if (true || DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
+ IoUtils.closeQuietly(reader);
+ if (!DEBUG_PERSISTER && deleteFile) {
+ if (true || DEBUG_PERSISTER)
+ Slog.d(TAG, "Deleting file=" + taskFile.getName());
taskFile.delete();
}
}
}
- if (!DEBUG) {
+ if (!DEBUG_PERSISTER) {
removeObsoleteFiles(recoveredTaskIds);
}
@@ -415,8 +447,8 @@ public class TaskPersister {
}
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
- " files=" + files);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds="
+ + persistentTaskIds + " files=" + files);
if (files == null) {
Slog.e(TAG, "File error accessing recents directory (too many files open?).");
return;
@@ -429,14 +461,14 @@ public class TaskPersister {
final int taskId;
try {
taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
} catch (Exception e) {
Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
file.delete();
continue;
}
if (!persistentTaskIds.contains(taskId)) {
- if (true || DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" +
+ if (true || DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: deleting file=" +
file.getName());
file.delete();
}
@@ -450,10 +482,363 @@ public class TaskPersister {
}
static Bitmap restoreImage(String filename) {
- if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "restoreImage: restoring " + filename);
return BitmapFactory.decodeFile(sImagesDir + File.separator + filename);
}
+ /**
+ * Tries to restore task that were backed-up on a different device onto this device.
+ */
+ void restoreTasksFromOtherDeviceLocked() {
+ readOtherDeviceTasksFromDisk();
+ addOtherDeviceTasksToRecentsLocked();
+ }
+
+ /**
+ * Read the tasks that were backed-up on a different device and can be restored to this device
+ * from disk and populated {@link #mOtherDeviceTasksMap} with the information. Also sets up
+ * time to clear out other device tasks that have not been restored on this device
+ * within the allotted time.
+ */
+ private void readOtherDeviceTasksFromDisk() {
+ synchronized (mOtherDeviceTasksMap) {
+ // Clear out current map and expiration time.
+ mOtherDeviceTasksMap.clear();
+ mExpiredTasksCleanupTime = Long.MAX_VALUE;
+
+ final File[] taskFiles;
+ if (!sRestoredTasksDir.exists()
+ || (taskFiles = sRestoredTasksDir.listFiles()) == null) {
+ // Nothing to do if there are no tasks to restore.
+ return;
+ }
+
+ long earliestMtime = System.currentTimeMillis();
+ SparseArray<List<OtherDeviceTask>> tasksByAffiliateIds =
+ new SparseArray<>(taskFiles.length);
+
+ // Read new tasks from disk
+ for (int i = 0; i < taskFiles.length; ++i) {
+ final File taskFile = taskFiles[i];
+ if (DEBUG_RESTORER) Slog.d(TAG, "readOtherDeviceTasksFromDisk: taskFile="
+ + taskFile.getName());
+
+ final OtherDeviceTask task = OtherDeviceTask.createFromFile(taskFile);
+
+ if (task == null) {
+ // Go ahead and remove the file on disk if we are unable to create a task from
+ // it.
+ if (DEBUG_RESTORER) Slog.e(TAG, "Unable to create task for file="
+ + taskFile.getName() + "...deleting file.");
+ taskFile.delete();
+ continue;
+ }
+
+ List<OtherDeviceTask> tasks = tasksByAffiliateIds.get(task.mAffiliatedTaskId);
+ if (tasks == null) {
+ tasks = new ArrayList<>();
+ tasksByAffiliateIds.put(task.mAffiliatedTaskId, tasks);
+ }
+ tasks.add(task);
+ final long taskMtime = taskFile.lastModified();
+ if (earliestMtime > taskMtime) {
+ earliestMtime = taskMtime;
+ }
+ }
+
+ if (tasksByAffiliateIds.size() > 0) {
+ // Sort each affiliated tasks chain by taskId which is the order they were created
+ // that should always be correct...Then add to task map.
+ for (int i = 0; i < tasksByAffiliateIds.size(); i++) {
+ List<OtherDeviceTask> chain = tasksByAffiliateIds.valueAt(i);
+ Collections.sort(chain);
+ // Package name of the root task in the affiliate chain.
+ final String packageName =
+ chain.get(chain.size()-1).mComponentName.getPackageName();
+ List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.get(packageName);
+ if (chains == null) {
+ chains = new ArrayList<>();
+ mOtherDeviceTasksMap.put(packageName, chains);
+ }
+ chains.add(chain);
+ }
+
+ // Set expiration time.
+ mExpiredTasksCleanupTime = earliestMtime + MAX_INSTALL_WAIT_TIME;
+ if (DEBUG_RESTORER) Slog.d(TAG, "Set Expiration time to "
+ + DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime,
+ DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
+ }
+ }
+ }
+
+ /**
+ * Removed any expired tasks from {@link #mOtherDeviceTasksMap} and disk if their expiration
+ * time is less than or equal to {@link #mExpiredTasksCleanupTime}.
+ */
+ private void removeExpiredTasksIfNeeded() {
+ synchronized (mOtherDeviceTasksMap) {
+ final long now = System.currentTimeMillis();
+ if (mOtherDeviceTasksMap.isEmpty() || now < mExpiredTasksCleanupTime) {
+ return;
+ }
+
+ long earliestNonExpiredMtime = now;
+ mExpiredTasksCleanupTime = Long.MAX_VALUE;
+
+ // Remove expired backed-up tasks that have not been restored. We only want to
+ // remove task if it is safe to remove all tasks in the affiliation chain.
+ for (int i = mOtherDeviceTasksMap.size() - 1; i >= 0 ; i--) {
+
+ List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.valueAt(i);
+ for (int j = chains.size() - 1; j >= 0 ; j--) {
+
+ List<OtherDeviceTask> chain = chains.get(j);
+ boolean removeChain = true;
+ for (int k = chain.size() - 1; k >= 0 ; k--) {
+ OtherDeviceTask task = chain.get(k);
+ final long taskLastModified = task.mFile.lastModified();
+ if ((taskLastModified + MAX_INSTALL_WAIT_TIME) > now) {
+ // File has not expired yet...but we keep looping to get the earliest
+ // mtime.
+ if (earliestNonExpiredMtime > taskLastModified) {
+ earliestNonExpiredMtime = taskLastModified;
+ }
+ removeChain = false;
+ }
+ }
+ if (removeChain) {
+ for (int k = chain.size() - 1; k >= 0; k--) {
+ final File file = chain.get(k).mFile;
+ if (DEBUG_RESTORER) Slog.d(TAG, "Deleting expired file="
+ + file.getName() + " mapped to not installed component="
+ + chain.get(k).mComponentName);
+ file.delete();
+ }
+ chains.remove(j);
+ }
+ }
+ if (chains.isEmpty()) {
+ final String packageName = mOtherDeviceTasksMap.keyAt(i);
+ mOtherDeviceTasksMap.removeAt(i);
+ if (DEBUG_RESTORER) Slog.d(TAG, "Removed package=" + packageName
+ + " from task map");
+ }
+ }
+
+ // Reset expiration time if there is any task remaining.
+ if (!mOtherDeviceTasksMap.isEmpty()) {
+ mExpiredTasksCleanupTime = earliestNonExpiredMtime + MAX_INSTALL_WAIT_TIME;
+ if (DEBUG_RESTORER) Slog.d(TAG, "Reset expiration time to "
+ + DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime,
+ DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
+ }
+ }
+ }
+
+ /**
+ * Tries to add all backed-up tasks from another device to this device recent's list.
+ */
+ private void addOtherDeviceTasksToRecentsLocked() {
+ synchronized (mOtherDeviceTasksMap) {
+ for (int i = mOtherDeviceTasksMap.size() - 1; i >= 0; i--) {
+ addOtherDeviceTasksToRecentsLocked(mOtherDeviceTasksMap.keyAt(i));
+ }
+ }
+ }
+
+ /**
+ * Tries to add backed-up tasks that are associated with the input package from
+ * another device to this device recent's list.
+ */
+ void addOtherDeviceTasksToRecentsLocked(String packageName) {
+ synchronized (mOtherDeviceTasksMap) {
+ List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.get(packageName);
+ if (chains == null) {
+ return;
+ }
+
+ for (int i = chains.size() - 1; i >= 0; i--) {
+ List<OtherDeviceTask> chain = chains.get(i);
+ if (!canAddOtherDeviceTaskChain(chain)) {
+ if (DEBUG_RESTORER) Slog.d(TAG, "Can't add task chain at index=" + i
+ + " for package=" + packageName);
+ continue;
+ }
+
+ // Generate task records for this chain.
+ List<TaskRecord> tasks = new ArrayList<>();
+ TaskRecord prev = null;
+ for (int j = chain.size() - 1; j >= 0; j--) {
+ TaskRecord task = createTaskRecordLocked(chain.get(j));
+ if (task == null) {
+ // There was a problem in creating one of this task records in this chain.
+ // There is no way we can continue...
+ if (DEBUG_RESTORER) Slog.d(TAG, "Can't create task record for file="
+ + chain.get(j).mFile + " for package=" + packageName);
+ break;
+ }
+
+ // Wire-up affiliation chain.
+ if (prev == null) {
+ task.mPrevAffiliate = null;
+ task.mPrevAffiliateTaskId = INVALID_TASK_ID;
+ task.mAffiliatedTaskId = task.taskId;
+ } else {
+ prev.mNextAffiliate = task;
+ prev.mNextAffiliateTaskId = task.taskId;
+ task.mAffiliatedTaskId = prev.mAffiliatedTaskId;
+ task.mPrevAffiliate = prev;
+ task.mPrevAffiliateTaskId = prev.taskId;
+ }
+ prev = task;
+ tasks.add(0, task);
+ }
+
+ // Add tasks to recent's if we were able to create task records for all the tasks
+ // in the chain.
+ if (tasks.size() == chain.size()) {
+ // Make sure there is space in recent's to add the new task. If there is space
+ // to the to the back.
+ // TODO: Would be more fancy to interleave the new tasks into recent's based on
+ // {@link TaskRecord.mLastTimeMoved} and drop the oldest recent's vs. just
+ // adding to the back of the list.
+ int spaceLeft =
+ ActivityManager.getMaxRecentTasksStatic()
+ - mService.mRecentTasks.size();
+ if (spaceLeft >= tasks.size()) {
+ mService.mRecentTasks.addAll(mService.mRecentTasks.size(), tasks);
+ for (int k = tasks.size() - 1; k >= 0; k--) {
+ // Persist new tasks.
+ wakeup(tasks.get(k), false);
+ }
+
+ if (DEBUG_RESTORER) Slog.d(TAG, "Added " + tasks.size()
+ + " tasks to recent's for" + " package=" + packageName);
+ } else {
+ if (DEBUG_RESTORER) Slog.d(TAG, "Didn't add to recents. tasks.size("
+ + tasks.size() + ") != chain.size(" + chain.size()
+ + ") for package=" + packageName);
+ }
+ } else {
+ if (DEBUG_RESTORER) Slog.v(TAG, "Unable to add restored tasks to recents "
+ + tasks.size() + " tasks for package=" + packageName);
+ }
+
+ // Clean-up structures
+ for (int j = chain.size() - 1; j >= 0; j--) {
+ chain.get(j).mFile.delete();
+ }
+ chains.remove(i);
+ if (chains.isEmpty()) {
+ // The fate of all backed-up tasks associated with this package has been
+ // determine. Go ahead and remove it from the to-process list.
+ mOtherDeviceTasksMap.remove(packageName);
+ if (DEBUG_RESTORER)
+ Slog.d(TAG, "Removed package=" + packageName + " from restore map");
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates and returns {@link TaskRecord} for the task from another device that can be used on
+ * this device. Returns null if the operation failed.
+ */
+ private TaskRecord createTaskRecordLocked(OtherDeviceTask other) {
+ File file = other.mFile;
+ BufferedReader reader = null;
+ TaskRecord task = null;
+ if (DEBUG_RESTORER) Slog.d(TAG, "createTaskRecordLocked: file=" + file.getName());
+
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(reader);
+
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
+ && event != XmlPullParser.END_TAG) {
+ final String name = in.getName();
+ if (event == XmlPullParser.START_TAG) {
+
+ if (TAG_TASK.equals(name)) {
+ // Create a task record using a task id that is valid for this device.
+ task = TaskRecord.restoreFromXml(
+ in, mStackSupervisor, mStackSupervisor.getNextTaskId());
+ if (DEBUG_RESTORER)
+ Slog.d(TAG, "createTaskRecordLocked: restored task=" + task);
+
+ if (task != null) {
+ task.isPersistable = true;
+ task.inRecents = true;
+ // Task can/should only be backed-up/restored for device owner.
+ task.userId = UserHandle.USER_OWNER;
+ // Clear out affiliated ids that are no longer valid on this device.
+ task.mAffiliatedTaskId = INVALID_TASK_ID;
+ task.mPrevAffiliateTaskId = INVALID_TASK_ID;
+ task.mNextAffiliateTaskId = INVALID_TASK_ID;
+ } else {
+ Slog.e(TAG, "Unable to create task for backed-up file=" + file + ": "
+ + fileToString(file));
+ }
+ } else {
+ Slog.wtf(TAG, "createTaskRecordLocked Unknown xml event=" + event
+ + " name=" + name);
+ }
+ }
+ XmlUtils.skipCurrentTag(in);
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Unable to parse " + file + ". Error ", e);
+ Slog.e(TAG, "Failing file: " + fileToString(file));
+ } finally {
+ IoUtils.closeQuietly(reader);
+ }
+
+ return task;
+ }
+
+ /**
+ * Returns true if the input task chain backed-up from another device can be restored on this
+ * device.
+ */
+ private boolean canAddOtherDeviceTaskChain(List<OtherDeviceTask> chain) {
+
+ // Get component names of all the tasks in the chain.
+ // Mainly doing this to reduce checking for a component twice if two or more
+ // affiliations belong to the same component which is highly likely.
+ ArraySet<ComponentName> componentsToCheck = new ArraySet<>();
+ for (int i = 0; i < chain.size(); i++) {
+
+ OtherDeviceTask task = chain.get(i);
+ // Quick check, we can't add the task chain if any of its task files don't exist.
+ if (!task.mFile.exists()) {
+ if (DEBUG_RESTORER)
+ Slog.d(TAG, "Can't add chain due to missing file=" + task.mFile);
+ return false;
+ }
+ componentsToCheck.add(task.mComponentName);
+ }
+
+ boolean canAdd = true;
+ try {
+ // Check to see if all the components for this task chain are installed.
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ for (int i = 0; canAdd && i < componentsToCheck.size(); i++) {
+ ComponentName cn = componentsToCheck.valueAt(i);
+ canAdd &= pm.getActivityInfo(cn, 0, UserHandle.USER_OWNER) != null;
+ if (DEBUG_RESTORER) Slog.d(TAG, "ComponentName=" + cn + " installed=" + canAdd);
+ }
+ } catch (RemoteException e) {
+ // Should not happen???
+ canAdd = false;
+ }
+
+ if (DEBUG_RESTORER) Slog.d(TAG, "canAdd=" + canAdd);
+ return canAdd;
+ }
+
private class LazyTaskWriterThread extends Thread {
LazyTaskWriterThread(String name) {
@@ -472,21 +857,22 @@ public class TaskPersister {
probablyDone = mWriteQueue.isEmpty();
}
if (probablyDone) {
- if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
+ if (DEBUG_PERSISTER) Slog.d(TAG, "Looking for obsolete files.");
persistentTaskIds.clear();
synchronized (mService) {
final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
- if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "mRecents=" + tasks);
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
+ if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: task=" + task +
" persistable=" + task.isPersistable);
if ((task.isPersistable || task.inRecents)
- && !task.stack.isHomeStack()) {
- if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
+ && (task.stack == null || !task.stack.isHomeStack())) {
+ if (DEBUG_PERSISTER)
+ Slog.d(TAG, "adding to persistentTaskIds task=" + task);
persistentTaskIds.add(task.taskId);
} else {
- if (DEBUG) Slog.d(TAG,
+ if (DEBUG_PERSISTER) Slog.d(TAG,
"omitting from persistentTaskIds task=" + task);
}
}
@@ -500,7 +886,7 @@ public class TaskPersister {
if (mNextWriteTime != FLUSH_QUEUE) {
// The next write we don't have to wait so long.
mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
- if (DEBUG) Slog.d(TAG, "Next write time may be in " +
+ if (DEBUG_PERSISTER) Slog.d(TAG, "Next write time may be in " +
INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
}
@@ -510,8 +896,13 @@ public class TaskPersister {
mNextWriteTime = 0; // idle.
TaskPersister.this.notifyAll(); // wake up flush() if needed.
}
+
+ // See if we need to remove any expired back-up tasks before waiting.
+ removeExpiredTasksIfNeeded();
+
try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+ if (DEBUG_PERSISTER)
+ Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
TaskPersister.this.wait();
} catch (InterruptedException e) {
}
@@ -521,11 +912,12 @@ public class TaskPersister {
item = mWriteQueue.remove(0);
long now = SystemClock.uptimeMillis();
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
- mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
+ if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: now=" + now
+ + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="
+ + mWriteQueue.size());
while (now < mNextWriteTime) {
try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
+ if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: waiting " +
(mNextWriteTime - now));
TaskPersister.this.wait(mNextWriteTime - now);
} catch (InterruptedException e) {
@@ -540,7 +932,7 @@ public class TaskPersister {
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
final String filename = imageWriteQueueItem.mFilename;
final Bitmap bitmap = imageWriteQueueItem.mImage;
- if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "writing bitmap: filename=" + filename);
FileOutputStream imageFile = null;
try {
imageFile = new FileOutputStream(new File(sImagesDir, filename));
@@ -548,23 +940,18 @@ public class TaskPersister {
} catch (Exception e) {
Slog.e(TAG, "saveImage: unable to save " + filename, e);
} finally {
- if (imageFile != null) {
- try {
- imageFile.close();
- } catch (IOException e) {
- }
- }
+ IoUtils.closeQuietly(imageFile);
}
} else if (item instanceof TaskWriteQueueItem) {
// Write out one task.
StringWriter stringWriter = null;
TaskRecord task = ((TaskWriteQueueItem) item).mTask;
- if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "Writing task=" + task);
synchronized (mService) {
if (task.inRecents) {
// Still there.
try {
- if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+ if (DEBUG_PERSISTER) Slog.d(TAG, "Saving task=" + task);
stringWriter = saveToXml(task);
} catch (IOException e) {
} catch (XmlPullParserException e) {
@@ -594,4 +981,100 @@ public class TaskPersister {
}
}
}
+
+ /**
+ * Helper class for holding essential information about task that were backed-up on a different
+ * device that can be restored on this device.
+ */
+ private static class OtherDeviceTask implements Comparable<OtherDeviceTask> {
+ final File mFile;
+ // See {@link TaskRecord} for information on the fields below.
+ final ComponentName mComponentName;
+ final int mTaskId;
+ final int mAffiliatedTaskId;
+
+ private OtherDeviceTask(
+ File file, ComponentName componentName, int taskId, int affiliatedTaskId) {
+ mFile = file;
+ mComponentName = componentName;
+ mTaskId = taskId;
+ mAffiliatedTaskId = (affiliatedTaskId == INVALID_TASK_ID) ? taskId: affiliatedTaskId;
+ }
+
+ @Override
+ public int compareTo(OtherDeviceTask another) {
+ return mTaskId - another.mTaskId;
+ }
+
+ /**
+ * Creates a new {@link OtherDeviceTask} object based on the contents of the input file.
+ *
+ * @param file input file that contains the complete task information.
+ * @return new {@link OtherDeviceTask} object or null if we failed to create the object.
+ */
+ static OtherDeviceTask createFromFile(File file) {
+ if (file == null || !file.exists()) {
+ if (DEBUG_RESTORER)
+ Slog.d(TAG, "createFromFile: file=" + file + " doesn't exist.");
+ return null;
+ }
+
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(reader);
+
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ event != XmlPullParser.START_TAG) {
+ // Skip to the start tag or end of document
+ }
+
+ if (event == XmlPullParser.START_TAG) {
+ final String name = in.getName();
+
+ if (TAG_TASK.equals(name)) {
+ ComponentName componentName = null;
+ int taskId = INVALID_TASK_ID;
+ int taskAffiliation = INVALID_TASK_ID;
+ for (int j = in.getAttributeCount() - 1; j >= 0; --j) {
+ final String attrName = in.getAttributeName(j);
+ final String attrValue = in.getAttributeValue(j);
+ if (TaskRecord.ATTR_REALACTIVITY.equals(attrName)) {
+ componentName = ComponentName.unflattenFromString(attrValue);
+ } else if (TaskRecord.ATTR_TASKID.equals(attrName)) {
+ taskId = Integer.valueOf(attrValue);
+ } else if (TaskRecord.ATTR_TASK_AFFILIATION.equals(attrName)) {
+ taskAffiliation = Integer.valueOf(attrValue);
+ }
+ }
+ if (componentName == null || taskId == INVALID_TASK_ID) {
+ if (DEBUG_RESTORER) Slog.e(TAG,
+ "createFromFile: FAILED componentName=" + componentName
+ + " taskId=" + taskId + " file=" + file);
+ return null;
+ }
+ if (DEBUG_RESTORER) Slog.d(TAG, "creating OtherDeviceTask from file="
+ + file.getName() + " componentName=" + componentName
+ + " taskId=" + taskId);
+ return new OtherDeviceTask(file, componentName, taskId, taskAffiliation);
+ } else {
+ Slog.wtf(TAG,
+ "createFromFile: Unknown xml event=" + event + " name=" + name);
+ }
+ } else {
+ Slog.wtf(TAG, "createFromFile: Unable to find start tag in file=" + file);
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.wtf(TAG, "Unable to parse " + file + ". Error ", e);
+ } finally {
+ IoUtils.closeQuietly(reader);
+ }
+
+ // Something went wrong...
+ return null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d726685..c3eda71 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -21,6 +21,8 @@ import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
+import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
+import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
import android.app.Activity;
import android.app.ActivityManager;
@@ -52,10 +54,10 @@ import java.io.PrintWriter;
import java.util.ArrayList;
final class TaskRecord {
- private static final String ATTR_TASKID = "task_id";
+ static final String ATTR_TASKID = "task_id";
private static final String TAG_INTENT = "intent";
private static final String TAG_AFFINITYINTENT = "affinity_intent";
- private static final String ATTR_REALACTIVITY = "real_activity";
+ static final String ATTR_REALACTIVITY = "real_activity";
private static final String ATTR_ORIGACTIVITY = "orig_activity";
private static final String TAG_ACTIVITY = "activity";
private static final String ATTR_AFFINITY = "affinity";
@@ -71,7 +73,7 @@ final class TaskRecord {
private static final String ATTR_LASTDESCRIPTION = "last_description";
private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
- private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
+ static final String ATTR_TASK_AFFILIATION = "task_affiliation";
private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
@@ -82,6 +84,8 @@ final class TaskRecord {
static final boolean IGNORE_RETURN_TO_RECENTS = true;
+ static final int INVALID_TASK_ID = -1;
+
final int taskId; // Unique identifier for this task.
String affinity; // The affinity name for this task, or null; may change identity.
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
@@ -151,9 +155,9 @@ final class TaskRecord {
int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
int mAffiliatedTaskColor; // color of the parent task affiliation.
TaskRecord mPrevAffiliate; // previous task in affiliated chain.
- int mPrevAffiliateTaskId = -1; // previous id for persistence.
+ int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
TaskRecord mNextAffiliate; // next task in affiliated chain.
- int mNextAffiliateTaskId = -1; // next id for persistence.
+ int mNextAffiliateTaskId = INVALID_TASK_ID; // next id for persistence.
// For relaunching the task from recents as though it was launched by the original launcher.
int mCallingUid;
@@ -363,12 +367,12 @@ final class TaskRecord {
void setPrevAffiliate(TaskRecord prevAffiliate) {
mPrevAffiliate = prevAffiliate;
- mPrevAffiliateTaskId = prevAffiliate == null ? -1 : prevAffiliate.taskId;
+ mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
}
void setNextAffiliate(TaskRecord nextAffiliate) {
mNextAffiliate = nextAffiliate;
- mNextAffiliateTaskId = nextAffiliate == null ? -1 : nextAffiliate.taskId;
+ mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
}
// Close up recents linked list.
@@ -875,6 +879,10 @@ final class TaskRecord {
static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
throws IOException, XmlPullParserException {
+ return restoreFromXml(in, stackSupervisor, INVALID_TASK_ID);
+ }
+ static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor,
+ int inTaskId) throws IOException, XmlPullParserException {
Intent intent = null;
Intent affinityIntent = null;
ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
@@ -894,23 +902,23 @@ final class TaskRecord {
long lastActiveTime = -1;
long lastTimeOnTop = 0;
boolean neverRelinquishIdentity = true;
- int taskId = -1;
+ int taskId = inTaskId;
final int outerDepth = in.getDepth();
TaskDescription taskDescription = new TaskDescription();
- int taskAffiliation = -1;
+ int taskAffiliation = INVALID_TASK_ID;
int taskAffiliationColor = 0;
- int prevTaskId = -1;
- int nextTaskId = -1;
+ int prevTaskId = INVALID_TASK_ID;
+ int nextTaskId = INVALID_TASK_ID;
int callingUid = -1;
String callingPackage = "";
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
final String attrValue = in.getAttributeValue(attrNdx);
- if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
- attrName + " value=" + attrValue);
+ if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
+ "TaskRecord: attribute name=" + attrName + " value=" + attrValue);
if (ATTR_TASKID.equals(attrName)) {
- taskId = Integer.valueOf(attrValue);
+ if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue);
} else if (ATTR_REALACTIVITY.equals(attrName)) {
realActivity = ComponentName.unflattenFromString(attrValue);
} else if (ATTR_ORIGACTIVITY.equals(attrName)) {
@@ -966,17 +974,16 @@ final class TaskRecord {
(event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
if (event == XmlPullParser.START_TAG) {
final String name = in.getName();
- if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
- name);
+ if (DEBUG_PERSISTER || DEBUG_RESTORER)
+ Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" + name);
if (TAG_AFFINITYINTENT.equals(name)) {
affinityIntent = Intent.restoreFromXml(in);
} else if (TAG_INTENT.equals(name)) {
intent = Intent.restoreFromXml(in);
} else if (TAG_ACTIVITY.equals(name)) {
- ActivityRecord activity =
- ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
- if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
- activity);
+ ActivityRecord activity = ActivityRecord.restoreFromXml(in, stackSupervisor);
+ if (DEBUG_PERSISTER || DEBUG_RESTORER)
+ Slog.d(TaskPersister.TAG, "TaskRecord: activity=" + activity);
if (activity != null) {
activities.add(activity);
}
@@ -1082,8 +1089,9 @@ 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) {
+ if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != INVALID_TASK_ID
+ || mPrevAffiliate != null || mNextAffiliateTaskId != INVALID_TASK_ID
+ || mNextAffiliate != null) {
pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
pw.print(" (");