• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.backup;
18 
19 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
20 import static com.android.server.backup.testing.TransportData.backupTransport;
21 import static com.android.server.backup.testing.TransportData.d2dTransport;
22 import static com.android.server.backup.testing.TransportData.localTransport;
23 import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
24 import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
25 import static com.google.common.truth.Truth.assertThat;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.ArgumentMatchers.anyInt;
28 import static org.mockito.ArgumentMatchers.eq;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 import static org.robolectric.Shadows.shadowOf;
34 import static org.testng.Assert.expectThrows;
35 
36 import android.app.backup.BackupManager;
37 import android.app.backup.ISelectBackupTransportCallback;
38 import android.content.ComponentName;
39 import android.content.Context;
40 import android.content.ContextWrapper;
41 import android.content.Intent;
42 import android.os.HandlerThread;
43 import android.platform.test.annotations.Presubmit;
44 import android.provider.Settings;
45 
46 import com.android.server.testing.shadows.ShadowAppBackupUtils;
47 import com.android.server.testing.shadows.ShadowBackupPolicyEnforcer;
48 import com.android.server.backup.testing.TransportData;
49 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
50 import com.android.server.backup.transport.TransportNotRegisteredException;
51 import com.android.server.testing.FrameworkRobolectricTestRunner;
52 import com.android.server.testing.SystemLoaderPackages;
53 
54 import java.io.File;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.Mock;
63 import org.mockito.MockitoAnnotations;
64 import org.robolectric.RuntimeEnvironment;
65 import org.robolectric.annotation.Config;
66 import org.robolectric.shadows.ShadowContextWrapper;
67 import org.robolectric.shadows.ShadowLog;
68 import org.robolectric.shadows.ShadowLooper;
69 import org.robolectric.shadows.ShadowPackageManager;
70 import org.robolectric.shadows.ShadowSettings;
71 import org.robolectric.shadows.ShadowSystemClock;
72 
73 @RunWith(FrameworkRobolectricTestRunner.class)
74 @Config(
75     manifest = Config.NONE,
76     sdk = 26,
77     shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class}
78 )
79 @SystemLoaderPackages({"com.android.server.backup"})
80 @Presubmit
81 public class BackupManagerServiceTest {
82     private static final String TAG = "BMSTest";
83 
84     @Mock private TransportManager mTransportManager;
85     private HandlerThread mBackupThread;
86     private ShadowLooper mShadowBackupLooper;
87     private File mBaseStateDir;
88     private File mDataDir;
89     private ShadowContextWrapper mShadowContext;
90     private Context mContext;
91     private TransportData mTransport;
92     private String mTransportName;
93 
94     @Before
setUp()95     public void setUp() throws Exception {
96         MockitoAnnotations.initMocks(this);
97 
98         mTransport = backupTransport();
99         mTransportName = mTransport.transportName;
100 
101         mBackupThread = startBackupThread(this::uncaughtException);
102         mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
103 
104         ContextWrapper context = RuntimeEnvironment.application;
105         mContext = context;
106         mShadowContext = shadowOf(context);
107 
108         File cacheDir = mContext.getCacheDir();
109         mBaseStateDir = new File(cacheDir, "base_state_dir");
110         mDataDir = new File(cacheDir, "data_dir");
111 
112         ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
113     }
114 
115     @After
tearDown()116     public void tearDown() throws Exception {
117         mBackupThread.quit();
118         ShadowAppBackupUtils.reset();
119         ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
120     }
121 
uncaughtException(Thread thread, Throwable e)122     private void uncaughtException(Thread thread, Throwable e) {
123         // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
124         // we should not fail tests because of this. This is not flakiness, the exceptions thrown
125         // don't interfere with the tests.
126         ShadowLog.e(TAG, "Uncaught exception in test thread " + thread.getName(), e);
127     }
128 
129     /* Tests for destination string */
130 
131     @Test
testDestinationString()132     public void testDestinationString() throws Exception {
133         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
134         when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
135                 .thenReturn("destinationString");
136         BackupManagerService backupManagerService = createInitializedBackupManagerService();
137 
138         String destination = backupManagerService.getDestinationString(mTransportName);
139 
140         assertThat(destination).isEqualTo("destinationString");
141     }
142 
143     @Test
testDestinationString_whenTransportNotRegistered()144     public void testDestinationString_whenTransportNotRegistered() throws Exception {
145         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
146         when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
147                 .thenThrow(TransportNotRegisteredException.class);
148         BackupManagerService backupManagerService = createInitializedBackupManagerService();
149 
150         String destination = backupManagerService.getDestinationString(mTransportName);
151 
152         assertThat(destination).isNull();
153     }
154 
155     @Test
testDestinationString_withoutPermission()156     public void testDestinationString_withoutPermission() throws Exception {
157         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
158         when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
159                 .thenThrow(TransportNotRegisteredException.class);
160         BackupManagerService backupManagerService = createInitializedBackupManagerService();
161 
162         expectThrows(
163                 SecurityException.class,
164                 () -> backupManagerService.getDestinationString(mTransportName));
165     }
166 
167     /* Tests for app eligibility */
168 
169     @Test
testIsAppEligibleForBackup_whenAppEligible()170     public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
171         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
172         TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
173         ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> true;
174         BackupManagerService backupManagerService = createInitializedBackupManagerService();
175 
176         boolean result = backupManagerService.isAppEligibleForBackup("app.package");
177 
178         assertThat(result).isTrue();
179 
180         verify(mTransportManager)
181                 .disposeOfTransportClient(eq(transportMock.transportClient), any());
182     }
183 
184     @Test
testIsAppEligibleForBackup_whenAppNotEligible()185     public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
186         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
187         setUpCurrentTransport(mTransportManager, mTransport);
188         ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
189         BackupManagerService backupManagerService = createInitializedBackupManagerService();
190 
191         boolean result = backupManagerService.isAppEligibleForBackup("app.package");
192 
193         assertThat(result).isFalse();
194     }
195 
196     @Test
testIsAppEligibleForBackup_withoutPermission()197     public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
198         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
199         setUpCurrentTransport(mTransportManager, mTransport);
200         BackupManagerService backupManagerService = createInitializedBackupManagerService();
201 
202         expectThrows(
203                 SecurityException.class,
204                 () -> backupManagerService.isAppEligibleForBackup("app.package"));
205     }
206 
207     @Test
testFilterAppsEligibleForBackup()208     public void testFilterAppsEligibleForBackup() throws Exception {
209         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
210         TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
211         Map<String, Boolean> packagesMap = new HashMap<>();
212         packagesMap.put("package.a", true);
213         packagesMap.put("package.b", false);
214         ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = packagesMap::get;
215         BackupManagerService backupManagerService = createInitializedBackupManagerService();
216         String[] packages = packagesMap.keySet().toArray(new String[packagesMap.size()]);
217 
218         String[] filtered = backupManagerService.filterAppsEligibleForBackup(packages);
219 
220         assertThat(filtered).asList().containsExactly("package.a");
221         verify(mTransportManager)
222                 .disposeOfTransportClient(eq(transportMock.transportClient), any());
223     }
224 
225     @Test
testFilterAppsEligibleForBackup_whenNoneIsEligible()226     public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
227         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
228         ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
229         BackupManagerService backupManagerService = createInitializedBackupManagerService();
230 
231         String[] filtered =
232                 backupManagerService.filterAppsEligibleForBackup(
233                         new String[] {"package.a", "package.b"});
234 
235         assertThat(filtered).isEmpty();
236     }
237 
238     @Test
testFilterAppsEligibleForBackup_withoutPermission()239     public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
240         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
241         setUpCurrentTransport(mTransportManager, mTransport);
242         BackupManagerService backupManagerService = createInitializedBackupManagerService();
243 
244         expectThrows(
245                 SecurityException.class,
246                 () ->
247                         backupManagerService.filterAppsEligibleForBackup(
248                                 new String[] {"package.a", "package.b"}));
249     }
250 
251     /* Tests for select transport */
252 
253     private ComponentName mNewTransportComponent;
254     private TransportData mNewTransport;
255     private TransportMock mNewTransportMock;
256     private ComponentName mOldTransportComponent;
257     private TransportData mOldTransport;
258     private TransportMock mOldTransportMock;
259 
setUpForSelectTransport()260     private void setUpForSelectTransport() throws Exception {
261         mNewTransport = backupTransport();
262         mNewTransportComponent = mNewTransport.getTransportComponent();
263         mOldTransport = d2dTransport();
264         mOldTransportComponent = mOldTransport.getTransportComponent();
265         List<TransportMock> transportMocks =
266                 setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
267         mNewTransportMock = transportMocks.get(0);
268         mOldTransportMock = transportMocks.get(1);
269         when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
270                 .thenReturn(mOldTransport.transportName);
271     }
272 
273     @Test
testSelectBackupTransport()274     public void testSelectBackupTransport() throws Exception {
275         setUpForSelectTransport();
276         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
277         BackupManagerService backupManagerService = createInitializedBackupManagerService();
278 
279         String oldTransport =
280                 backupManagerService.selectBackupTransport(mNewTransport.transportName);
281 
282         assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
283         assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
284         verify(mTransportManager)
285                 .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
286     }
287 
288     @Test
testSelectBackupTransport_withoutPermission()289     public void testSelectBackupTransport_withoutPermission() throws Exception {
290         setUpForSelectTransport();
291         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
292         BackupManagerService backupManagerService = createInitializedBackupManagerService();
293 
294         expectThrows(
295                 SecurityException.class,
296                 () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
297     }
298 
299     @Test
testSelectBackupTransportAsync()300     public void testSelectBackupTransportAsync() throws Exception {
301         setUpForSelectTransport();
302         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
303         when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
304                 .thenReturn(BackupManager.SUCCESS);
305         BackupManagerService backupManagerService = createInitializedBackupManagerService();
306         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
307 
308         backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
309 
310         mShadowBackupLooper.runToEndOfTasks();
311         assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
312         verify(callback).onSuccess(eq(mNewTransport.transportName));
313         verify(mTransportManager)
314                 .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
315     }
316 
317     @Test
testSelectBackupTransportAsync_whenMandatoryTransport()318     public void testSelectBackupTransportAsync_whenMandatoryTransport() throws Exception {
319         setUpForSelectTransport();
320         ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mNewTransportComponent);
321         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
322         when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
323                 .thenReturn(BackupManager.SUCCESS);
324         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
325         BackupManagerService backupManagerService = createInitializedBackupManagerService();
326 
327         backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
328 
329         mShadowBackupLooper.runToEndOfTasks();
330         assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
331         verify(callback).onSuccess(eq(mNewTransport.transportName));
332         verify(mTransportManager)
333                 .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
334     }
335 
336     @Test
testSelectBackupTransportAsync_whenOtherThanMandatoryTransport()337     public void testSelectBackupTransportAsync_whenOtherThanMandatoryTransport() throws Exception {
338         setUpForSelectTransport();
339         ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mOldTransportComponent);
340         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
341         when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
342                 .thenReturn(BackupManager.SUCCESS);
343         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
344         BackupManagerService backupManagerService = createInitializedBackupManagerService();
345 
346         backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
347 
348         mShadowBackupLooper.runToEndOfTasks();
349         assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
350         verify(callback).onFailure(eq(BackupManager.ERROR_BACKUP_NOT_ALLOWED));
351     }
352 
353     @Test
testSelectBackupTransportAsync_whenRegistrationFails()354     public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
355         setUpForSelectTransport();
356         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
357         when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
358                 .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
359         BackupManagerService backupManagerService = createInitializedBackupManagerService();
360         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
361 
362         backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
363 
364         mShadowBackupLooper.runToEndOfTasks();
365         assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
366         verify(callback).onFailure(anyInt());
367     }
368 
369     @Test
testSelectBackupTransportAsync_whenTransportGetsUnregistered()370     public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
371         setUpTransports(mTransportManager, mTransport.unregistered());
372         ComponentName newTransportComponent = mTransport.getTransportComponent();
373         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
374         when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
375                 .thenReturn(BackupManager.SUCCESS);
376         BackupManagerService backupManagerService = createInitializedBackupManagerService();
377         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
378 
379         backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
380 
381         mShadowBackupLooper.runToEndOfTasks();
382         assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
383         verify(callback).onFailure(anyInt());
384     }
385 
386     @Test
testSelectBackupTransportAsync_withoutPermission()387     public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
388         setUpForSelectTransport();
389         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
390         BackupManagerService backupManagerService = createInitializedBackupManagerService();
391         ComponentName newTransportComponent = mNewTransport.getTransportComponent();
392 
393         expectThrows(
394                 SecurityException.class,
395                 () ->
396                         backupManagerService.selectBackupTransportAsync(
397                                 newTransportComponent, mock(ISelectBackupTransportCallback.class)));
398     }
399 
getSettingsTransport()400     private String getSettingsTransport() {
401         return ShadowSettings.ShadowSecure.getString(
402                 mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
403     }
404 
405     /* Tests for updating transport attributes */
406 
407     private static final int PACKAGE_UID = 10;
408     private ComponentName mTransportComponent;
409     private int mTransportUid;
410 
setUpForUpdateTransportAttributes()411     private void setUpForUpdateTransportAttributes() throws Exception {
412         mTransportComponent = mTransport.getTransportComponent();
413         String transportPackage = mTransportComponent.getPackageName();
414 
415         ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
416         shadowPackageManager.addPackage(transportPackage);
417         shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
418 
419         mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
420     }
421 
422     @Test
423     public void
testUpdateTransportAttributes_whenTransportUidEqualsToCallingUid_callsThroughToTransportManager()424             testUpdateTransportAttributes_whenTransportUidEqualsToCallingUid_callsThroughToTransportManager()
425                     throws Exception {
426         setUpForUpdateTransportAttributes();
427         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
428         Intent configurationIntent = new Intent();
429         Intent dataManagementIntent = new Intent();
430         BackupManagerService backupManagerService = createInitializedBackupManagerService();
431 
432         backupManagerService.updateTransportAttributes(
433                 mTransportUid,
434                 mTransportComponent,
435                 mTransportName,
436                 configurationIntent,
437                 "currentDestinationString",
438                 dataManagementIntent,
439                 "dataManagementLabel");
440 
441         verify(mTransportManager)
442                 .updateTransportAttributes(
443                         eq(mTransportComponent),
444                         eq(mTransportName),
445                         eq(configurationIntent),
446                         eq("currentDestinationString"),
447                         eq(dataManagementIntent),
448                         eq("dataManagementLabel"));
449     }
450 
451     @Test
testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()452     public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
453             throws Exception {
454         setUpForUpdateTransportAttributes();
455         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
456         BackupManagerService backupManagerService = createInitializedBackupManagerService();
457 
458         expectThrows(
459                 SecurityException.class,
460                 () ->
461                         backupManagerService.updateTransportAttributes(
462                                 mTransportUid + 1,
463                                 mTransportComponent,
464                                 mTransportName,
465                                 new Intent(),
466                                 "currentDestinationString",
467                                 new Intent(),
468                                 "dataManagementLabel"));
469     }
470 
471     @Test
testUpdateTransportAttributes_whenTransportComponentNull_throwsException()472     public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
473             throws Exception {
474         setUpForUpdateTransportAttributes();
475         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
476         BackupManagerService backupManagerService = createInitializedBackupManagerService();
477 
478         expectThrows(
479                 RuntimeException.class,
480                 () ->
481                         backupManagerService.updateTransportAttributes(
482                                 mTransportUid,
483                                 null,
484                                 mTransportName,
485                                 new Intent(),
486                                 "currentDestinationString",
487                                 new Intent(),
488                                 "dataManagementLabel"));
489     }
490 
491     @Test
testUpdateTransportAttributes_whenNameNull_throwsException()492     public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
493         setUpForUpdateTransportAttributes();
494         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
495         BackupManagerService backupManagerService = createInitializedBackupManagerService();
496 
497         expectThrows(
498                 RuntimeException.class,
499                 () ->
500                         backupManagerService.updateTransportAttributes(
501                                 mTransportUid,
502                                 mTransportComponent,
503                                 null,
504                                 new Intent(),
505                                 "currentDestinationString",
506                                 new Intent(),
507                                 "dataManagementLabel"));
508     }
509 
510     @Test
testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()511     public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
512             throws Exception {
513         setUpForUpdateTransportAttributes();
514         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
515         BackupManagerService backupManagerService = createInitializedBackupManagerService();
516 
517         expectThrows(
518                 RuntimeException.class,
519                 () ->
520                         backupManagerService.updateTransportAttributes(
521                                 mTransportUid,
522                                 mTransportComponent,
523                                 mTransportName,
524                                 new Intent(),
525                                 null,
526                                 new Intent(),
527                                 "dataManagementLabel"));
528     }
529 
530     @Test
531     public void
testUpdateTransportAttributes_whenDataManagementArgumentsNullityDontMatch_throwsException()532             testUpdateTransportAttributes_whenDataManagementArgumentsNullityDontMatch_throwsException()
533                     throws Exception {
534         setUpForUpdateTransportAttributes();
535         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
536         BackupManagerService backupManagerService = createInitializedBackupManagerService();
537 
538         expectThrows(
539                 RuntimeException.class,
540                 () ->
541                         backupManagerService.updateTransportAttributes(
542                                 mTransportUid,
543                                 mTransportComponent,
544                                 mTransportName,
545                                 new Intent(),
546                                 "currentDestinationString",
547                                 null,
548                                 "dataManagementLabel"));
549 
550         expectThrows(
551                 RuntimeException.class,
552                 () ->
553                         backupManagerService.updateTransportAttributes(
554                                 mTransportUid,
555                                 mTransportComponent,
556                                 mTransportName,
557                                 new Intent(),
558                                 "currentDestinationString",
559                                 new Intent(),
560                                 null));
561     }
562 
563     @Test
testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()564     public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
565             throws Exception {
566         setUpForUpdateTransportAttributes();
567         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
568         Intent configurationIntent = new Intent();
569         Intent dataManagementIntent = new Intent();
570         BackupManagerService backupManagerService = createInitializedBackupManagerService();
571 
572         backupManagerService.updateTransportAttributes(
573                 mTransportUid,
574                 mTransportComponent,
575                 mTransportName,
576                 configurationIntent,
577                 "currentDestinationString",
578                 dataManagementIntent,
579                 "dataManagementLabel");
580 
581         verify(mTransportManager)
582                 .updateTransportAttributes(
583                         eq(mTransportComponent),
584                         eq(mTransportName),
585                         eq(configurationIntent),
586                         eq("currentDestinationString"),
587                         eq(dataManagementIntent),
588                         eq("dataManagementLabel"));
589     }
590 
591     @Test
testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()592     public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
593             throws Exception {
594         setUpForUpdateTransportAttributes();
595         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
596         BackupManagerService backupManagerService = createInitializedBackupManagerService();
597 
598         expectThrows(
599                 SecurityException.class,
600                 () ->
601                         backupManagerService.updateTransportAttributes(
602                                 mTransportUid,
603                                 mTransportComponent,
604                                 mTransportName,
605                                 new Intent(),
606                                 "currentDestinationString",
607                                 new Intent(),
608                                 "dataManagementLabel"));
609     }
610 
611     /* Miscellaneous tests */
612 
613     @Test
testConstructor_postRegisterTransports()614     public void testConstructor_postRegisterTransports() {
615         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
616 
617         createBackupManagerService();
618 
619         mShadowBackupLooper.runToEndOfTasks();
620         verify(mTransportManager).registerTransports();
621     }
622 
623     @Test
testConstructor_doesNotRegisterTransportsSynchronously()624     public void testConstructor_doesNotRegisterTransportsSynchronously() {
625         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
626 
627         createBackupManagerService();
628 
629         // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
630         verify(mTransportManager, never()).registerTransports();
631     }
632 
createBackupManagerService()633     private BackupManagerService createBackupManagerService() {
634         return new BackupManagerService(
635                 mContext,
636                 new Trampoline(mContext),
637                 mBackupThread,
638                 mBaseStateDir,
639                 mDataDir,
640                 mTransportManager);
641     }
642 
createInitializedBackupManagerService()643     private BackupManagerService createInitializedBackupManagerService() {
644         BackupManagerService backupManagerService =
645                 new BackupManagerService(
646                         mContext,
647                         new Trampoline(mContext),
648                         mBackupThread,
649                         mBaseStateDir,
650                         mDataDir,
651                         mTransportManager);
652         mShadowBackupLooper.runToEndOfTasks();
653         // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
654         // above does NOT advance the handlers' clock, hence whenever a handler post messages with
655         // specific time to the looper the time of those messages will be before the looper's time.
656         // To fix this we advance SystemClock as well since that is from where the handlers read
657         // time.
658         ShadowSystemClock.setCurrentTimeMillis(mShadowBackupLooper.getScheduler().getCurrentTime());
659         return backupManagerService;
660     }
661 }
662