All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.backup.UserBackupManagerServiceTest Maven / Gradle / Ivy

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.backup;

import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
import static com.android.server.backup.testing.TransportData.backupTransport;
import static com.android.server.backup.testing.TransportData.d2dTransport;
import static com.android.server.backup.testing.TransportData.localTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.expectThrows;

import android.app.backup.BackupManager;
import android.app.backup.IBackupObserver;
import android.app.backup.ISelectBackupTransportCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;

import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.testing.shadows.ShadowBackupEligibilityRules;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBinder;
import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
import com.android.server.testing.shadows.ShadowSystemServiceRegistry;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowContextWrapper;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Tests for the per-user instance of the backup/restore system service {@link
 * UserBackupManagerService} that performs operations for its target user.
 */
@RunWith(RobolectricTestRunner.class)
@Config(
        shadows = {
            ShadowBackupEligibilityRules.class,
            ShadowApplicationPackageManager.class,
            ShadowSystemServiceRegistry.class
        })
@Presubmit
public class UserBackupManagerServiceTest {
    private static final String TAG = "BMSTest";
    private static final String PACKAGE_1 = "some.package.1";
    private static final String PACKAGE_2 = "some.package.2";
    private static final String USER_FACING_PACKAGE = "user.facing.package";
    private static final int USER_ID = 10;

    @Mock private TransportManager mTransportManager;
    private HandlerThread mBackupThread;
    private ShadowLooper mShadowBackupLooper;
    private File mBaseStateDir;
    private File mDataDir;
    private ShadowContextWrapper mShadowContext;
    private Context mContext;
    private TransportData mTransport;
    private String mTransportName;
    private ShadowPackageManager mShadowPackageManager;

    /**
     * Initialize state that {@link UserBackupManagerService} operations interact with. This
     * includes setting up the transport, starting the backup thread, and creating backup data
     * directories.
     */
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        mTransport = backupTransport();
        mTransportName = mTransport.transportName;

        // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
        // we should not fail tests because of this. This is not flakiness, the exceptions thrown
        // don't interfere with the tests.
        mBackupThread = startSilentBackupThread(TAG);
        mShadowBackupLooper = shadowOf(mBackupThread.getLooper());

        ContextWrapper context = RuntimeEnvironment.application;
        mShadowPackageManager = shadowOf(context.getPackageManager());
        mContext = context;
        mShadowContext = shadowOf(context);

        File cacheDir = mContext.getCacheDir();
        // Corresponds to /data/backup
        mBaseStateDir = new File(cacheDir, "base_state");
        // Corresponds to /cache/backup_stage
        mDataDir = new File(cacheDir, "data");
    }

    /**
     * Clean up and reset state that was created for testing {@link UserBackupManagerService}
     * operations.
     */
    @After
    public void tearDown() throws Exception {
        mBackupThread.quit();
        ShadowBackupEligibilityRules.reset();
        ShadowApplicationPackageManager.reset();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns the
     * current destination string of inputted transport if the transport is registered.
     */
    @Test
    public void testDestinationString() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
                .thenReturn("destinationString");
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        String destination = backupManagerService.getDestinationString(mTransportName);

        assertThat(destination).isEqualTo("destinationString");
    }

    /**
     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns
     * {@code null} if the inputted transport is not registered.
     */
    @Test
    public void testDestinationString_whenTransportNotRegistered() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
                .thenThrow(TransportNotRegisteredException.class);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        String destination = backupManagerService.getDestinationString(mTransportName);

        assertThat(destination).isNull();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} throws a
     * {@link SecurityException} if the caller does not have backup permission.
     */
    @Test
    public void testDestinationString_withoutPermission() throws Exception {
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
                .thenThrow(TransportNotRegisteredException.class);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () -> backupManagerService.getDestinationString(mTransportName));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
     * {@code false} when the given app is not eligible for backup.
     */
    @Test
    public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        setUpCurrentTransport(mTransportManager, mTransport);
        registerPackages(PACKAGE_1);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);

        assertThat(result).isFalse();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
     * {@code true} when the given app is eligible for backup.
     */
    @Test
    public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
        registerPackages(PACKAGE_1);
        ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);

        assertThat(result).isTrue();
        verify(mTransportManager)
                .disposeOfTransportClient(eq(transportMock.transportClient), any());
    }

    /**
     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} throws a
     * {@link SecurityException} if the caller does not have backup permission.
     */
    @Test
    public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        setUpCurrentTransport(mTransportManager, mTransport);
        registerPackages(PACKAGE_1);
        ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
     * returns an {@code array} of only apps that are eligible for backup from an {@array} of
     * inputted apps.
     */
    @Test
    public void testFilterAppsEligibleForBackup() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
        registerPackages(PACKAGE_1, PACKAGE_2);
        ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        String[] filtered =
                backupManagerService.filterAppsEligibleForBackup(
                        new String[] {PACKAGE_1, PACKAGE_2});

        assertThat(filtered).asList().containsExactly(PACKAGE_1);
        verify(mTransportManager)
                .disposeOfTransportClient(eq(transportMock.transportClient), any());
    }

    /**
     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
     * returns an empty {@code array} if no inputted apps are eligible for backup.
     */
    @Test
    public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        registerPackages(PACKAGE_1, PACKAGE_2);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        String[] filtered =
                backupManagerService.filterAppsEligibleForBackup(
                        new String[] {PACKAGE_1, PACKAGE_2});

        assertThat(filtered).isEmpty();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
     * throws a {@link SecurityException} if the caller does not have backup permission.
     */
    @Test
    public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        setUpCurrentTransport(mTransportManager, mTransport);
        registerPackages(PACKAGE_1, PACKAGE_2);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () ->
                        backupManagerService.filterAppsEligibleForBackup(
                                new String[] {PACKAGE_1, PACKAGE_2}));
    }

    /* Tests for select transport */

    private ComponentName mNewTransportComponent;
    private TransportData mNewTransport;
    private TransportMock mNewTransportMock;
    private TransportData mOldTransport;
    private TransportMock mOldTransportMock;

    private void setUpForSelectTransport() throws Exception {
        mNewTransport = backupTransport();
        mNewTransportComponent = mNewTransport.getTransportComponent();
        mOldTransport = d2dTransport();
        List transportMocks =
                setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
        mNewTransportMock = transportMocks.get(0);
        mOldTransportMock = transportMocks.get(1);
        when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
                .thenReturn(mOldTransport.transportName);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)}
     * successfully switches the current transport to the inputted transport, returns the name of
     * the old transport, and disposes of the transport client after the operation.
     */
    @Test
    public void testSelectBackupTransport() throws Exception {
        setUpForSelectTransport();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        String oldTransport =
                backupManagerService.selectBackupTransport(mNewTransport.transportName);

        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
        assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
        verify(mTransportManager)
                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
    }

    /**
     * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)} throws a
     * {@link SecurityException} if the caller does not have backup permission.
     */
    @Test
    public void testSelectBackupTransport_withoutPermission() throws Exception {
        setUpForSelectTransport();
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
     * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
     * transport and disposes of the transport client after the operation.
     */
    @Test
    public void testSelectBackupTransportAsync() throws Exception {
        setUpForSelectTransport();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
                .thenReturn(BackupManager.SUCCESS);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);

        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);

        mShadowBackupLooper.runToEndOfTasks();
        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
        verify(callback).onSuccess(eq(mNewTransport.transportName));
        verify(mTransportManager)
                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
    }

    /**
     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
     * transport and notifies the inputted callback of failure when it fails to register the
     * transport.
     */
    @Test
    public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
        setUpForSelectTransport();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
                .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);

        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);

        mShadowBackupLooper.runToEndOfTasks();
        assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
        verify(callback).onFailure(anyInt());
    }

    /**
     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
     * transport and notifies the inputted callback of failure when the transport gets unregistered.
     */
    @Test
    public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
        setUpTransports(mTransportManager, mTransport.unregistered());
        ComponentName newTransportComponent = mTransport.getTransportComponent();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
                .thenReturn(BackupManager.SUCCESS);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);

        backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);

        mShadowBackupLooper.runToEndOfTasks();
        assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
        verify(callback).onFailure(anyInt());
    }

    /**
     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
     * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
     * have backup permission.
     */
    @Test
    public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
        setUpForSelectTransport();
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        ComponentName newTransportComponent = mNewTransport.getTransportComponent();

        expectThrows(
                SecurityException.class,
                () ->
                        backupManagerService.selectBackupTransportAsync(
                                newTransportComponent, mock(ISelectBackupTransportCallback.class)));
    }

    private String getSettingsTransport() {
        return Settings.Secure.getString(
                mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
     * the {@link ComponentName} of the currently selected transport.
     */
    @Test
    public void testGetCurrentTransportComponent() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.getCurrentTransportComponent())
                .thenReturn(mTransport.getTransportComponent());
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();

        assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
    }

    /**
     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
     * {@code null} if there is no currently selected transport.
     */
    @Test
    public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();

        assertThat(transportComponent).isNull();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
     * {@code null} if the currently selected transport is not registered.
     */
    @Test
    public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        when(mTransportManager.getCurrentTransportComponent())
                .thenThrow(TransportNotRegisteredException.class);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();

        assertThat(transportComponent).isNull();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} throws a
     * {@link SecurityException} if the caller does not have backup permission.
     */
    @Test
    public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#excludeKeysFromRestore(String, List)}
     * throws a {@link SecurityException} if the caller does not have backup permission.
     */
    @Test
    public void testExcludeKeysFromRestore_withoutPermission() throws Exception {
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () ->
                        backupManagerService.excludeKeysFromRestore(
                                PACKAGE_1,
                                new ArrayList(){}));
    }

    /* Tests for updating transport attributes */

    private static final int PACKAGE_UID = 10;
    private ComponentName mTransportComponent;
    private int mTransportUid;

    private void setUpForUpdateTransportAttributes() throws Exception {
        mTransportComponent = mTransport.getTransportComponent();
        String transportPackage = mTransportComponent.getPackageName();
        PackageInfo packageInfo = getPackageInfo(transportPackage);

        ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
        shadowPackageManager.installPackage(packageInfo);
        shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
        // Set up for user invocations on ApplicationPackageManager.
        ShadowApplicationPackageManager.addInstalledPackage(transportPackage, packageInfo);
        ShadowApplicationPackageManager.setPackageUid(transportPackage, PACKAGE_UID);

        mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} succeeds if the uid of the transport
     * is same as the uid of the caller.
     */
    @Test
    public void
            testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
                    throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        Intent configurationIntent = new Intent();
        Intent dataManagementIntent = new Intent();
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        backupManagerService.updateTransportAttributes(
                mTransportUid,
                mTransportComponent,
                mTransportName,
                configurationIntent,
                "currentDestinationString",
                dataManagementIntent,
                "dataManagementLabel");

        verify(mTransportManager)
                .updateTransportAttributes(
                        eq(mTransportComponent),
                        eq(mTransportName),
                        eq(configurationIntent),
                        eq("currentDestinationString"),
                        eq(dataManagementIntent),
                        eq("dataManagementLabel"));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
     * the uid of the transport is not equal to the uid of the caller.
     */
    @Test
    public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
            throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () ->
                        backupManagerService.updateTransportAttributes(
                                mTransportUid + 1,
                                mTransportComponent,
                                mTransportName,
                                new Intent(),
                                "currentDestinationString",
                                new Intent(),
                                "dataManagementLabel"));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
     * given a {@code null} transport component.
     */
    @Test
    public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
            throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                RuntimeException.class,
                () ->
                        backupManagerService.updateTransportAttributes(
                                mTransportUid,
                                null,
                                mTransportName,
                                new Intent(),
                                "currentDestinationString",
                                new Intent(),
                                "dataManagementLabel"));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
     * given a {@code null} transport name.
     */
    @Test
    public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                RuntimeException.class,
                () ->
                        backupManagerService.updateTransportAttributes(
                                mTransportUid,
                                mTransportComponent,
                                null,
                                new Intent(),
                                "currentDestinationString",
                                new Intent(),
                                "dataManagementLabel"));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
     * given a {@code null} destination string.
     */
    @Test
    public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
            throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                RuntimeException.class,
                () ->
                        backupManagerService.updateTransportAttributes(
                                mTransportUid,
                                mTransportComponent,
                                mTransportName,
                                new Intent(),
                                null,
                                new Intent(),
                                "dataManagementLabel"));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
     * given either a {@code null} data management label or {@code null} data management intent, but
     * not both.
     */
    @Test
    public void
            testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
                    throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                RuntimeException.class,
                () ->
                        backupManagerService.updateTransportAttributes(
                                mTransportUid,
                                mTransportComponent,
                                mTransportName,
                                new Intent(),
                                "currentDestinationString",
                                null,
                                "dataManagementLabel"));

        expectThrows(
                RuntimeException.class,
                () ->
                        backupManagerService.updateTransportAttributes(
                                mTransportUid,
                                mTransportComponent,
                                mTransportName,
                                new Intent(),
                                "currentDestinationString",
                                new Intent(),
                                null));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} succeeds if the caller has backup
     * permission.
     */
    @Test
    public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
            throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        Intent configurationIntent = new Intent();
        Intent dataManagementIntent = new Intent();
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        backupManagerService.updateTransportAttributes(
                mTransportUid,
                mTransportComponent,
                mTransportName,
                configurationIntent,
                "currentDestinationString",
                dataManagementIntent,
                "dataManagementLabel");

        verify(mTransportManager)
                .updateTransportAttributes(
                        eq(mTransportComponent),
                        eq(mTransportName),
                        eq(configurationIntent),
                        eq("currentDestinationString"),
                        eq(dataManagementIntent),
                        eq("dataManagementLabel"));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
     * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
     * the caller does not have backup permission.
     */
    @Test
    public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
            throws Exception {
        setUpForUpdateTransportAttributes();
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () ->
                        backupManagerService.updateTransportAttributes(
                                mTransportUid,
                                mTransportComponent,
                                mTransportName,
                                new Intent(),
                                "currentDestinationString",
                                new Intent(),
                                "dataManagementLabel"));
    }

    /* Tests for request backup */

    @Mock private IBackupObserver mObserver;

    private void setUpForRequestBackup(String... packages) throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        for (String packageName : packages) {
            registerPackages(packageName);
            ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(packageName);
        }
        setUpCurrentTransport(mTransportManager, mTransport);
    }

    private void tearDownForRequestBackup() {
        ShadowKeyValueBackupTask.reset();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} throws a {@link SecurityException} if the caller does not have backup permission.
     */
    @Test
    public void testRequestBackup_whenPermissionDenied() throws Exception {
        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                SecurityException.class,
                () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
     */
    @Test
    public void testRequestBackup_whenPackagesNull() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                IllegalArgumentException.class,
                () -> backupManagerService.requestBackup(null, mObserver, 0));
        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
     * packages.
     */
    @Test
    public void testRequestBackup_whenPackagesEmpty() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();

        expectThrows(
                IllegalArgumentException.class,
                () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
     */
    @Test
    public void testRequestBackup_whenBackupDisabled() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        backupManagerService.setEnabled(false);

        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);

        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
     * through SUW.
     */
    @Test
    public void testRequestBackup_whenNotProvisioned() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        backupManagerService.setSetupComplete(false);

        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);

        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
     * registered.
     */
    @Test
    public void testRequestBackup_whenTransportNotRegistered() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        setUpCurrentTransport(mTransportManager, mTransport.unregistered());
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        backupManagerService.setEnabled(true);
        backupManagerService.setSetupComplete(true);

        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);

        assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
     * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
     */
    @Test
    public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        registerPackages(PACKAGE_1);
        setUpCurrentTransport(mTransportManager, mTransport);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        backupManagerService.setEnabled(true);
        backupManagerService.setSetupComplete(true);
        // Haven't set PACKAGE_1 as eligible

        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);

        assertThat(result).isEqualTo(BackupManager.SUCCESS);
        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
        // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
        tearDownForRequestBackup();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
     * package succeeds.
     */
    @Test
    @Config(shadows = ShadowKeyValueBackupTask.class)
    public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
        setUpForRequestBackup(PACKAGE_1);
        UserBackupManagerService backupManagerService =
                createBackupManagerServiceForRequestBackup();

        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);

        mShadowBackupLooper.runToEndOfTasks();
        assertThat(result).isEqualTo(BackupManager.SUCCESS);
        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
        assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
        assertThat(shadowTask.getPendingFullBackups()).isEmpty();
        // TODO: Assert more about KeyValueBackupTask
        tearDownForRequestBackup();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
     * backup package succeeds.
     */
    @Test
    @Config(shadows = ShadowKeyValueBackupTask.class)
    public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
        setUpForRequestBackup(PACKAGE_1);
        ShadowBackupEligibilityRules.setAppGetsFullBackup(PACKAGE_1);
        UserBackupManagerService backupManagerService =
                createBackupManagerServiceForRequestBackup();

        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);

        mShadowBackupLooper.runToEndOfTasks();
        assertThat(result).isEqualTo(BackupManager.SUCCESS);
        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
        assertThat(shadowTask.getQueue()).isEmpty();
        assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
        // TODO: Assert more about KeyValueBackupTask
        tearDownForRequestBackup();
    }

    /**
     * Test verifying that {@link UserBackupManagerService#backupNow()} clears the calling identity
     * for scheduling a job and then restores the original calling identity after the operation.
     */
    @Test
    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
    public void testBackupNow_clearsCallingIdentityForJobScheduler() {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        setUpPowerManager(backupManagerService);
        ShadowBinder.setCallingUid(1);

        backupManagerService.backupNow();

        assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
        assertThat(Binder.getCallingUid()).isEqualTo(1);
    }

    /**
     * Test verifying that {@link UserBackupManagerService#backupNow()} restores the original
     * calling identity if an exception is thrown during execution.
     */
    @Test
    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
    public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        setUpPowerManager(backupManagerService);
        ShadowBinder.setCallingUid(1);

        expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
        assertThat(ShadowKeyValueBackupJobException.getCallingUid())
                .isEqualTo(ShadowBinder.LOCAL_UID);
        assertThat(Binder.getCallingUid()).isEqualTo(1);
    }

    private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
        backupManagerService.setEnabled(true);
        backupManagerService.setSetupComplete(true);
        return backupManagerService;
    }

    /**
     * Test verifying that creating a new instance posts a transport registration task to the backup
     * thread.
     */
    @Test
    public void testCreateAndInitializeService_postRegisterTransports() {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);

        UserBackupManagerService.createAndInitializeService(
                USER_ID,
                mContext,
                new BackupManagerService(mContext),
                mBackupThread,
                mBaseStateDir,
                mDataDir,
                mTransportManager);

        mShadowBackupLooper.runToEndOfTasks();
        verify(mTransportManager).registerTransports();
    }

    /**
     * Test verifying that creating a new instance does not directly register transports on the main
     * thread.
     */
    @Test
    public void testCreateAndInitializeService_doesNotRegisterTransportsSynchronously() {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);

        UserBackupManagerService.createAndInitializeService(
                USER_ID,
                mContext,
                new BackupManagerService(mContext),
                mBackupThread,
                mBaseStateDir,
                mDataDir,
                mTransportManager);

        // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
        verify(mTransportManager, never()).registerTransports();
    }

    /** Test checking non-null argument on instance creation. */
    @Test
    public void testCreateAndInitializeService_withNullContext_throws() {
        expectThrows(
                NullPointerException.class,
                () ->
                        UserBackupManagerService.createAndInitializeService(
                                USER_ID,
                                /* context */ null,
                                new BackupManagerService(mContext),
                                mBackupThread,
                                mBaseStateDir,
                                mDataDir,
                                mTransportManager));
    }

    /** Test checking non-null argument on instance creation. */
    @Test
    public void testCreateAndInitializeService_withNullTrampoline_throws() {
        expectThrows(
                NullPointerException.class,
                () ->
                        UserBackupManagerService.createAndInitializeService(
                                USER_ID,
                                mContext,
                                /* trampoline */ null,
                                mBackupThread,
                                mBaseStateDir,
                                mDataDir,
                                mTransportManager));
    }

    /** Test checking non-null argument on instance creation. */
    @Test
    public void testCreateAndInitializeService_withNullBackupThread_throws() {
        expectThrows(
                NullPointerException.class,
                () ->
                        UserBackupManagerService.createAndInitializeService(
                                USER_ID,
                                mContext,
                                new BackupManagerService(mContext),
                                /* backupThread */ null,
                                mBaseStateDir,
                                mDataDir,
                                mTransportManager));
    }

    /** Test checking non-null argument on instance creation. */
    @Test
    public void testCreateAndInitializeService_withNullStateDir_throws() {
        expectThrows(
                NullPointerException.class,
                () ->
                        UserBackupManagerService.createAndInitializeService(
                                USER_ID,
                                mContext,
                                new BackupManagerService(mContext),
                                mBackupThread,
                                /* baseStateDir */ null,
                                mDataDir,
                                mTransportManager));
    }

    /**
     * Test checking non-null argument on {@link
     * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
     * HandlerThread, File, File, TransportManager)}.
     */
    @Test
    public void testCreateAndInitializeService_withNullDataDir_throws() {
        expectThrows(
                NullPointerException.class,
                () ->
                        UserBackupManagerService.createAndInitializeService(
                                USER_ID,
                                mContext,
                                new BackupManagerService(mContext),
                                mBackupThread,
                                mBaseStateDir,
                                /* dataDir */ null,
                                mTransportManager));
    }

    /**
     * Test checking non-null argument on {@link
     * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
     * HandlerThread, File, File, TransportManager)}.
     */
    @Test
    public void testCreateAndInitializeService_withNullTransportManager_throws() {
        expectThrows(
                NullPointerException.class,
                () ->
                        UserBackupManagerService.createAndInitializeService(
                                USER_ID,
                                mContext,
                                new BackupManagerService(mContext),
                                mBackupThread,
                                mBaseStateDir,
                                mDataDir,
                                /* transportManager */ null));
    }

    /**
     * Test verifying that creating a new instance registers the broadcast receiver for package
     * tracking
     */
    @Test
    public void testCreateAndInitializeService_registersPackageTrackingReceiver() throws Exception {
        Context contextSpy = Mockito.spy(mContext);

        UserBackupManagerService service = UserBackupManagerService.createAndInitializeService(
                USER_ID,
                contextSpy,
                new BackupManagerService(mContext),
                mBackupThread,
                mBaseStateDir,
                mDataDir,
                mTransportManager);

        BroadcastReceiver packageTrackingReceiver = service.getPackageTrackingReceiver();
        assertThat(packageTrackingReceiver).isNotNull();

        // One call for package changes and one call for sd card events.
        verify(contextSpy, times(2)).registerReceiverAsUser(
                eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any());
    }

    @Test
    public void testFilterUserFacingPackages_shouldSkipUserFacing_filtersUserFacing() {
        List packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
                getPackageInfo(PACKAGE_1));
        UserBackupManagerService backupManagerService = spy(
                createUserBackupManagerServiceAndRunTasks());
        when(backupManagerService.shouldSkipUserFacingData()).thenReturn(true);
        when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);

        List filteredPackages = backupManagerService.filterUserFacingPackages(
                packages);

        assertFalse(containsPackage(filteredPackages, USER_FACING_PACKAGE));
        assertTrue(containsPackage(filteredPackages, PACKAGE_1));
    }

    @Test
    public void testFilterUserFacingPackages_shouldNotSkipUserFacing_doesNotFilterUserFacing() {
        List packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
                getPackageInfo(PACKAGE_1));
        UserBackupManagerService backupManagerService = spy(
                createUserBackupManagerServiceAndRunTasks());
        when(backupManagerService.shouldSkipUserFacingData()).thenReturn(false);
        when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);

        List filteredPackages = backupManagerService.filterUserFacingPackages(
                packages);

        assertTrue(containsPackage(filteredPackages, USER_FACING_PACKAGE));
        assertTrue(containsPackage(filteredPackages, PACKAGE_1));
    }

    private static boolean containsPackage(List packages, String targetPackage) {
        for (PackageInfo packageInfo : packages) {
            if (targetPackage.equals(packageInfo.packageName)) {
                return true;
            }
        }
        return false;
    }

    private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
        return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
                USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
    }

    private void setUpPowerManager(UserBackupManagerService backupManagerService) {
        PowerManager powerManagerMock = mock(PowerManager.class);
        when(powerManagerMock.getPowerSaveState(anyInt()))
                .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
        backupManagerService.setPowerManager(powerManagerMock);
    }

    private PackageInfo getPackageInfo(String packageName) {
        PackageInfo packageInfo = new PackageInfo();
        packageInfo.packageName = packageName;
        packageInfo.applicationInfo = new ApplicationInfo();
        packageInfo.applicationInfo.packageName = packageName;
        return packageInfo;
    }

    private void registerPackages(String... packages) {
        for (String packageName : packages) {
            PackageInfo packageInfo = getPackageInfo(packageName);
            mShadowPackageManager.installPackage(packageInfo);
            ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
        }
    }

    /**
     * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns {@code -1}
     * when value not set.
     */
    @Test
    public void testGetAncestralSerialNumber_notSet_returnsMinusOne() {
        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();

        assertThat(service.getAncestralSerialNumber()).isEqualTo(-1L);
    }

    /**
     * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value
     * when value set.
     */
    @Test
    public void testGetAncestralSerialNumber_set_returnsCorrectValue() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
        service.setAncestralSerialNumberFile(createTestFile());

        long testSerialNumber = 20L;
        service.setAncestralSerialNumber(testSerialNumber);

        assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber);
    }

    /**
     * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value
     * when value set.
     */
    @Test
    public void testGetAncestralSerialNumber_setTwice_returnsCorrectValue() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
        service.setAncestralSerialNumberFile(createTestFile());

        long testSerialNumber = 20L;
        long testSerialNumber2 = 21L;
        service.setAncestralSerialNumber(testSerialNumber);
        service.setAncestralSerialNumber(testSerialNumber2);

        assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2);
    }

    /**
     * Test that {@link UserBackupManagerService#dump()} for system user does not prefix dump with
     * "User 0:".
     */
    @Test
    public void testDump_forSystemUser_DoesNotHaveUserPrefix() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService service =
                BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
                        UserHandle.USER_SYSTEM,
                        mContext,
                        mBackupThread,
                        mBaseStateDir,
                        mDataDir,
                        mTransportManager);

        StringWriter dump = new StringWriter();
        service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);

        assertThat(dump.toString()).startsWith("Backup Manager is ");
    }

    /**
     * Test that {@link UserBackupManagerService#dump()} for non-system user prefixes dump with
     * "User :".
     */
    @Test
    public void testDump_forNonSystemUser_HasUserPrefix() throws Exception {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();

        StringWriter dump = new StringWriter();
        service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);

        assertThat(dump.toString()).startsWith("User " + USER_ID + ":" + "Backup Manager is ");
    }

    private File createTestFile() throws IOException {
        File testFile = new File(mContext.getFilesDir(), "test");
        testFile.createNewFile();
        return testFile;
    }

    /**
     * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
     * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
     */
    @Implements(KeyValueBackupJob.class)
    public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
        /**
         * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
         * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
         */
        public static void schedule(int userId, Context ctx, long delay,
                BackupManagerConstants constants) {
            ShadowKeyValueBackupJob.schedule(userId, ctx, delay, constants);
            throw new IllegalArgumentException();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy