Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5f12930 authored by Piyush Mehrotra's avatar Piyush Mehrotra Committed by Android (Google) Code Review
Browse files

Merge "Control behavior of unified restore pipeline after transport failure...

Merge "Control behavior of unified restore pipeline after transport failure during K/V restore with flag" into udc-dev
parents 1246a1f6 cedd0bec
Loading
Loading
Loading
Loading
+15 −0
Original line number Original line Diff line number Diff line
@@ -77,4 +77,19 @@ public class BackupAndRestoreFeatureFlags {
                /* name= */ "full_backup_utils_route_buffer_size_bytes",
                /* name= */ "full_backup_utils_route_buffer_size_bytes",
                /* defaultValue= */ 32 * 1024); // 32 KB
                /* 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);
    }
}
}
+34 −5
Original line number Original line Diff line number Diff line
@@ -57,6 +57,7 @@ import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupAndRestoreFeatureFlags;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
import com.android.server.backup.BackupUtils;
import com.android.server.backup.OperationStorage;
import com.android.server.backup.OperationStorage;
@@ -168,11 +169,13 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
    private final BackupEligibilityRules mBackupEligibilityRules;
    private final BackupEligibilityRules mBackupEligibilityRules;


    @VisibleForTesting
    @VisibleForTesting
    PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
    PerformUnifiedRestoreTask(
            UserBackupManagerService backupManagerService,
            TransportConnection transportConnection) {
        mListener = null;
        mListener = null;
        mAgentTimeoutParameters = null;
        mAgentTimeoutParameters = null;
        mOperationStorage = null;
        mOperationStorage = null;
        mTransportConnection = null;
        mTransportConnection = transportConnection;
        mTransportManager = null;
        mTransportManager = null;
        mEphemeralOpToken = 0;
        mEphemeralOpToken = 0;
        mUserId = 0;
        mUserId = 0;
@@ -731,13 +734,18 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
                            ParcelFileDescriptor.MODE_TRUNCATE);
                            ParcelFileDescriptor.MODE_TRUNCATE);


            if (transport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
            if (transport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
                // Transport-level failure, so we wind everything up and
                // Transport-level failure. This failure could be specific to package currently in
                // terminate the restore operation.
                // restore.
                Slog.e(TAG, "Error getting restore data for " + packageName);
                Slog.e(TAG, "Error getting restore data for " + packageName);
                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
                stage.close();
                stage.close();
                downloadFile.delete();
                downloadFile.delete();
                executeNextState(UnifiedRestoreState.FINAL);
                UnifiedRestoreState nextState =
                        BackupAndRestoreFeatureFlags
                                .getUnifiedRestoreContinueAfterTransportFailureInKvRestore()
                                ? UnifiedRestoreState.RUNNING_QUEUE
                                : UnifiedRestoreState.FINAL;
                executeNextState(nextState);
                return;
                return;
            }
            }


@@ -1358,6 +1366,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
        executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
        executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
    }
    }


    @VisibleForTesting
    void executeNextState(UnifiedRestoreState nextState) {
    void executeNextState(UnifiedRestoreState nextState) {
        if (MORE_DEBUG) {
        if (MORE_DEBUG) {
            Slog.i(TAG, " => executing next step on "
            Slog.i(TAG, " => executing next step on "
@@ -1369,6 +1378,26 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
        backupManagerService.getBackupHandler().sendMessage(msg);
        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
    // restore observer support
    void sendStartRestore(int numPackages) {
    void sendStartRestore(int numPackages) {
        if (mObserver != null) {
        if (mObserver != null) {
+20 −0
Original line number Original line Diff line number Diff line
@@ -104,4 +104,24 @@ public class BackupAndRestoreFeatureFlagsTest {
        assertThat(BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes())
        assertThat(BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes())
                .isEqualTo(5678);
                .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);
    }
}
}
+93 −7
Original line number Original line Diff line number Diff line
@@ -25,20 +25,33 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
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.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;

import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;


import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.backup.UserBackupManagerService;
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.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.Answer;
@@ -62,9 +75,14 @@ public class PerformUnifiedRestoreTaskTest {
    private static final String SYSTEM_PACKAGE_NAME = "android";
    private static final String SYSTEM_PACKAGE_NAME = "android";
    private static final String NON_SYSTEM_PACKAGE_NAME = "package";
    private static final String NON_SYSTEM_PACKAGE_NAME = "package";


    @Mock private BackupDataInput mBackupDataInput;
    @Mock
    @Mock private BackupDataOutput mBackupDataOutput;
    private BackupDataInput mBackupDataInput;
    @Mock private UserBackupManagerService mBackupManagerService;
    @Mock
    private BackupDataOutput mBackupDataOutput;
    @Mock
    private UserBackupManagerService mBackupManagerService;
    @Mock
    private TransportConnection mTransportConnection;


    private Set<String> mExcludedkeys = new HashSet<>();
    private Set<String> mExcludedkeys = new HashSet<>();
    private Map<String, String> mBackupData = new HashMap<>();
    private Map<String, String> mBackupData = new HashMap<>();
@@ -74,12 +92,20 @@ public class PerformUnifiedRestoreTaskTest {
    private Set<String> mBackupDataDump;
    private Set<String> mBackupDataDump;
    private PerformUnifiedRestoreTask mRestoreTask;
    private PerformUnifiedRestoreTask mRestoreTask;


    @Rule
    public TestableDeviceConfig.TestableDeviceConfigRule
            mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();

    private Context mContext;

    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);


        populateTestData();
        populateTestData();


        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

        mBackupDataSource = new ArrayDeque<>(mBackupData.keySet());
        mBackupDataSource = new ArrayDeque<>(mBackupData.keySet());
        when(mBackupDataInput.readNextHeader()).then(new Answer<Boolean>() {
        when(mBackupDataInput.readNextHeader()).then(new Answer<Boolean>() {
            @Override
            @Override
@@ -106,7 +132,7 @@ public class PerformUnifiedRestoreTaskTest {
                    }
                    }
                });
                });


        mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService);
        mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
    }
    }


    private void populateTestData() {
    private void populateTestData() {
@@ -179,4 +205,64 @@ public class PerformUnifiedRestoreTaskTest {


        assertTrue(mRestoreTask.shouldStageBackupData(SYSTEM_PACKAGE_NAME));
        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);
    }
}
}