diff options
author | Wale Ogunwale <ogunwale@google.com> | 2014-12-05 21:41:03 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-12-05 21:41:03 +0000 |
commit | af3361bb264d9d037f0bd10532a752bbaeaec39c (patch) | |
tree | 5943c4dab0569f6012bd8506c327cbbac86160c7 | |
parent | c67d89588c17fc94b7b9088eb60f311b5dbf31ac (diff) | |
parent | 9a3f6ac63c9c11008b6dbb99d5213b3d3f51fca0 (diff) | |
download | frameworks_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.
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(" ("); |