From 62d1e1ef7e04f03c492b49e721ee9773677bba8b Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 20 May 2015 14:49:38 -0700 Subject: [PATCH] Scan at boot time to detect newly-present full backup candidates OTA or similar might have caused an app to appear without the usual notifications being sent. Make sure we pick up those apps as appropriate for full-data backup. Bug 19462310 Change-Id: Ic17bc72b14cc7599ae8ea540548fda932606b8f2 --- .../server/backup/BackupManagerService.java | 165 ++++++++++++--------- 1 file changed, 99 insertions(+), 66 deletions(-) diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 01cc2ca704e7..ff8fb835617e 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -88,13 +88,13 @@ import android.util.Slog; import android.util.SparseArray; import android.util.StringBuilderPrinter; +import com.android.internal.annotations.GuardedBy; import com.android.internal.backup.IBackupTransport; import com.android.internal.backup.IObbBackupService; import com.android.server.AppWidgetBackupBridge; import com.android.server.EventLogTags; import com.android.server.SystemService; import com.android.server.backup.PackageManagerBackupAgent.Metadata; -import com.android.server.pm.PackageManagerService; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -589,8 +589,12 @@ public class BackupManagerService { File mFullBackupScheduleFile; // If we're running a schedule-driven full backup, this is the task instance doing it - PerformFullTransportBackupTask mRunningFullBackupTask; // inside mQueueLock - ArrayList mFullBackupQueue; // inside mQueueLock + + @GuardedBy("mQueueLock") + PerformFullTransportBackupTask mRunningFullBackupTask; + + @GuardedBy("mQueueLock") + ArrayList mFullBackupQueue; // Utility: build a new random integer token int generateToken() { @@ -1229,8 +1233,10 @@ public class BackupManagerService { } } - // Resume the full-data backup queue - mFullBackupQueue = readFullBackupSchedule(); + synchronized (mQueueLock) { + // Resume the full-data backup queue + mFullBackupQueue = readFullBackupSchedule(); + } // Register for broadcasts about package install, etc., so we can // update the provider list. @@ -1248,74 +1254,98 @@ public class BackupManagerService { } private ArrayList readFullBackupSchedule() { + boolean changed = false; ArrayList schedule = null; - synchronized (mQueueLock) { - if (mFullBackupScheduleFile.exists()) { - FileInputStream fstream = null; - BufferedInputStream bufStream = null; - DataInputStream in = null; - try { - fstream = new FileInputStream(mFullBackupScheduleFile); - bufStream = new BufferedInputStream(fstream); - in = new DataInputStream(bufStream); + List apps = + PackageManagerBackupAgent.getStorableApplications(mPackageManager); - int version = in.readInt(); - if (version != SCHEDULE_FILE_VERSION) { - Slog.e(TAG, "Unknown backup schedule version " + version); - return null; - } + if (mFullBackupScheduleFile.exists()) { + FileInputStream fstream = null; + BufferedInputStream bufStream = null; + DataInputStream in = null; + try { + fstream = new FileInputStream(mFullBackupScheduleFile); + bufStream = new BufferedInputStream(fstream); + in = new DataInputStream(bufStream); - int N = in.readInt(); - schedule = new ArrayList(N); - for (int i = 0; i < N; i++) { - String pkgName = in.readUTF(); - long lastBackup = in.readLong(); - try { - PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0); - if (appGetsFullBackup(pkg) - && appIsEligibleForBackup(pkg.applicationInfo)) { - schedule.add(new FullBackupEntry(pkgName, lastBackup)); - } else { - if (DEBUG) { - Slog.i(TAG, "Package " + pkgName - + " no longer eligible for full backup"); - } - } - } catch (NameNotFoundException e) { + int version = in.readInt(); + if (version != SCHEDULE_FILE_VERSION) { + Slog.e(TAG, "Unknown backup schedule version " + version); + return null; + } + + final int N = in.readInt(); + schedule = new ArrayList(N); + + // HashSet instead of ArraySet specifically because we want the eventual + // lookups against O(hundreds) of entries to be as fast as possible, and + // we discard the set immediately after the scan so the extra memory + // overhead is transient. + HashSet foundApps = new HashSet(N); + + for (int i = 0; i < N; i++) { + String pkgName = in.readUTF(); + long lastBackup = in.readLong(); + foundApps.add(pkgName); // all apps that we've addressed already + try { + PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0); + if (appGetsFullBackup(pkg) && appIsEligibleForBackup(pkg.applicationInfo)) { + schedule.add(new FullBackupEntry(pkgName, lastBackup)); + } else { if (DEBUG) { Slog.i(TAG, "Package " + pkgName - + " not installed; dropping from full backup"); + + " no longer eligible for full backup"); } } + } catch (NameNotFoundException e) { + if (DEBUG) { + Slog.i(TAG, "Package " + pkgName + + " not installed; dropping from full backup"); + } } - Collections.sort(schedule); - } catch (Exception e) { - Slog.e(TAG, "Unable to read backup schedule", e); - mFullBackupScheduleFile.delete(); - schedule = null; - } finally { - IoUtils.closeQuietly(in); - IoUtils.closeQuietly(bufStream); - IoUtils.closeQuietly(fstream); } - } - if (schedule == null) { - // no prior queue record, or unable to read it. Set up the queue - // from scratch. - List apps = - PackageManagerBackupAgent.getStorableApplications(mPackageManager); - final int N = apps.size(); - schedule = new ArrayList(N); - for (int i = 0; i < N; i++) { - PackageInfo info = apps.get(i); - if (appGetsFullBackup(info) && appIsEligibleForBackup(info.applicationInfo)) { - schedule.add(new FullBackupEntry(info.packageName, 0)); + // New apps can arrive "out of band" via OTA and similar, so we also need to + // scan to make sure that we're tracking all full-backup candidates properly + for (PackageInfo app : apps) { + if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) { + if (!foundApps.contains(app.packageName)) { + if (DEBUG) { + Slog.i(TAG, "New full backup app " + app.packageName + " found"); + } + schedule.add(new FullBackupEntry(app.packageName, 0)); + changed = true; + } } } - writeFullBackupScheduleAsync(); + + Collections.sort(schedule); + } catch (Exception e) { + Slog.e(TAG, "Unable to read backup schedule", e); + mFullBackupScheduleFile.delete(); + schedule = null; + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(bufStream); + IoUtils.closeQuietly(fstream); + } + } + + if (schedule == null) { + // no prior queue record, or unable to read it. Set up the queue + // from scratch. + changed = true; + schedule = new ArrayList(apps.size()); + for (PackageInfo info : apps) { + if (appGetsFullBackup(info) && appIsEligibleForBackup(info.applicationInfo)) { + schedule.add(new FullBackupEntry(info.packageName, 0)); + } } } + + if (changed) { + writeFullBackupScheduleAsync(); + } return schedule; } @@ -4313,13 +4343,16 @@ public class BackupManagerService { // This is also slow but easy for modest numbers of apps: work backwards // from the end of the queue until we find an item whose last backup - // time was before this one, then insert this new entry after it. - int which; - for (which = mFullBackupQueue.size() - 1; which >= 0; which--) { - final FullBackupEntry entry = mFullBackupQueue.get(which); - if (entry.lastBackup <= lastBackedUp) { - mFullBackupQueue.add(which + 1, newEntry); - break; + // time was before this one, then insert this new entry after it. If we're + // adding something new we don't bother scanning, and just prepend. + int which = -1; + if (lastBackedUp > 0) { + for (which = mFullBackupQueue.size() - 1; which >= 0; which--) { + final FullBackupEntry entry = mFullBackupQueue.get(which); + if (entry.lastBackup <= lastBackedUp) { + mFullBackupQueue.add(which + 1, newEntry); + break; + } } } if (which < 0) { -- 2.11.0