diff --git a/services/backup/java/com/android/server/backup/AppsBackedUpOnThisDeviceJournal.java b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java similarity index 59% rename from services/backup/java/com/android/server/backup/AppsBackedUpOnThisDeviceJournal.java rename to services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java index c942bb27b2268..187d5d93bfddf 100644 --- a/services/backup/java/com/android/server/backup/AppsBackedUpOnThisDeviceJournal.java +++ b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java @@ -16,17 +16,17 @@ package com.android.server.backup; -import static com.android.server.backup.RefactoredBackupManagerService.DEBUG; - import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.server.backup.RefactoredBackupManagerService; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.HashSet; +import java.util.Set; /** * Records which apps have been backed up on this device, persisting it to disk so that it can be @@ -41,44 +41,62 @@ import java.util.HashSet; *

NB: this is always backed by the same files within the state directory supplied at * construction. */ -final class AppsBackedUpOnThisDeviceJournal { - private static final String TAG = "AppsBackedUpOnThisDeviceJournal"; +final class ProcessedPackagesJournal { + private static final String TAG = "ProcessedPackagesJournal"; private static final String JOURNAL_FILE_NAME = "processed"; + private static final boolean DEBUG = RefactoredBackupManagerService.DEBUG || false; - @GuardedBy("this") - private final HashSet mProcessedPackages = new HashSet<>(); + // using HashSet instead of ArraySet since we expect 100-500 elements range + @GuardedBy("mProcessedPackages") + private final Set mProcessedPackages = new HashSet<>(); + // TODO: at some point consider splitting the bookkeeping to be per-transport private final File mStateDirectory; /** - * Constructs a new journal, loading state from disk if it has been previously persisted. + * Constructs a new journal. + * + * After constructing the object one should call {@link #init()} to load state from disk if + * it has been previously persisted. * * @param stateDirectory The directory in which backup state (including journals) is stored. */ - AppsBackedUpOnThisDeviceJournal(File stateDirectory) { + ProcessedPackagesJournal(File stateDirectory) { mStateDirectory = stateDirectory; - loadFromDisk(); + } + + /** + * Loads state from disk if it has been previously persisted. + */ + void init() { + synchronized (mProcessedPackages) { + loadFromDisk(); + } } /** * Returns {@code true} if {@code packageName} has previously been backed up. */ - synchronized boolean hasBeenProcessed(String packageName) { - return mProcessedPackages.contains(packageName); + boolean hasBeenProcessed(String packageName) { + synchronized (mProcessedPackages) { + return mProcessedPackages.contains(packageName); + } } - synchronized void addPackage(String packageName) { - if (!mProcessedPackages.add(packageName)) { - // This package has already been processed - no need to add it to the journal. - return; - } + void addPackage(String packageName) { + synchronized (mProcessedPackages) { + if (!mProcessedPackages.add(packageName)) { + // This package has already been processed - no need to add it to the journal. + return; + } - File journalFile = new File(mStateDirectory, JOURNAL_FILE_NAME); + File journalFile = new File(mStateDirectory, JOURNAL_FILE_NAME); - try (RandomAccessFile out = new RandomAccessFile(journalFile, "rws")) { - out.seek(out.length()); - out.writeUTF(packageName); - } catch (IOException e) { - Slog.e(TAG, "Can't log backup of " + packageName + " to " + journalFile); + try (RandomAccessFile out = new RandomAccessFile(journalFile, "rws")) { + out.seek(out.length()); + out.writeUTF(packageName); + } catch (IOException e) { + Slog.e(TAG, "Can't log backup of " + packageName + " to " + journalFile); + } } } @@ -91,14 +109,18 @@ final class AppsBackedUpOnThisDeviceJournal { * * @return The current set of packages that have been backed up previously. */ - synchronized HashSet getPackagesCopy() { - return new HashSet<>(mProcessedPackages); + Set getPackagesCopy() { + synchronized (mProcessedPackages) { + return new HashSet<>(mProcessedPackages); + } } - synchronized void reset() { - mProcessedPackages.clear(); - File journalFile = new File(mStateDirectory, JOURNAL_FILE_NAME); - journalFile.delete(); + void reset() { + synchronized (mProcessedPackages) { + mProcessedPackages.clear(); + File journalFile = new File(mStateDirectory, JOURNAL_FILE_NAME); + journalFile.delete(); + } } private void loadFromDisk() { diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 39f7232f7facb..b01cfc572432e 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -629,7 +629,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // Keep a log of all the apps we've ever backed up, and what the dataset tokens are for both // the current backup dataset and the ancestral dataset. - private AppsBackedUpOnThisDeviceJournal mAppsBackedUpOnThisDeviceJournal; + private ProcessedPackagesJournal mProcessedPackagesJournal; private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes @@ -816,7 +816,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter Slog.w(TAG, "Unable to read token file", e); } - mAppsBackedUpOnThisDeviceJournal = new AppsBackedUpOnThisDeviceJournal(mBaseStateDir); + mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir); + mProcessedPackagesJournal.init(); synchronized (mQueueLock) { // Resume the full-data backup queue @@ -1068,7 +1069,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // so we must re-upload all saved settings. public void resetBackupState(File stateFileDir) { synchronized (mQueueLock) { - mAppsBackedUpOnThisDeviceJournal.reset(); + mProcessedPackagesJournal.reset(); mCurrentToken = 0; writeRestoreTokens(); @@ -1363,7 +1364,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter public void logBackupComplete(String packageName) { if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return; - mAppsBackedUpOnThisDeviceJournal.addPackage(packageName); + mProcessedPackagesJournal.addPackage(packageName); } // Persistently record the current and ancestral backup tokens as well @@ -1500,7 +1501,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter long token = mAncestralToken; synchronized (mQueueLock) { - if (mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(packageName)) { + if (mProcessedPackagesJournal.hasBeenProcessed(packageName)) { if (MORE_DEBUG) { Slog.i(TAG, "App in ever-stored, so using current token"); } @@ -3303,9 +3304,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } - HashSet processedApps = mAppsBackedUpOnThisDeviceJournal.getPackagesCopy(); - pw.println("Ever backed up: " + processedApps.size()); - for (String pkg : processedApps) { + Set processedPackages = mProcessedPackagesJournal.getPackagesCopy(); + pw.println("Ever backed up: " + processedPackages.size()); + for (String pkg : processedPackages) { pw.println(" " + pkg); } diff --git a/services/tests/servicestests/src/com/android/server/backup/AppsBackedUpOnThisDeviceJournalTest.java b/services/tests/servicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java similarity index 66% rename from services/tests/servicestests/src/com/android/server/backup/AppsBackedUpOnThisDeviceJournalTest.java rename to services/tests/servicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java index 093f92054a239..b4a1f1857d775 100644 --- a/services/tests/servicestests/src/com/android/server/backup/AppsBackedUpOnThisDeviceJournalTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java @@ -43,7 +43,7 @@ import java.util.Set; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class AppsBackedUpOnThisDeviceJournalTest { +public class ProcessedPackagesJournalTest { private static final String JOURNAL_FILE_NAME = "processed"; private static final String GOOGLE_PHOTOS = "com.google.photos"; @@ -53,21 +53,23 @@ public class AppsBackedUpOnThisDeviceJournalTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private File mStateDirectory; - private AppsBackedUpOnThisDeviceJournal mAppsBackedUpOnThisDeviceJournal; + private ProcessedPackagesJournal mProcessedPackagesJournal; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mStateDirectory = mTemporaryFolder.newFolder(); - mAppsBackedUpOnThisDeviceJournal = new AppsBackedUpOnThisDeviceJournal(mStateDirectory); + mProcessedPackagesJournal = new ProcessedPackagesJournal(mStateDirectory); + mProcessedPackagesJournal.init(); } @Test public void constructor_loadsAnyPreviousJournalFromDisk() throws Exception { writePermanentJournalPackages(Sets.newHashSet(GOOGLE_PHOTOS, GMAIL)); - AppsBackedUpOnThisDeviceJournal journalFromDisk = - new AppsBackedUpOnThisDeviceJournal(mStateDirectory); + ProcessedPackagesJournal journalFromDisk = + new ProcessedPackagesJournal(mStateDirectory); + journalFromDisk.init(); assertThat(journalFromDisk.hasBeenProcessed(GOOGLE_PHOTOS)).isTrue(); assertThat(journalFromDisk.hasBeenProcessed(GMAIL)).isTrue(); @@ -75,61 +77,61 @@ public class AppsBackedUpOnThisDeviceJournalTest { @Test public void hasBeenProcessed_isFalseForAnyPackageFromBlankInit() { - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isFalse(); - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GMAIL)).isFalse(); - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PLUS)).isFalse(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isFalse(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GMAIL)).isFalse(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GOOGLE_PLUS)).isFalse(); } @Test public void addPackage_addsPackageToObjectState() { - mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS); + mProcessedPackagesJournal.addPackage(GOOGLE_PHOTOS); - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isTrue(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isTrue(); } @Test public void addPackage_addsPackageToFileSystem() throws Exception { - mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS); + mProcessedPackagesJournal.addPackage(GOOGLE_PHOTOS); assertThat(readJournalPackages()).contains(GOOGLE_PHOTOS); } @Test public void getPackagesCopy_returnsTheCurrentState() throws Exception { - mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS); - mAppsBackedUpOnThisDeviceJournal.addPackage(GMAIL); + mProcessedPackagesJournal.addPackage(GOOGLE_PHOTOS); + mProcessedPackagesJournal.addPackage(GMAIL); - assertThat(mAppsBackedUpOnThisDeviceJournal.getPackagesCopy()) + assertThat(mProcessedPackagesJournal.getPackagesCopy()) .isEqualTo(Sets.newHashSet(GOOGLE_PHOTOS, GMAIL)); } @Test public void getPackagesCopy_returnsACopy() throws Exception { - mAppsBackedUpOnThisDeviceJournal.getPackagesCopy().add(GMAIL); + mProcessedPackagesJournal.getPackagesCopy().add(GMAIL); - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GMAIL)).isFalse(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GMAIL)).isFalse(); } @Test public void reset_removesAllPackagesFromObjectState() { - mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS); - mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PLUS); - mAppsBackedUpOnThisDeviceJournal.addPackage(GMAIL); + mProcessedPackagesJournal.addPackage(GOOGLE_PHOTOS); + mProcessedPackagesJournal.addPackage(GOOGLE_PLUS); + mProcessedPackagesJournal.addPackage(GMAIL); - mAppsBackedUpOnThisDeviceJournal.reset(); + mProcessedPackagesJournal.reset(); - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isFalse(); - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GMAIL)).isFalse(); - assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PLUS)).isFalse(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isFalse(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GMAIL)).isFalse(); + assertThat(mProcessedPackagesJournal.hasBeenProcessed(GOOGLE_PLUS)).isFalse(); } @Test public void reset_removesAllPackagesFromFileSystem() throws Exception { - mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS); - mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PLUS); - mAppsBackedUpOnThisDeviceJournal.addPackage(GMAIL); + mProcessedPackagesJournal.addPackage(GOOGLE_PHOTOS); + mProcessedPackagesJournal.addPackage(GOOGLE_PLUS); + mProcessedPackagesJournal.addPackage(GMAIL); - mAppsBackedUpOnThisDeviceJournal.reset(); + mProcessedPackagesJournal.reset(); assertThat(readJournalPackages()).isEmpty(); }