Loading core/tests/coretests/src/android/app/backup/BackupAgentTest.java +39 −26 Original line number Diff line number Diff line Loading @@ -62,8 +62,7 @@ public class BackupAgentTest { @Mock FullBackup.BackupScheme mBackupScheme; @Mock Context mContext; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Before public void setUp() { Loading @@ -72,9 +71,9 @@ public class BackupAgentTest { @Test public void testGetIncludeExcludeRules_isNotMigration_returnsRules() throws Exception { PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags */ 0); Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test", Collections.singleton(path)); PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags= */ 0); Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test", Collections.singleton(path)); ArraySet<PathWithRequiredFlags> excludePaths = new ArraySet<>(); excludePaths.add(path); IncludeExcludeRules expectedRules = new IncludeExcludeRules(includePaths, excludePaths); Loading @@ -99,8 +98,8 @@ public class BackupAgentTest { BackupAgent agent = new TestFullBackupAgent(); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo( OperationType.BACKUP); assertThat(agent.getBackupRestoreEventLogger().getOperationType()) .isEqualTo(OperationType.BACKUP); } @Test Loading @@ -108,8 +107,8 @@ public class BackupAgentTest { BackupAgent agent = new TestFullBackupAgent(); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.RESTORE); assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo( OperationType.RESTORE); assertThat(agent.getBackupRestoreEventLogger().getOperationType()) .isEqualTo(OperationType.RESTORE); } @Test Loading Loading @@ -145,8 +144,8 @@ public class BackupAgentTest { agent.clearBackupRestoreEventLogger(); agent.onFullBackup(new FullBackupDataOutput(/* quota= */ 0)); assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get( 0).getSuccessCount()).isEqualTo(1); assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get(0).getSuccessCount()) .isEqualTo(1); } @Test Loading @@ -158,13 +157,19 @@ public class BackupAgentTest { IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createPipe(); FileOutputStream writeSide = new FileOutputStream( pipes[1].getFileDescriptor()); FileOutputStream writeSide = new FileOutputStream(pipes[1].getFileDescriptor()); writeSide.write("Hello".getBytes(StandardCharsets.UTF_8)); agentBinder.doRestoreFile(pipes[0], /* length= */ 5, BackupAgent.TYPE_FILE, FullBackup.FILES_TREE_TOKEN, /* path= */ "hello_file", /* mode= */ 0666, /* mtime= */ 12345, /* token= */ 6789, mIBackupManager); agentBinder.doRestoreFile( pipes[0], /* length= */ 5, BackupAgent.TYPE_FILE, FullBackup.FILES_TREE_TOKEN, /* path= */ "hello_file", /* mode= */ 0666, /* mtime= */ 12345, /* token= */ 6789, mIBackupManager); try (FileInputStream in = new FileInputStream(pipes[0].getFileDescriptor())) { assertThat(in.available()).isEqualTo(0); Loading @@ -182,8 +187,9 @@ public class BackupAgentTest { private static class TestFullBackupAgent extends BackupAgent { @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { public void onBackup( ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Left empty as this is a full backup agent. } Loading @@ -193,8 +199,9 @@ public class BackupAgentTest { } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { public void onRestore( BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Left empty as this is a full backup agent. } } Loading @@ -202,8 +209,14 @@ public class BackupAgentTest { private static class TestRestoreIgnoringFullBackupAgent extends TestFullBackupAgent { @Override protected void onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime) protected void onRestoreFile( ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime) throws IOException { // Ignore the file and don't consume any data. } Loading packages/LocalTransport/src/com/android/localtransport/LocalTransport.java +137 −95 Original line number Diff line number Diff line Loading @@ -61,25 +61,22 @@ import java.util.Collections; import java.util.List; /** * Backup transport for stashing stuff into a known location on disk, and * later restoring from there. For testing only. * Backup transport for stashing stuff into a known location on disk, and later restoring from * there. For testing only. * * <p>Note: the quickest way to build and sync this class is: * {@code m LocalTransport && adb install $OUT/system/priv-app/LocalTransport/LocalTransport.apk} * <p>Note: the quickest way to build and sync this class is: {@code m LocalTransport && adb install * $OUT/system/priv-app/LocalTransport/LocalTransport.apk} */ public class LocalTransport extends BackupTransport { static final String TAG = "LocalTransport"; static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE); private static final String TRANSPORT_DIR_NAME = "com.android.localtransport.LocalTransport"; private static final String TRANSPORT_DIR_NAME = "com.android.localtransport.LocalTransport"; private static final String TRANSPORT_DESTINATION_STRING = "Backing up to debug-only private cache"; private static final String TRANSPORT_DESTINATION_STRING = "Backing up to debug-only private cache"; private static final String TRANSPORT_DATA_MANAGEMENT_LABEL = ""; private static final String TRANSPORT_DATA_MANAGEMENT_LABEL = ""; private static final String INCREMENTAL_DIR = "_delta"; private static final String FULL_DATA_DIR = "_full"; Loading Loading @@ -131,8 +128,13 @@ public class LocalTransport extends BackupTransport { } public LocalTransport(Context context, LocalTransportParameters parameters) { Log.i(TAG, "Creating LocalTransport for user " + context.getUserId() + " (DEBUG=" + DEBUG + ")"); Log.i( TAG, "Creating LocalTransport for user " + context.getUserId() + " (DEBUG=" + DEBUG + ")"); mContext = context; mParameters = parameters; makeDataDirs(); Loading @@ -142,7 +144,6 @@ public class LocalTransport extends BackupTransport { return mParameters; } @UsesReflection({ // As the runtime class name is used to generate the returned name, and the returned // name may be used used with reflection, generate the necessary keep rules. Loading Loading @@ -215,12 +216,12 @@ public class LocalTransport extends BackupTransport { // Encapsulation of a single k/v element change private class KVOperation { final String key; // Element filename, not the raw key, for efficiency final byte[] value; // null when this is a deletion operation final String mKey; // Element filename, not the raw key, for efficiency final byte[] mValue; // null when this is a deletion operation KVOperation(String k, byte[] v) { key = k; value = v; mKey = k; mValue = v; } } Loading Loading @@ -260,10 +261,18 @@ public class LocalTransport extends BackupTransport { if (DEBUG) { try { StructStat ss = Os.fstat(data.getFileDescriptor()); Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName + " size=" + ss.st_size + " flags=" + flags); Log.v( TAG, "performBackup() pkg=" + packageInfo.packageName + " size=" + ss.st_size + " flags=" + flags); } catch (ErrnoException e) { Log.w(TAG, "Unable to stat input file in performBackup() on " Log.w( TAG, "Unable to stat input file in performBackup() on " + packageInfo.packageName); } } Loading @@ -277,7 +286,8 @@ public class LocalTransport extends BackupTransport { Log.w(TAG, "Transport is in non-incremental only mode."); } else { Log.w(TAG, Log.w( TAG, "Requested incremental, but transport currently stores no data for the " + "package, requesting non-incremental retry."); } Loading Loading @@ -321,20 +331,24 @@ public class LocalTransport extends BackupTransport { int updatedSize = totalSize; for (KVOperation op : changeOps) { // Deduct the size of the key we're about to replace, if any final Integer curSize = datastore.get(op.key); final Integer curSize = datastore.get(op.mKey); if (curSize != null) { updatedSize -= curSize.intValue(); if (DEBUG && op.value == null) { Log.v(TAG, " delete " + op.key + ", updated total " + updatedSize); if (DEBUG && op.mValue == null) { Log.v(TAG, " delete " + op.mKey + ", updated total " + updatedSize); } } // And add back the size of the value we're about to store, if any if (op.value != null) { updatedSize += op.value.length; if (op.mValue != null) { updatedSize += op.mValue.length; if (DEBUG) { Log.v(TAG, ((curSize == null) ? " new " : " replace ") + op.key + ", updated total " + updatedSize); Log.v( TAG, ((curSize == null) ? " new " : " replace ") + op.mKey + ", updated total " + updatedSize); } } } Loading @@ -342,8 +356,12 @@ public class LocalTransport extends BackupTransport { // If our final size is over quota, report the failure if (updatedSize > KEY_VALUE_BACKUP_SIZE_QUOTA) { if (DEBUG) { Log.i(TAG, "New datastore size " + updatedSize + " exceeds quota " + KEY_VALUE_BACKUP_SIZE_QUOTA); Log.i( TAG, "New datastore size " + updatedSize + " exceeds quota " + KEY_VALUE_BACKUP_SIZE_QUOTA); } return TRANSPORT_QUOTA_EXCEEDED; } Loading @@ -351,16 +369,16 @@ public class LocalTransport extends BackupTransport { // No problem with storage size, so go ahead and apply the delta operations // (in the order that the app provided them) for (KVOperation op : changeOps) { File element = new File(packageDir, op.key); File element = new File(packageDir, op.mKey); // this is either a deletion or a rewrite-from-zero, so we can just remove // the existing file and proceed in either case. element.delete(); // if this wasn't a deletion, put the new data in place if (op.value != null) { if (op.mValue != null) { try (FileOutputStream out = new FileOutputStream(element)) { out.write(op.value, 0, op.value.length); out.write(op.mValue, 0, op.mValue.length); } catch (IOException e) { Log.e(TAG, "Unable to update key file " + element, e); return TRANSPORT_ERROR; Loading @@ -371,8 +389,7 @@ public class LocalTransport extends BackupTransport { } // Parses a backup stream into individual key/value operations private ArrayList<KVOperation> parseBackupStream(ParcelFileDescriptor data) throws IOException { private ArrayList<KVOperation> parseBackupStream(ParcelFileDescriptor data) throws IOException { ArrayList<KVOperation> changeOps = new ArrayList<>(); BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); while (changeSet.readNextHeader()) { Loading @@ -380,8 +397,14 @@ public class LocalTransport extends BackupTransport { String base64Key = new String(Base64.encode(key.getBytes(), Base64.NO_WRAP)); int dataSize = changeSet.getDataSize(); if (DEBUG) { Log.v(TAG, " Delta operation key " + key + " size " + dataSize + " key64 " + base64Key); Log.v( TAG, " Delta operation key " + key + " size " + dataSize + " key64 " + base64Key); } byte[] buf = (dataSize >= 0) ? new byte[dataSize] : null; Loading Loading @@ -631,7 +654,9 @@ public class LocalTransport extends BackupTransport { existing[num++] = CURRENT_SET_TOKEN; RestoreSet[] available = new RestoreSet[num]; String deviceName = mParameters.isDeviceTransfer() ? DEVICE_NAME_FOR_D2D_RESTORE_SET String deviceName = mParameters.isDeviceTransfer() ? DEVICE_NAME_FOR_D2D_RESTORE_SET : DEFAULT_DEVICE_NAME_FOR_RESTORE_SET; for (int i = 0; i < available.length; i++) { available[i] = new RestoreSet("Local disk image", deviceName, existing[i]); Loading @@ -647,8 +672,9 @@ public class LocalTransport extends BackupTransport { @Override public int startRestore(long token, PackageInfo[] packages) { if (DEBUG) Log.v(TAG, "start restore " + token + " : " + packages.length + " matching packages"); if (DEBUG) { Log.v(TAG, "start restore " + token + " : " + packages.length + " matching packages"); } mRestorePackages = packages; mRestorePackage = -1; mRestoreSetDir = new File(mDataDir, Long.toString(token)); Loading @@ -660,8 +686,12 @@ public class LocalTransport extends BackupTransport { @Override public RestoreDescription nextRestorePackage() { if (DEBUG) { Log.v(TAG, "nextRestorePackage() : mRestorePackage=" + mRestorePackage + " length=" + mRestorePackages.length); Log.v( TAG, "nextRestorePackage() : mRestorePackage=" + mRestorePackage + " length=" + mRestorePackages.length); } if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); Loading @@ -681,8 +711,12 @@ public class LocalTransport extends BackupTransport { File maybeFullData = new File(mRestoreSetFullDir, name); if (maybeFullData.length() > 0) { if (DEBUG) { Log.v(TAG, " nextRestorePackage(TYPE_FULL_STREAM) @ " + mRestorePackage + " = " + name); Log.v( TAG, " nextRestorePackage(TYPE_FULL_STREAM) @ " + mRestorePackage + " = " + name); } mRestoreType = RestoreDescription.TYPE_FULL_STREAM; mCurFullRestoreStream = null; // ensure starting from the ground state Loading @@ -695,7 +729,12 @@ public class LocalTransport extends BackupTransport { } if (DEBUG) { Log.v(TAG, " ... package @ " + mRestorePackage + " = " + name Log.v( TAG, " ... package @ " + mRestorePackage + " = " + name + " has no data; skipping"); } } Loading @@ -708,8 +747,12 @@ public class LocalTransport extends BackupTransport { String[] contents = (new File(mRestoreSetIncrementalDir, packageName)).list(); if (contents != null && contents.length > 0) { if (DEBUG) { Log.v(TAG, " nextRestorePackage(TYPE_KEY_VALUE) @ " + mRestorePackage + " = " + packageName); Log.v( TAG, " nextRestorePackage(TYPE_KEY_VALUE) @ " + mRestorePackage + " = " + packageName); } return true; } Loading @@ -723,8 +766,8 @@ public class LocalTransport extends BackupTransport { if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) { throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset"); } File packageDir = new File(mRestoreSetIncrementalDir, mRestorePackages[mRestorePackage].packageName); File packageDir = new File(mRestoreSetIncrementalDir, mRestorePackages[mRestorePackage].packageName); // The restore set is the concatenation of the individual record blobs, // each of which is a file in the package's directory. We return the Loading Loading @@ -814,26 +857,24 @@ public class LocalTransport extends BackupTransport { } /** * Ask the transport to provide data for the "current" package being restored. The * transport then writes some data to the socket supplied to this call, and returns * the number of bytes written. The system will then read that many bytes and * stream them to the application's agent for restore, then will call this method again * to receive the next chunk of the archive. This sequence will be repeated until the * transport returns zero indicating that all of the package's data has been delivered * (or returns a negative value indicating some sort of hard error condition at the * transport level). * Ask the transport to provide data for the "current" package being restored. The transport * then writes some data to the socket supplied to this call, and returns the number of bytes * written. The system will then read that many bytes and stream them to the application's agent * for restore, then will call this method again to receive the next chunk of the archive. This * sequence will be repeated until the transport returns zero indicating that all of the * package's data has been delivered (or returns a negative value indicating some sort of hard * error condition at the transport level). * * <p>After this method returns zero, the system will then call * {@link #getNextFullRestorePackage()} to begin the restore process for the next * application, and the sequence begins again. * <p>After this method returns zero, the system will then call {@link * #getNextFullRestorePackage()} to begin the restore process for the next application, and the * sequence begins again. * * @param socket The file descriptor that the transport will use for delivering the * streamed archive. * @return 0 when no more data for the current package is available. A positive value * indicates the presence of that much data to be delivered to the app. A negative * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, * indicating a fatal error condition that precludes further restore operations * on the current dataset. * @param socket The file descriptor that the transport will use for delivering the streamed * archive. * @return 0 when no more data for the current package is available. A positive value indicates * the presence of that much data to be delivered to the app. A negative return value is * treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, indicating a fatal * error condition that precludes further restore operations on the current dataset. */ @Override public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) { Loading Loading @@ -888,16 +929,15 @@ public class LocalTransport extends BackupTransport { /** * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM} * data for restore, it will invoke this method to tell the transport that it should * abandon the data download for the current package. The OS will then either call * {@link #nextRestorePackage()} again to move on to restoring the next package in the * set being iterated over, or will call {@link #finishRestore()} to shut down the restore * operation. * data for restore, it will invoke this method to tell the transport that it should abandon the * data download for the current package. The OS will then either call {@link * #nextRestorePackage()} again to move on to restoring the next package in the set being * iterated over, or will call {@link #finishRestore()} to shut down the restore operation. * * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious * transport-level failure. If the transport reports an error here, the entire restore * operation will immediately be finished with no further attempts to restore app data. * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the current * stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious transport-level * failure. If the transport reports an error here, the entire restore operation will * immediately be finished with no further attempts to restore app data. */ @Override public int abortFullRestore() { Loading Loading @@ -939,7 +979,8 @@ public class LocalTransport extends BackupTransport { } mNumberReceivedEvents++; boolean logResults = mParameters.logAgentResults(); ArrayList<DataTypeResult> results = event.getParcelableArrayList( ArrayList<DataTypeResult> results = event.getParcelableArrayList( BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS, DataTypeResult.class); int size = results.size(); Loading Loading @@ -999,7 +1040,8 @@ public class LocalTransport extends BackupTransport { @Override public String toString() { try { try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) { try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) { dump(pw, /* args= */ null); pw.flush(); return sw.toString(); Loading packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java +12 −6 Original line number Diff line number Diff line Loading @@ -98,12 +98,18 @@ public class LocalTransportParameters extends KeyValueSettingObserver { @Override public String toString() { return "LocalTransportParameters[mFakeEncryptionFlag=" + mFakeEncryptionFlag + ", mIsNonIncrementalOnly=" + mIsNonIncrementalOnly + ", mIsDeviceTransfer=" + mIsDeviceTransfer + ", mIsEncrypted=" + mIsEncrypted + ", mLogAgentResults=" + mLogAgentResults + ", noRestrictedModePackages()=" + noRestrictedModePackages() return "LocalTransportParameters[mFakeEncryptionFlag=" + mFakeEncryptionFlag + ", mIsNonIncrementalOnly=" + mIsNonIncrementalOnly + ", mIsDeviceTransfer=" + mIsDeviceTransfer + ", mIsEncrypted=" + mIsEncrypted + ", mLogAgentResults=" + mLogAgentResults + ", noRestrictedModePackages()=" + noRestrictedModePackages() + "]"; } } Loading
core/tests/coretests/src/android/app/backup/BackupAgentTest.java +39 −26 Original line number Diff line number Diff line Loading @@ -62,8 +62,7 @@ public class BackupAgentTest { @Mock FullBackup.BackupScheme mBackupScheme; @Mock Context mContext; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Before public void setUp() { Loading @@ -72,9 +71,9 @@ public class BackupAgentTest { @Test public void testGetIncludeExcludeRules_isNotMigration_returnsRules() throws Exception { PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags */ 0); Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test", Collections.singleton(path)); PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags= */ 0); Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test", Collections.singleton(path)); ArraySet<PathWithRequiredFlags> excludePaths = new ArraySet<>(); excludePaths.add(path); IncludeExcludeRules expectedRules = new IncludeExcludeRules(includePaths, excludePaths); Loading @@ -99,8 +98,8 @@ public class BackupAgentTest { BackupAgent agent = new TestFullBackupAgent(); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo( OperationType.BACKUP); assertThat(agent.getBackupRestoreEventLogger().getOperationType()) .isEqualTo(OperationType.BACKUP); } @Test Loading @@ -108,8 +107,8 @@ public class BackupAgentTest { BackupAgent agent = new TestFullBackupAgent(); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.RESTORE); assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo( OperationType.RESTORE); assertThat(agent.getBackupRestoreEventLogger().getOperationType()) .isEqualTo(OperationType.RESTORE); } @Test Loading Loading @@ -145,8 +144,8 @@ public class BackupAgentTest { agent.clearBackupRestoreEventLogger(); agent.onFullBackup(new FullBackupDataOutput(/* quota= */ 0)); assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get( 0).getSuccessCount()).isEqualTo(1); assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get(0).getSuccessCount()) .isEqualTo(1); } @Test Loading @@ -158,13 +157,19 @@ public class BackupAgentTest { IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createPipe(); FileOutputStream writeSide = new FileOutputStream( pipes[1].getFileDescriptor()); FileOutputStream writeSide = new FileOutputStream(pipes[1].getFileDescriptor()); writeSide.write("Hello".getBytes(StandardCharsets.UTF_8)); agentBinder.doRestoreFile(pipes[0], /* length= */ 5, BackupAgent.TYPE_FILE, FullBackup.FILES_TREE_TOKEN, /* path= */ "hello_file", /* mode= */ 0666, /* mtime= */ 12345, /* token= */ 6789, mIBackupManager); agentBinder.doRestoreFile( pipes[0], /* length= */ 5, BackupAgent.TYPE_FILE, FullBackup.FILES_TREE_TOKEN, /* path= */ "hello_file", /* mode= */ 0666, /* mtime= */ 12345, /* token= */ 6789, mIBackupManager); try (FileInputStream in = new FileInputStream(pipes[0].getFileDescriptor())) { assertThat(in.available()).isEqualTo(0); Loading @@ -182,8 +187,9 @@ public class BackupAgentTest { private static class TestFullBackupAgent extends BackupAgent { @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { public void onBackup( ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Left empty as this is a full backup agent. } Loading @@ -193,8 +199,9 @@ public class BackupAgentTest { } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { public void onRestore( BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Left empty as this is a full backup agent. } } Loading @@ -202,8 +209,14 @@ public class BackupAgentTest { private static class TestRestoreIgnoringFullBackupAgent extends TestFullBackupAgent { @Override protected void onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime) protected void onRestoreFile( ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime) throws IOException { // Ignore the file and don't consume any data. } Loading
packages/LocalTransport/src/com/android/localtransport/LocalTransport.java +137 −95 Original line number Diff line number Diff line Loading @@ -61,25 +61,22 @@ import java.util.Collections; import java.util.List; /** * Backup transport for stashing stuff into a known location on disk, and * later restoring from there. For testing only. * Backup transport for stashing stuff into a known location on disk, and later restoring from * there. For testing only. * * <p>Note: the quickest way to build and sync this class is: * {@code m LocalTransport && adb install $OUT/system/priv-app/LocalTransport/LocalTransport.apk} * <p>Note: the quickest way to build and sync this class is: {@code m LocalTransport && adb install * $OUT/system/priv-app/LocalTransport/LocalTransport.apk} */ public class LocalTransport extends BackupTransport { static final String TAG = "LocalTransport"; static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE); private static final String TRANSPORT_DIR_NAME = "com.android.localtransport.LocalTransport"; private static final String TRANSPORT_DIR_NAME = "com.android.localtransport.LocalTransport"; private static final String TRANSPORT_DESTINATION_STRING = "Backing up to debug-only private cache"; private static final String TRANSPORT_DESTINATION_STRING = "Backing up to debug-only private cache"; private static final String TRANSPORT_DATA_MANAGEMENT_LABEL = ""; private static final String TRANSPORT_DATA_MANAGEMENT_LABEL = ""; private static final String INCREMENTAL_DIR = "_delta"; private static final String FULL_DATA_DIR = "_full"; Loading Loading @@ -131,8 +128,13 @@ public class LocalTransport extends BackupTransport { } public LocalTransport(Context context, LocalTransportParameters parameters) { Log.i(TAG, "Creating LocalTransport for user " + context.getUserId() + " (DEBUG=" + DEBUG + ")"); Log.i( TAG, "Creating LocalTransport for user " + context.getUserId() + " (DEBUG=" + DEBUG + ")"); mContext = context; mParameters = parameters; makeDataDirs(); Loading @@ -142,7 +144,6 @@ public class LocalTransport extends BackupTransport { return mParameters; } @UsesReflection({ // As the runtime class name is used to generate the returned name, and the returned // name may be used used with reflection, generate the necessary keep rules. Loading Loading @@ -215,12 +216,12 @@ public class LocalTransport extends BackupTransport { // Encapsulation of a single k/v element change private class KVOperation { final String key; // Element filename, not the raw key, for efficiency final byte[] value; // null when this is a deletion operation final String mKey; // Element filename, not the raw key, for efficiency final byte[] mValue; // null when this is a deletion operation KVOperation(String k, byte[] v) { key = k; value = v; mKey = k; mValue = v; } } Loading Loading @@ -260,10 +261,18 @@ public class LocalTransport extends BackupTransport { if (DEBUG) { try { StructStat ss = Os.fstat(data.getFileDescriptor()); Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName + " size=" + ss.st_size + " flags=" + flags); Log.v( TAG, "performBackup() pkg=" + packageInfo.packageName + " size=" + ss.st_size + " flags=" + flags); } catch (ErrnoException e) { Log.w(TAG, "Unable to stat input file in performBackup() on " Log.w( TAG, "Unable to stat input file in performBackup() on " + packageInfo.packageName); } } Loading @@ -277,7 +286,8 @@ public class LocalTransport extends BackupTransport { Log.w(TAG, "Transport is in non-incremental only mode."); } else { Log.w(TAG, Log.w( TAG, "Requested incremental, but transport currently stores no data for the " + "package, requesting non-incremental retry."); } Loading Loading @@ -321,20 +331,24 @@ public class LocalTransport extends BackupTransport { int updatedSize = totalSize; for (KVOperation op : changeOps) { // Deduct the size of the key we're about to replace, if any final Integer curSize = datastore.get(op.key); final Integer curSize = datastore.get(op.mKey); if (curSize != null) { updatedSize -= curSize.intValue(); if (DEBUG && op.value == null) { Log.v(TAG, " delete " + op.key + ", updated total " + updatedSize); if (DEBUG && op.mValue == null) { Log.v(TAG, " delete " + op.mKey + ", updated total " + updatedSize); } } // And add back the size of the value we're about to store, if any if (op.value != null) { updatedSize += op.value.length; if (op.mValue != null) { updatedSize += op.mValue.length; if (DEBUG) { Log.v(TAG, ((curSize == null) ? " new " : " replace ") + op.key + ", updated total " + updatedSize); Log.v( TAG, ((curSize == null) ? " new " : " replace ") + op.mKey + ", updated total " + updatedSize); } } } Loading @@ -342,8 +356,12 @@ public class LocalTransport extends BackupTransport { // If our final size is over quota, report the failure if (updatedSize > KEY_VALUE_BACKUP_SIZE_QUOTA) { if (DEBUG) { Log.i(TAG, "New datastore size " + updatedSize + " exceeds quota " + KEY_VALUE_BACKUP_SIZE_QUOTA); Log.i( TAG, "New datastore size " + updatedSize + " exceeds quota " + KEY_VALUE_BACKUP_SIZE_QUOTA); } return TRANSPORT_QUOTA_EXCEEDED; } Loading @@ -351,16 +369,16 @@ public class LocalTransport extends BackupTransport { // No problem with storage size, so go ahead and apply the delta operations // (in the order that the app provided them) for (KVOperation op : changeOps) { File element = new File(packageDir, op.key); File element = new File(packageDir, op.mKey); // this is either a deletion or a rewrite-from-zero, so we can just remove // the existing file and proceed in either case. element.delete(); // if this wasn't a deletion, put the new data in place if (op.value != null) { if (op.mValue != null) { try (FileOutputStream out = new FileOutputStream(element)) { out.write(op.value, 0, op.value.length); out.write(op.mValue, 0, op.mValue.length); } catch (IOException e) { Log.e(TAG, "Unable to update key file " + element, e); return TRANSPORT_ERROR; Loading @@ -371,8 +389,7 @@ public class LocalTransport extends BackupTransport { } // Parses a backup stream into individual key/value operations private ArrayList<KVOperation> parseBackupStream(ParcelFileDescriptor data) throws IOException { private ArrayList<KVOperation> parseBackupStream(ParcelFileDescriptor data) throws IOException { ArrayList<KVOperation> changeOps = new ArrayList<>(); BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); while (changeSet.readNextHeader()) { Loading @@ -380,8 +397,14 @@ public class LocalTransport extends BackupTransport { String base64Key = new String(Base64.encode(key.getBytes(), Base64.NO_WRAP)); int dataSize = changeSet.getDataSize(); if (DEBUG) { Log.v(TAG, " Delta operation key " + key + " size " + dataSize + " key64 " + base64Key); Log.v( TAG, " Delta operation key " + key + " size " + dataSize + " key64 " + base64Key); } byte[] buf = (dataSize >= 0) ? new byte[dataSize] : null; Loading Loading @@ -631,7 +654,9 @@ public class LocalTransport extends BackupTransport { existing[num++] = CURRENT_SET_TOKEN; RestoreSet[] available = new RestoreSet[num]; String deviceName = mParameters.isDeviceTransfer() ? DEVICE_NAME_FOR_D2D_RESTORE_SET String deviceName = mParameters.isDeviceTransfer() ? DEVICE_NAME_FOR_D2D_RESTORE_SET : DEFAULT_DEVICE_NAME_FOR_RESTORE_SET; for (int i = 0; i < available.length; i++) { available[i] = new RestoreSet("Local disk image", deviceName, existing[i]); Loading @@ -647,8 +672,9 @@ public class LocalTransport extends BackupTransport { @Override public int startRestore(long token, PackageInfo[] packages) { if (DEBUG) Log.v(TAG, "start restore " + token + " : " + packages.length + " matching packages"); if (DEBUG) { Log.v(TAG, "start restore " + token + " : " + packages.length + " matching packages"); } mRestorePackages = packages; mRestorePackage = -1; mRestoreSetDir = new File(mDataDir, Long.toString(token)); Loading @@ -660,8 +686,12 @@ public class LocalTransport extends BackupTransport { @Override public RestoreDescription nextRestorePackage() { if (DEBUG) { Log.v(TAG, "nextRestorePackage() : mRestorePackage=" + mRestorePackage + " length=" + mRestorePackages.length); Log.v( TAG, "nextRestorePackage() : mRestorePackage=" + mRestorePackage + " length=" + mRestorePackages.length); } if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); Loading @@ -681,8 +711,12 @@ public class LocalTransport extends BackupTransport { File maybeFullData = new File(mRestoreSetFullDir, name); if (maybeFullData.length() > 0) { if (DEBUG) { Log.v(TAG, " nextRestorePackage(TYPE_FULL_STREAM) @ " + mRestorePackage + " = " + name); Log.v( TAG, " nextRestorePackage(TYPE_FULL_STREAM) @ " + mRestorePackage + " = " + name); } mRestoreType = RestoreDescription.TYPE_FULL_STREAM; mCurFullRestoreStream = null; // ensure starting from the ground state Loading @@ -695,7 +729,12 @@ public class LocalTransport extends BackupTransport { } if (DEBUG) { Log.v(TAG, " ... package @ " + mRestorePackage + " = " + name Log.v( TAG, " ... package @ " + mRestorePackage + " = " + name + " has no data; skipping"); } } Loading @@ -708,8 +747,12 @@ public class LocalTransport extends BackupTransport { String[] contents = (new File(mRestoreSetIncrementalDir, packageName)).list(); if (contents != null && contents.length > 0) { if (DEBUG) { Log.v(TAG, " nextRestorePackage(TYPE_KEY_VALUE) @ " + mRestorePackage + " = " + packageName); Log.v( TAG, " nextRestorePackage(TYPE_KEY_VALUE) @ " + mRestorePackage + " = " + packageName); } return true; } Loading @@ -723,8 +766,8 @@ public class LocalTransport extends BackupTransport { if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) { throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset"); } File packageDir = new File(mRestoreSetIncrementalDir, mRestorePackages[mRestorePackage].packageName); File packageDir = new File(mRestoreSetIncrementalDir, mRestorePackages[mRestorePackage].packageName); // The restore set is the concatenation of the individual record blobs, // each of which is a file in the package's directory. We return the Loading Loading @@ -814,26 +857,24 @@ public class LocalTransport extends BackupTransport { } /** * Ask the transport to provide data for the "current" package being restored. The * transport then writes some data to the socket supplied to this call, and returns * the number of bytes written. The system will then read that many bytes and * stream them to the application's agent for restore, then will call this method again * to receive the next chunk of the archive. This sequence will be repeated until the * transport returns zero indicating that all of the package's data has been delivered * (or returns a negative value indicating some sort of hard error condition at the * transport level). * Ask the transport to provide data for the "current" package being restored. The transport * then writes some data to the socket supplied to this call, and returns the number of bytes * written. The system will then read that many bytes and stream them to the application's agent * for restore, then will call this method again to receive the next chunk of the archive. This * sequence will be repeated until the transport returns zero indicating that all of the * package's data has been delivered (or returns a negative value indicating some sort of hard * error condition at the transport level). * * <p>After this method returns zero, the system will then call * {@link #getNextFullRestorePackage()} to begin the restore process for the next * application, and the sequence begins again. * <p>After this method returns zero, the system will then call {@link * #getNextFullRestorePackage()} to begin the restore process for the next application, and the * sequence begins again. * * @param socket The file descriptor that the transport will use for delivering the * streamed archive. * @return 0 when no more data for the current package is available. A positive value * indicates the presence of that much data to be delivered to the app. A negative * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, * indicating a fatal error condition that precludes further restore operations * on the current dataset. * @param socket The file descriptor that the transport will use for delivering the streamed * archive. * @return 0 when no more data for the current package is available. A positive value indicates * the presence of that much data to be delivered to the app. A negative return value is * treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, indicating a fatal * error condition that precludes further restore operations on the current dataset. */ @Override public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) { Loading Loading @@ -888,16 +929,15 @@ public class LocalTransport extends BackupTransport { /** * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM} * data for restore, it will invoke this method to tell the transport that it should * abandon the data download for the current package. The OS will then either call * {@link #nextRestorePackage()} again to move on to restoring the next package in the * set being iterated over, or will call {@link #finishRestore()} to shut down the restore * operation. * data for restore, it will invoke this method to tell the transport that it should abandon the * data download for the current package. The OS will then either call {@link * #nextRestorePackage()} again to move on to restoring the next package in the set being * iterated over, or will call {@link #finishRestore()} to shut down the restore operation. * * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious * transport-level failure. If the transport reports an error here, the entire restore * operation will immediately be finished with no further attempts to restore app data. * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the current * stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious transport-level * failure. If the transport reports an error here, the entire restore operation will * immediately be finished with no further attempts to restore app data. */ @Override public int abortFullRestore() { Loading Loading @@ -939,7 +979,8 @@ public class LocalTransport extends BackupTransport { } mNumberReceivedEvents++; boolean logResults = mParameters.logAgentResults(); ArrayList<DataTypeResult> results = event.getParcelableArrayList( ArrayList<DataTypeResult> results = event.getParcelableArrayList( BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS, DataTypeResult.class); int size = results.size(); Loading Loading @@ -999,7 +1040,8 @@ public class LocalTransport extends BackupTransport { @Override public String toString() { try { try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) { try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) { dump(pw, /* args= */ null); pw.flush(); return sw.toString(); Loading
packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java +12 −6 Original line number Diff line number Diff line Loading @@ -98,12 +98,18 @@ public class LocalTransportParameters extends KeyValueSettingObserver { @Override public String toString() { return "LocalTransportParameters[mFakeEncryptionFlag=" + mFakeEncryptionFlag + ", mIsNonIncrementalOnly=" + mIsNonIncrementalOnly + ", mIsDeviceTransfer=" + mIsDeviceTransfer + ", mIsEncrypted=" + mIsEncrypted + ", mLogAgentResults=" + mLogAgentResults + ", noRestrictedModePackages()=" + noRestrictedModePackages() return "LocalTransportParameters[mFakeEncryptionFlag=" + mFakeEncryptionFlag + ", mIsNonIncrementalOnly=" + mIsNonIncrementalOnly + ", mIsDeviceTransfer=" + mIsDeviceTransfer + ", mIsEncrypted=" + mIsEncrypted + ", mLogAgentResults=" + mLogAgentResults + ", noRestrictedModePackages()=" + noRestrictedModePackages() + "]"; } }