Loading core/api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -9283,6 +9283,7 @@ package android.app.backup { method public void onCreate(); method public void onDestroy(); method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException; method @FlaggedApi("com.android.server.backup.enable_cross_platform_transfer") public long onMeasureFullBackup(long, int) throws java.io.IOException; method public void onQuotaExceeded(long, long); method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException; method public void onRestore(android.app.backup.BackupDataInput, long, android.os.ParcelFileDescriptor) throws java.io.IOException; core/java/android/app/backup/BackupAgent.java +39 −6 Original line number Diff line number Diff line Loading @@ -160,8 +160,8 @@ public abstract class BackupAgent extends ContextWrapper { public static final int TYPE_SYMLINK = 3; /** * Flag for {@link BackupDataOutput#getTransportFlags()} and {@link * FullBackupDataOutput#getTransportFlags()} only. * Flag for {@link BackupDataOutput#getTransportFlags()}, {@link * FullBackupDataOutput#getTransportFlags()} and {@link #onMeasureFullBackup(long, int)} only. * * <p>The transport has client-side encryption enabled. i.e., the user's backup has been * encrypted with a key known only to the device, and not to the remote storage solution. Even Loading @@ -171,8 +171,8 @@ public abstract class BackupAgent extends ContextWrapper { public static final int FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED = 1; /** * Flag for {@link BackupDataOutput#getTransportFlags()} and {@link * FullBackupDataOutput#getTransportFlags()} only. * Flag for {@link BackupDataOutput#getTransportFlags()}, {@link * FullBackupDataOutput#getTransportFlags()} and {@link #onMeasureFullBackup(long, int)} only. * * <p>The transport is for a device-to-device transfer. There is no third party or intermediate * storage. The user's backup data is sent directly to another device over e.g., USB or WiFi. Loading Loading @@ -636,6 +636,26 @@ public abstract class BackupAgent extends ContextWrapper { return new IncludeExcludeRules(manifestIncludeMap, manifestExcludeSet); } /** * Estimate how much data in bytes a full backup will deliver. This is used during the preflight * check to make sure the size doesn't exceed the backup quota. * * <p>By default, the backup size is measured by calling {@link * #onFullBackup(FullBackupDataOutput)} and looking at the size of the backup it produces while * discarding the data. This method can be overridden to provide an alternative, more efficient * estimation if necessary. * * @param quotaBytes The maximum data size that the transport currently permits this application * to store as a backup. * @param transportFlags flags with additional information about the backup transport. * @return estimated size of the full backup. If the returned size is negative, the backup agent * will fallback to using {@link #onFullBackup(FullBackupDataOutput)} to measure the size. */ @FlaggedApi(Flags.FLAG_ENABLE_CROSS_PLATFORM_TRANSFER) public long onMeasureFullBackup(long quotaBytes, int transportFlags) throws IOException { return -1; } /** * Notification that the application's current backup operation causes it to exceed the maximum * size permitted by the transport. The ongoing backup operation is halted and rolled back: any Loading Loading @@ -1348,6 +1368,7 @@ public abstract class BackupAgent extends ContextWrapper { public void doMeasureFullBackup( long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) { long estimatedBackupSize = -1; FullBackupDataOutput measureOutput = new FullBackupDataOutput(quotaBytes, transportFlags); Loading @@ -1356,7 +1377,15 @@ public abstract class BackupAgent extends ContextWrapper { // Ensure that we're running with the app's normal permission level final long ident = Binder.clearCallingIdentity(); try { if (Flags.enableCrossPlatformTransfer()) { estimatedBackupSize = BackupAgent.this.onMeasureFullBackup(quotaBytes, transportFlags); if (estimatedBackupSize < 0) { BackupAgent.this.onFullBackup(measureOutput); } } else { BackupAgent.this.onFullBackup(measureOutput); } } catch (IOException ex) { Log.d( TAG, Loading @@ -1373,7 +1402,11 @@ public abstract class BackupAgent extends ContextWrapper { Binder.restoreCallingIdentity(ident); try { callbackBinder.opCompleteForUser( getBackupUserId(), token, measureOutput.getSize()); getBackupUserId(), token, estimatedBackupSize >= 0 ? estimatedBackupSize : measureOutput.getSize()); } catch (RemoteException e) { // timeout, so we're safe } Loading core/tests/coretests/src/android/app/backup/BackupAgentTest.java +85 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app.backup; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.IBackupAgent; Loading @@ -28,6 +29,8 @@ import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArraySet; Loading Loading @@ -179,6 +182,53 @@ public class BackupAgentTest { } } @Test @DisableFlags({Flags.FLAG_ENABLE_CROSS_PLATFORM_TRANSFER}) public void doMeasureFullBackup_flagOff_callOnFullBackup() throws Exception { TestMeasureSizeBackupAgent agent = new TestMeasureSizeBackupAgent(1024); agent.attach(mContext); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); agentBinder.doMeasureFullBackup( /* quotaBytes= */ 2048, /* token= */ 0, mIBackupManager, /* transportFlags= */ 0); assertThat(agent.mOnMeasureFullBackupCalled).isFalse(); assertThat(agent.mOnFullBackupCalled).isTrue(); verify(mIBackupManager).opCompleteForUser(USER_HANDLE.getIdentifier(), 0, 0); } @Test @EnableFlags({Flags.FLAG_ENABLE_CROSS_PLATFORM_TRANSFER}) public void doMeasureFullBackup_flagOn_callsOnMeasureFullBackup() throws Exception { TestMeasureSizeBackupAgent agent = new TestMeasureSizeBackupAgent(1024); agent.attach(mContext); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); agentBinder.doMeasureFullBackup( /* quotaBytes= */ 2048, /* token= */ 0, mIBackupManager, /* transportFlags= */ 0); assertThat(agent.mOnMeasureFullBackupCalled).isTrue(); assertThat(agent.mOnFullBackupCalled).isFalse(); verify(mIBackupManager).opCompleteForUser(USER_HANDLE.getIdentifier(), 0, 1024); } @Test public void doMeasureFullBackup_flagOn_negativeSize_callsOnFullBackup() throws Exception { TestMeasureSizeBackupAgent agent = new TestMeasureSizeBackupAgent(-1); agent.attach(mContext); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); agentBinder.doMeasureFullBackup( /* quotaBytes= */ 2048, /* token= */ 0, mIBackupManager, /* transportFlags= */ 0); assertThat(agent.mOnMeasureFullBackupCalled).isTrue(); assertThat(agent.mOnFullBackupCalled).isTrue(); verify(mIBackupManager).opCompleteForUser(USER_HANDLE.getIdentifier(), 0, 0); } private BackupAgent getAgentForBackupDestination(@BackupDestination int backupDestination) { BackupAgent agent = new TestFullBackupAgent(); agent.onCreate(USER_HANDLE, backupDestination); Loading Loading @@ -221,4 +271,39 @@ public class BackupAgentTest { // Ignore the file and don't consume any data. } } private static class TestMeasureSizeBackupAgent extends BackupAgent { private final long mSize; private boolean mOnFullBackupCalled; private boolean mOnMeasureFullBackupCalled; TestMeasureSizeBackupAgent(long size) { this.mSize = size; } @Override public void onBackup( ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { // Left empty as this is a full backup agent. } @Override public void onRestore( BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) { // Left empty as this is a full backup agent. } @Override public void onFullBackup(FullBackupDataOutput data) { mOnFullBackupCalled = true; } @Override public long onMeasureFullBackup(long quotaBytes, int transportFlags) { mOnMeasureFullBackupCalled = true; return mSize; } } } services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java +5 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,11 @@ public class ForwardingBackupAgent extends BackupAgent { mBackupAgent.onFullBackup(data); } @Override public long onMeasureFullBackup(long quotaBytes, int transportFlags) throws IOException { return mBackupAgent.onMeasureFullBackup(quotaBytes, transportFlags); } @Override public void onQuotaExceeded(long backupDataBytes, long quotaBytes) { mBackupAgent.onQuotaExceeded(backupDataBytes, quotaBytes); Loading Loading
core/api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -9283,6 +9283,7 @@ package android.app.backup { method public void onCreate(); method public void onDestroy(); method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException; method @FlaggedApi("com.android.server.backup.enable_cross_platform_transfer") public long onMeasureFullBackup(long, int) throws java.io.IOException; method public void onQuotaExceeded(long, long); method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException; method public void onRestore(android.app.backup.BackupDataInput, long, android.os.ParcelFileDescriptor) throws java.io.IOException;
core/java/android/app/backup/BackupAgent.java +39 −6 Original line number Diff line number Diff line Loading @@ -160,8 +160,8 @@ public abstract class BackupAgent extends ContextWrapper { public static final int TYPE_SYMLINK = 3; /** * Flag for {@link BackupDataOutput#getTransportFlags()} and {@link * FullBackupDataOutput#getTransportFlags()} only. * Flag for {@link BackupDataOutput#getTransportFlags()}, {@link * FullBackupDataOutput#getTransportFlags()} and {@link #onMeasureFullBackup(long, int)} only. * * <p>The transport has client-side encryption enabled. i.e., the user's backup has been * encrypted with a key known only to the device, and not to the remote storage solution. Even Loading @@ -171,8 +171,8 @@ public abstract class BackupAgent extends ContextWrapper { public static final int FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED = 1; /** * Flag for {@link BackupDataOutput#getTransportFlags()} and {@link * FullBackupDataOutput#getTransportFlags()} only. * Flag for {@link BackupDataOutput#getTransportFlags()}, {@link * FullBackupDataOutput#getTransportFlags()} and {@link #onMeasureFullBackup(long, int)} only. * * <p>The transport is for a device-to-device transfer. There is no third party or intermediate * storage. The user's backup data is sent directly to another device over e.g., USB or WiFi. Loading Loading @@ -636,6 +636,26 @@ public abstract class BackupAgent extends ContextWrapper { return new IncludeExcludeRules(manifestIncludeMap, manifestExcludeSet); } /** * Estimate how much data in bytes a full backup will deliver. This is used during the preflight * check to make sure the size doesn't exceed the backup quota. * * <p>By default, the backup size is measured by calling {@link * #onFullBackup(FullBackupDataOutput)} and looking at the size of the backup it produces while * discarding the data. This method can be overridden to provide an alternative, more efficient * estimation if necessary. * * @param quotaBytes The maximum data size that the transport currently permits this application * to store as a backup. * @param transportFlags flags with additional information about the backup transport. * @return estimated size of the full backup. If the returned size is negative, the backup agent * will fallback to using {@link #onFullBackup(FullBackupDataOutput)} to measure the size. */ @FlaggedApi(Flags.FLAG_ENABLE_CROSS_PLATFORM_TRANSFER) public long onMeasureFullBackup(long quotaBytes, int transportFlags) throws IOException { return -1; } /** * Notification that the application's current backup operation causes it to exceed the maximum * size permitted by the transport. The ongoing backup operation is halted and rolled back: any Loading Loading @@ -1348,6 +1368,7 @@ public abstract class BackupAgent extends ContextWrapper { public void doMeasureFullBackup( long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) { long estimatedBackupSize = -1; FullBackupDataOutput measureOutput = new FullBackupDataOutput(quotaBytes, transportFlags); Loading @@ -1356,7 +1377,15 @@ public abstract class BackupAgent extends ContextWrapper { // Ensure that we're running with the app's normal permission level final long ident = Binder.clearCallingIdentity(); try { if (Flags.enableCrossPlatformTransfer()) { estimatedBackupSize = BackupAgent.this.onMeasureFullBackup(quotaBytes, transportFlags); if (estimatedBackupSize < 0) { BackupAgent.this.onFullBackup(measureOutput); } } else { BackupAgent.this.onFullBackup(measureOutput); } } catch (IOException ex) { Log.d( TAG, Loading @@ -1373,7 +1402,11 @@ public abstract class BackupAgent extends ContextWrapper { Binder.restoreCallingIdentity(ident); try { callbackBinder.opCompleteForUser( getBackupUserId(), token, measureOutput.getSize()); getBackupUserId(), token, estimatedBackupSize >= 0 ? estimatedBackupSize : measureOutput.getSize()); } catch (RemoteException e) { // timeout, so we're safe } Loading
core/tests/coretests/src/android/app/backup/BackupAgentTest.java +85 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app.backup; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.IBackupAgent; Loading @@ -28,6 +29,8 @@ import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArraySet; Loading Loading @@ -179,6 +182,53 @@ public class BackupAgentTest { } } @Test @DisableFlags({Flags.FLAG_ENABLE_CROSS_PLATFORM_TRANSFER}) public void doMeasureFullBackup_flagOff_callOnFullBackup() throws Exception { TestMeasureSizeBackupAgent agent = new TestMeasureSizeBackupAgent(1024); agent.attach(mContext); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); agentBinder.doMeasureFullBackup( /* quotaBytes= */ 2048, /* token= */ 0, mIBackupManager, /* transportFlags= */ 0); assertThat(agent.mOnMeasureFullBackupCalled).isFalse(); assertThat(agent.mOnFullBackupCalled).isTrue(); verify(mIBackupManager).opCompleteForUser(USER_HANDLE.getIdentifier(), 0, 0); } @Test @EnableFlags({Flags.FLAG_ENABLE_CROSS_PLATFORM_TRANSFER}) public void doMeasureFullBackup_flagOn_callsOnMeasureFullBackup() throws Exception { TestMeasureSizeBackupAgent agent = new TestMeasureSizeBackupAgent(1024); agent.attach(mContext); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); agentBinder.doMeasureFullBackup( /* quotaBytes= */ 2048, /* token= */ 0, mIBackupManager, /* transportFlags= */ 0); assertThat(agent.mOnMeasureFullBackupCalled).isTrue(); assertThat(agent.mOnFullBackupCalled).isFalse(); verify(mIBackupManager).opCompleteForUser(USER_HANDLE.getIdentifier(), 0, 1024); } @Test public void doMeasureFullBackup_flagOn_negativeSize_callsOnFullBackup() throws Exception { TestMeasureSizeBackupAgent agent = new TestMeasureSizeBackupAgent(-1); agent.attach(mContext); agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP); IBackupAgent agentBinder = (IBackupAgent) agent.onBind(); agentBinder.doMeasureFullBackup( /* quotaBytes= */ 2048, /* token= */ 0, mIBackupManager, /* transportFlags= */ 0); assertThat(agent.mOnMeasureFullBackupCalled).isTrue(); assertThat(agent.mOnFullBackupCalled).isTrue(); verify(mIBackupManager).opCompleteForUser(USER_HANDLE.getIdentifier(), 0, 0); } private BackupAgent getAgentForBackupDestination(@BackupDestination int backupDestination) { BackupAgent agent = new TestFullBackupAgent(); agent.onCreate(USER_HANDLE, backupDestination); Loading Loading @@ -221,4 +271,39 @@ public class BackupAgentTest { // Ignore the file and don't consume any data. } } private static class TestMeasureSizeBackupAgent extends BackupAgent { private final long mSize; private boolean mOnFullBackupCalled; private boolean mOnMeasureFullBackupCalled; TestMeasureSizeBackupAgent(long size) { this.mSize = size; } @Override public void onBackup( ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { // Left empty as this is a full backup agent. } @Override public void onRestore( BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) { // Left empty as this is a full backup agent. } @Override public void onFullBackup(FullBackupDataOutput data) { mOnFullBackupCalled = true; } @Override public long onMeasureFullBackup(long quotaBytes, int transportFlags) { mOnMeasureFullBackupCalled = true; return mSize; } } }
services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java +5 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,11 @@ public class ForwardingBackupAgent extends BackupAgent { mBackupAgent.onFullBackup(data); } @Override public long onMeasureFullBackup(long quotaBytes, int transportFlags) throws IOException { return mBackupAgent.onMeasureFullBackup(quotaBytes, transportFlags); } @Override public void onQuotaExceeded(long backupDataBytes, long quotaBytes) { mBackupAgent.onQuotaExceeded(backupDataBytes, quotaBytes); Loading