Merge "Control behavior of unified restore pipeline after transport failure during K/V restore with flag" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f5f12930ff
@@ -77,4 +77,19 @@ public class BackupAndRestoreFeatureFlags {
|
||||
/* name= */ "full_backup_utils_route_buffer_size_bytes",
|
||||
/* defaultValue= */ 32 * 1024); // 32 KB
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of the flag
|
||||
* "unified_restore_continue_after_transport_failure_in_kv_restore".
|
||||
* If true, Unified restore task will continue to next package if key-value restore of a
|
||||
* package fails due to Transport-level failure. See b/128499560 for more context.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
|
||||
public static boolean getUnifiedRestoreContinueAfterTransportFailureInKvRestore() {
|
||||
return DeviceConfig.getBoolean(
|
||||
NAMESPACE,
|
||||
/* name= */
|
||||
"unified_restore_continue_after_transport_failure_in_kv_restore",
|
||||
/* defaultValue= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import com.android.server.AppWidgetBackupBridge;
|
||||
import com.android.server.EventLogTags;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupAgentTimeoutParameters;
|
||||
import com.android.server.backup.BackupAndRestoreFeatureFlags;
|
||||
import com.android.server.backup.BackupRestoreTask;
|
||||
import com.android.server.backup.BackupUtils;
|
||||
import com.android.server.backup.OperationStorage;
|
||||
@@ -168,11 +169,13 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
private final BackupEligibilityRules mBackupEligibilityRules;
|
||||
|
||||
@VisibleForTesting
|
||||
PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
|
||||
PerformUnifiedRestoreTask(
|
||||
UserBackupManagerService backupManagerService,
|
||||
TransportConnection transportConnection) {
|
||||
mListener = null;
|
||||
mAgentTimeoutParameters = null;
|
||||
mOperationStorage = null;
|
||||
mTransportConnection = null;
|
||||
mTransportConnection = transportConnection;
|
||||
mTransportManager = null;
|
||||
mEphemeralOpToken = 0;
|
||||
mUserId = 0;
|
||||
@@ -731,13 +734,18 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
ParcelFileDescriptor.MODE_TRUNCATE);
|
||||
|
||||
if (transport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
|
||||
// Transport-level failure, so we wind everything up and
|
||||
// terminate the restore operation.
|
||||
// Transport-level failure. This failure could be specific to package currently in
|
||||
// restore.
|
||||
Slog.e(TAG, "Error getting restore data for " + packageName);
|
||||
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
|
||||
stage.close();
|
||||
downloadFile.delete();
|
||||
executeNextState(UnifiedRestoreState.FINAL);
|
||||
UnifiedRestoreState nextState =
|
||||
BackupAndRestoreFeatureFlags
|
||||
.getUnifiedRestoreContinueAfterTransportFailureInKvRestore()
|
||||
? UnifiedRestoreState.RUNNING_QUEUE
|
||||
: UnifiedRestoreState.FINAL;
|
||||
executeNextState(nextState);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1358,6 +1366,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void executeNextState(UnifiedRestoreState nextState) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, " => executing next step on "
|
||||
@@ -1369,6 +1378,26 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
backupManagerService.getBackupHandler().sendMessage(msg);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
UnifiedRestoreState getCurrentUnifiedRestoreStateForTesting() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState state) {
|
||||
mState = state;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setStateDirForTesting(File stateDir) {
|
||||
mStateDir = stateDir;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initiateOneRestoreForTesting(PackageInfo app, long appVersionCode) {
|
||||
initiateOneRestore(app, appVersionCode);
|
||||
}
|
||||
|
||||
// restore observer support
|
||||
void sendStartRestore(int numPackages) {
|
||||
if (mObserver != null) {
|
||||
|
||||
@@ -104,4 +104,24 @@ public class BackupAndRestoreFeatureFlagsTest {
|
||||
assertThat(BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes())
|
||||
.isEqualTo(5678);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getUnifiedRestoreContinueAfterTransportFailureInKvRestore_notSet_returnsDefault() {
|
||||
assertThat(
|
||||
BackupAndRestoreFeatureFlags
|
||||
.getUnifiedRestoreContinueAfterTransportFailureInKvRestore())
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getUnifiedRestoreContinueAfterTransportFailureInKvRestore_set_returnsSetValue() {
|
||||
DeviceConfig.setProperty(/*namespace=*/ "backup_and_restore",
|
||||
/*name=*/ "unified_restore_continue_after_transport_failure_in_kv_restore",
|
||||
/*value=*/ "false", /*makeDefault=*/ false);
|
||||
|
||||
assertThat(
|
||||
BackupAndRestoreFeatureFlags
|
||||
.getUnifiedRestoreContinueAfterTransportFailureInKvRestore())
|
||||
.isEqualTo(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,20 +25,33 @@ import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.backup.BackupDataInput;
|
||||
import android.app.backup.BackupDataOutput;
|
||||
import android.app.backup.BackupTransport;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import android.app.backup.BackupDataInput;
|
||||
import android.app.backup.BackupDataOutput;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import com.android.modules.utils.testing.TestableDeviceConfig;
|
||||
import com.android.server.backup.UserBackupManagerService;
|
||||
import com.android.server.backup.internal.BackupHandler;
|
||||
import com.android.server.backup.transport.BackupTransportClient;
|
||||
import com.android.server.backup.transport.TransportConnection;
|
||||
import com.android.server.backup.transport.TransportNotAvailableException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
@@ -62,9 +75,14 @@ public class PerformUnifiedRestoreTaskTest {
|
||||
private static final String SYSTEM_PACKAGE_NAME = "android";
|
||||
private static final String NON_SYSTEM_PACKAGE_NAME = "package";
|
||||
|
||||
@Mock private BackupDataInput mBackupDataInput;
|
||||
@Mock private BackupDataOutput mBackupDataOutput;
|
||||
@Mock private UserBackupManagerService mBackupManagerService;
|
||||
@Mock
|
||||
private BackupDataInput mBackupDataInput;
|
||||
@Mock
|
||||
private BackupDataOutput mBackupDataOutput;
|
||||
@Mock
|
||||
private UserBackupManagerService mBackupManagerService;
|
||||
@Mock
|
||||
private TransportConnection mTransportConnection;
|
||||
|
||||
private Set<String> mExcludedkeys = new HashSet<>();
|
||||
private Map<String, String> mBackupData = new HashMap<>();
|
||||
@@ -74,12 +92,20 @@ public class PerformUnifiedRestoreTaskTest {
|
||||
private Set<String> mBackupDataDump;
|
||||
private PerformUnifiedRestoreTask mRestoreTask;
|
||||
|
||||
@Rule
|
||||
public TestableDeviceConfig.TestableDeviceConfigRule
|
||||
mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
populateTestData();
|
||||
|
||||
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
mBackupDataSource = new ArrayDeque<>(mBackupData.keySet());
|
||||
when(mBackupDataInput.readNextHeader()).then(new Answer<Boolean>() {
|
||||
@Override
|
||||
@@ -106,7 +132,7 @@ public class PerformUnifiedRestoreTaskTest {
|
||||
}
|
||||
});
|
||||
|
||||
mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService);
|
||||
mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
|
||||
}
|
||||
|
||||
private void populateTestData() {
|
||||
@@ -179,4 +205,64 @@ public class PerformUnifiedRestoreTaskTest {
|
||||
|
||||
assertTrue(mRestoreTask.shouldStageBackupData(SYSTEM_PACKAGE_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailedKeyValueRestore_continueAfterFeatureEnabled_nextStateIsRunningQueue()
|
||||
throws TransportNotAvailableException, RemoteException {
|
||||
DeviceConfig.setProperty(
|
||||
"backup_and_restore",
|
||||
"unified_restore_continue_after_transport_failure_in_kv_restore",
|
||||
"true",
|
||||
false);
|
||||
|
||||
setupForRestoreKeyValueState(BackupTransport.TRANSPORT_ERROR);
|
||||
|
||||
mRestoreTask.setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState.RESTORE_KEYVALUE);
|
||||
mRestoreTask.setStateDirForTesting(mContext.getCacheDir());
|
||||
|
||||
PackageInfo testPackageInfo = new PackageInfo();
|
||||
testPackageInfo.packageName = "test.package.name";
|
||||
mRestoreTask.initiateOneRestoreForTesting(testPackageInfo, 0L);
|
||||
assertTrue(
|
||||
mRestoreTask.getCurrentUnifiedRestoreStateForTesting()
|
||||
== UnifiedRestoreState.RUNNING_QUEUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailedKeyValueRestore_continueAfterFeatureDisabled_nextStateIsFinal()
|
||||
throws RemoteException, TransportNotAvailableException {
|
||||
DeviceConfig.setProperty(
|
||||
"backup_and_restore",
|
||||
"unified_restore_continue_after_transport_failure_in_kv_restore",
|
||||
"false",
|
||||
false);
|
||||
|
||||
setupForRestoreKeyValueState(BackupTransport.TRANSPORT_ERROR);
|
||||
|
||||
mRestoreTask.setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState.RESTORE_KEYVALUE);
|
||||
mRestoreTask.setStateDirForTesting(mContext.getCacheDir());
|
||||
|
||||
PackageInfo testPackageInfo = new PackageInfo();
|
||||
testPackageInfo.packageName = "test.package.name";
|
||||
mRestoreTask.initiateOneRestoreForTesting(testPackageInfo, 0L);
|
||||
assertTrue(
|
||||
mRestoreTask.getCurrentUnifiedRestoreStateForTesting()
|
||||
== UnifiedRestoreState.FINAL);
|
||||
}
|
||||
|
||||
private void setupForRestoreKeyValueState(int transportStatus)
|
||||
throws RemoteException, TransportNotAvailableException {
|
||||
// Mock BackupHandler to do nothing when executeNextState() is called
|
||||
BackupHandler backupHandler = Mockito.mock(BackupHandler.class);
|
||||
when(backupHandler.obtainMessage(anyInt(), any())).thenReturn(new Message());
|
||||
when(backupHandler.sendMessage(any())).thenReturn(true);
|
||||
|
||||
// Return cache directory for any bookkeeping or maintaining persistent state.
|
||||
when(mBackupManagerService.getDataDir()).thenReturn(mContext.getCacheDir());
|
||||
when(mBackupManagerService.getBackupHandler()).thenReturn(backupHandler);
|
||||
|
||||
BackupTransportClient transport = Mockito.mock(BackupTransportClient.class);
|
||||
when(transport.getRestoreData(any())).thenReturn(transportStatus);
|
||||
when(mTransportConnection.connectOrThrow(any())).thenReturn(transport);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user