content: /*
   * 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.times;
  import static org.mockito.Mockito.verify;
  import static org.mockito.Mockito.when;
  import static org.robolectric.Shadows.shadowOf;
  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.ShadowAppBackupUtils;
  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 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.IOException;
  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 = {ShadowAppBackupUtils.class, ShadowApplicationPackageManager.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 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();
          ShadowAppBackupUtils.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);
          ShadowAppBackupUtils.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);
          ShadowAppBackupUtils.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);
          ShadowAppBackupUtils.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<TransportMock> 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);
      }
  
      /* 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);
              ShadowAppBackupUtils.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);
          ShadowAppBackupUtils.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 Trampoline(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 Trampoline(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 Trampoline(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 Trampoline(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 Trampoline(mContext),
                                  mBackupThread,
                                  /* baseStateDir */ null,
                                  mDataDir,
                                  mTransportManager));
      }
  
      /**
       * Test checking non-null argument on {@link
       * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
       * File, File, TransportManager)}.
       */
      @Test
      public void testCreateAndInitializeService_withNullDataDir_throws() {
          expectThrows(
                  NullPointerException.class,
                  () ->
                          UserBackupManagerService.createAndInitializeService(
                                  USER_ID,
                                  mContext,
                                  new Trampoline(mContext),
                                  mBackupThread,
                                  mBaseStateDir,
                                  /* dataDir */ null,
                                  mTransportManager));
      }
  
      /**
       * Test checking non-null argument on {@link
       * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
       * File, File, TransportManager)}.
       */
      @Test
      public void testCreateAndInitializeService_withNullTransportManager_throws() {
          expectThrows(
                  NullPointerException.class,
                  () ->
                          UserBackupManagerService.createAndInitializeService(
                                  USER_ID,
                                  mContext,
                                  new Trampoline(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 Trampoline(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());
      }
  
      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);
      }
  
      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();
          }
      }
  }
  