• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
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 package com.google.android.libraries.mobiledatadownload;
17 
18 import static android.system.Os.readlink;
19 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_CHECKSUM;
20 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_GROUP_NAME;
21 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_ID;
22 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_SIZE;
23 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_URL;
24 import static com.google.android.libraries.mobiledatadownload.testing.MddTestDependencies.ExecutorType;
25 import static com.google.android.libraries.mobiledatadownload.tracing.TracePropagation.propagateCallable;
26 import static com.google.common.truth.Truth.assertThat;
27 import static java.util.concurrent.TimeUnit.SECONDS;
28 import static org.junit.Assert.assertThrows;
29 import static org.junit.Assert.fail;
30 import static org.mockito.Mockito.eq;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 
34 import android.accounts.Account;
35 import android.content.Context;
36 import android.net.Uri;
37 import android.util.Log;
38 import androidx.test.core.app.ApplicationProvider;
39 import com.google.android.libraries.mobiledatadownload.account.AccountUtil;
40 import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader;
41 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
42 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidFileBackend;
43 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidUri;
44 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidUriAdapter;
45 import com.google.android.libraries.mobiledatadownload.file.backends.JavaFileBackend;
46 import com.google.android.libraries.mobiledatadownload.file.openers.ReadStringOpener;
47 import com.google.android.libraries.mobiledatadownload.file.openers.WriteStringOpener;
48 import com.google.android.libraries.mobiledatadownload.internal.util.DirectoryUtil;
49 import com.google.android.libraries.mobiledatadownload.monitor.NetworkUsageMonitor;
50 import com.google.android.libraries.mobiledatadownload.testing.BlockingFileDownloader;
51 import com.google.android.libraries.mobiledatadownload.testing.FakeTimeSource;
52 import com.google.android.libraries.mobiledatadownload.testing.TestFileDownloader;
53 import com.google.android.libraries.mobiledatadownload.testing.TestFlags;
54 import com.google.common.base.Optional;
55 import com.google.common.base.Supplier;
56 import com.google.common.collect.ImmutableList;
57 import com.google.common.util.concurrent.Futures;
58 import com.google.common.util.concurrent.ListenableFuture;
59 import com.google.common.util.concurrent.ListeningExecutorService;
60 import com.google.common.util.concurrent.ListeningScheduledExecutorService;
61 import com.google.common.util.concurrent.MoreExecutors;
62 import com.google.mobiledatadownload.ClientConfigProto.ClientFile;
63 import com.google.mobiledatadownload.ClientConfigProto.ClientFileGroup;
64 import com.google.mobiledatadownload.DownloadConfigProto.DataFile;
65 import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup;
66 import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions.DeviceNetworkPolicy;
67 import com.google.mobiledatadownload.LogEnumsProto.MddDownloadResult;
68 import com.google.mobiledatadownload.LogProto.MddDownloadResultLog;
69 import com.google.mobiledatadownload.LogProto.MddLogData;
70 import java.io.IOException;
71 import java.util.Arrays;
72 import java.util.Collection;
73 import java.util.List;
74 import java.util.concurrent.CancellationException;
75 import java.util.concurrent.ExecutionException;
76 import java.util.concurrent.Executors;
77 import java.util.concurrent.TimeoutException;
78 import org.junit.After;
79 import org.junit.Before;
80 import org.junit.Rule;
81 import org.junit.Test;
82 import org.junit.runner.RunWith;
83 import org.junit.runners.Parameterized;
84 import org.junit.runners.Parameterized.Parameter;
85 import org.junit.runners.Parameterized.Parameters;
86 import org.mockito.ArgumentCaptor;
87 import org.mockito.Mock;
88 import org.mockito.junit.MockitoJUnit;
89 import org.mockito.junit.MockitoRule;
90 
91 // NOTE: TestParameterInjector is preferred for parameterized tests, but it has a API
92 // level constraint of >= 24 while MDD has a constraint of >= 16. To prevent basic regressions, run
93 // this test using junit's Parameterized TestRunner, which supports all API levels.
94 @RunWith(Parameterized.class)
95 public class MobileDataDownloadIntegrationTest {
96 
97   private static final String TAG = "MobileDataDownloadIntegrationTest";
98   private static final int MAX_HANDLE_TASK_WAIT_TIME_SECS = 300;
99   private static final int MAX_MDD_API_WAIT_TIME_SECS = 5;
100 
101   private static final String TEST_DATA_RELATIVE_PATH =
102       "third_party/java_src/android_libs/mobiledatadownload/javatests/com/google/android/libraries/mobiledatadownload/testdata/";
103 
104   private static final ListeningScheduledExecutorService DOWNLOAD_EXECUTOR =
105       MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(2));
106 
107   private static final Context context = ApplicationProvider.getApplicationContext();
108   private final NetworkUsageMonitor networkUsageMonitor =
109       new NetworkUsageMonitor(context, new FakeTimeSource());
110 
111   private final SynchronousFileStorage fileStorage =
112       new SynchronousFileStorage(
113           ImmutableList.of(AndroidFileBackend.builder(context).build(), new JavaFileBackend()),
114           ImmutableList.of(),
115           ImmutableList.of(networkUsageMonitor));
116 
117   private final TestFlags flags = new TestFlags();
118 
119   private ListeningExecutorService controlExecutor;
120 
121   private MobileDataDownload mobileDataDownload;
122 
123   @Mock private Logger mockLogger;
124   @Mock private TaskScheduler mockTaskScheduler;
125 
126   @Rule public final MockitoRule mocks = MockitoJUnit.rule();
127 
128   @Parameter public ExecutorType controlExecutorType;
129 
130   @Parameters
data()131   public static Collection<Object[]> data() {
132     return Arrays.asList(
133         new Object[][] {
134           {ExecutorType.SINGLE_THREADED}, {ExecutorType.MULTI_THREADED},
135         });
136   }
137 
138   @Before
setUp()139   public void setUp() throws Exception {
140 
141     flags.enableZipFolder = Optional.of(true);
142 
143     controlExecutor = controlExecutorType.executor();
144   }
145 
146   @After
tearDown()147   public void tearDown() throws Exception {
148     mobileDataDownload.clear().get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
149   }
150 
151   @Test
download_success_fileGroupDownloaded()152   public void download_success_fileGroupDownloaded() throws Exception {
153     mobileDataDownload =
154         builderForTest()
155             .setFileDownloaderSupplier(
156                 () ->
157                     new TestFileDownloader(
158                         TEST_DATA_RELATIVE_PATH,
159                         fileStorage,
160                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
161             .addFileGroupPopulator(new TestFileGroupPopulator(context))
162             .build();
163 
164     waitForHandleTask();
165 
166     String debugString = mobileDataDownload.getDebugInfoAsString();
167     Log.i(TAG, "MDD Lib dump:");
168     for (String line : debugString.split("\n", -1)) {
169       Log.i(TAG, line);
170     }
171 
172     ClientFileGroup clientFileGroup = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
173     verifyClientFile(clientFileGroup.getFileList().get(0), FILE_ID, FILE_SIZE);
174   }
175 
176   @Test
download_withCustomValidator()177   public void download_withCustomValidator() throws Exception {
178     CustomFileGroupValidator validator =
179         fileGroup -> {
180           if (!fileGroup.getGroupName().equals(FILE_GROUP_NAME)) {
181             return Futures.immediateFuture(true);
182           }
183           return MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)
184               .submit(
185                   propagateCallable(
186                       () -> {
187                         SynchronousFileStorage storage =
188                             new SynchronousFileStorage(
189                                 ImmutableList.of(AndroidFileBackend.builder(context).build()));
190                         for (ClientFile file : fileGroup.getFileList()) {
191                           if (!storage.exists(Uri.parse(file.getFileUri()))) {
192                             return false;
193                           }
194                         }
195                         return true;
196                       }));
197         };
198 
199     mobileDataDownload =
200         builderForTest()
201             .setFileDownloaderSupplier(
202                 () ->
203                     new TestFileDownloader(
204                         TEST_DATA_RELATIVE_PATH,
205                         fileStorage,
206                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
207             .addFileGroupPopulator(new TestFileGroupPopulator(context))
208             .setCustomFileGroupValidatorOptional(Optional.of(validator))
209             .build();
210 
211     waitForHandleTask();
212 
213     ClientFileGroup clientFileGroup =
214         mobileDataDownload
215             .getFileGroup(GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
216             .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
217     verifyClientFile(clientFileGroup.getFileList().get(0), FILE_ID, FILE_SIZE);
218   }
219 
220   @Test
download_success_maintenanceLogsNetworkUsage()221   public void download_success_maintenanceLogsNetworkUsage() throws Exception {
222     flags.networkStatsLoggingSampleInterval = Optional.of(1);
223 
224     mobileDataDownload =
225         builderForTest()
226             .setFileDownloaderSupplier(
227                 () ->
228                     new TestFileDownloader(
229                         TEST_DATA_RELATIVE_PATH,
230                         fileStorage,
231                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
232             .addFileGroupPopulator(new TestFileGroupPopulator(context))
233             .build();
234 
235     waitForHandleTask();
236 
237     // This should flush the logs from NetworkLogger.
238     mobileDataDownload
239         .handleTask(TaskScheduler.MAINTENANCE_PERIODIC_TASK)
240         .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
241 
242     ClientFileGroup clientFileGroup = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
243     verifyClientFile(clientFileGroup.getFileList().get(0), FILE_ID, FILE_SIZE);
244 
245     ArgumentCaptor<MddLogData> logDataCaptor = ArgumentCaptor.forClass(MddLogData.class);
246     // 1056 is the tag number for MddClientEvent.Code.EVENT_CODE_UNSPECIFIED.
247     verify(mockLogger, times(1)).log(logDataCaptor.capture(), /* eventCode= */ eq(1056));
248 
249     List<MddLogData> logDataList = logDataCaptor.getAllValues();
250     assertThat(logDataList).hasSize(1);
251     MddLogData logData = logDataList.get(0);
252 
253     Void mddNetworkStats = null;
254 
255     // Network status changes depending on emulator:
256     boolean isCellular = NetworkUsageMonitor.isCellular(context);
257   }
258 
259   @Test
corrupted_files_detectedDuringMaintenance()260   public void corrupted_files_detectedDuringMaintenance() throws Exception {
261     flags.mddDefaultSampleInterval = Optional.of(1);
262 
263     mobileDataDownload =
264         builderForTest()
265             .setFileDownloaderSupplier(
266                 () ->
267                     new TestFileDownloader(
268                         TEST_DATA_RELATIVE_PATH,
269                         fileStorage,
270                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
271             .addFileGroupPopulator(new TestFileGroupPopulator(context))
272             .build();
273 
274     waitForHandleTask();
275 
276     ClientFileGroup clientFileGroup = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
277     fileStorage.open(
278         Uri.parse(clientFileGroup.getFile(0).getFileUri()), WriteStringOpener.create("c0rrupt3d"));
279 
280     // Bad file is detected during maintenance.
281     mobileDataDownload
282         .handleTask(TaskScheduler.MAINTENANCE_PERIODIC_TASK)
283         .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
284 
285     // File group is re-downloaded.
286     mobileDataDownload
287         .handleTask(TaskScheduler.CELLULAR_CHARGING_PERIODIC_TASK)
288         .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
289 
290     // Re-load the file group since the on-disk URIs will have changed.
291     clientFileGroup = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
292     assertThat(
293             fileStorage.open(
294                 Uri.parse(clientFileGroup.getFile(0).getFileUri()), ReadStringOpener.create()))
295         .isNotEqualTo("c0rrupt3d");
296   }
297 
298   @Test
delete_files_detectedDuringMaintenance()299   public void delete_files_detectedDuringMaintenance() throws Exception {
300     flags.mddDefaultSampleInterval = Optional.of(1);
301 
302     mobileDataDownload =
303         builderForTest()
304             .setFileDownloaderSupplier(
305                 () ->
306                     new TestFileDownloader(
307                         TEST_DATA_RELATIVE_PATH,
308                         fileStorage,
309                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
310             .addFileGroupPopulator(new TestFileGroupPopulator(context))
311             .build();
312 
313     waitForHandleTask();
314 
315     ClientFileGroup clientFileGroup = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
316     fileStorage.deleteFile(Uri.parse(clientFileGroup.getFile(0).getFileUri()));
317 
318     // Bad file is detected during maintenance.
319     mobileDataDownload
320         .handleTask(TaskScheduler.MAINTENANCE_PERIODIC_TASK)
321         .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
322 
323     // File group is re-downloaded.
324     mobileDataDownload
325         .handleTask(TaskScheduler.CELLULAR_CHARGING_PERIODIC_TASK)
326         .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
327 
328     // Re-load the file group since the on-disk URIs will have changed.
329     clientFileGroup = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
330     assertThat(fileStorage.exists(Uri.parse(clientFileGroup.getFile(0).getFileUri()))).isTrue();
331   }
332 
333   @Test
remove_withAccount_fileGroupRemains()334   public void remove_withAccount_fileGroupRemains() throws Exception {
335     mobileDataDownload =
336         builderForTest()
337             .setFileDownloaderSupplier(
338                 () ->
339                     new TestFileDownloader(
340                         TEST_DATA_RELATIVE_PATH,
341                         fileStorage,
342                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
343             .addFileGroupPopulator(new TestFileGroupPopulator(context))
344             .build();
345 
346     waitForHandleTask();
347 
348     // Remove the file group with account doesn't change anything, because the test group is not
349     // associated with any account.
350     Account account = AccountUtil.create("name", "google");
351     assertThat(account).isNotNull();
352     assertThat(
353             mobileDataDownload
354                 .removeFileGroup(
355                     RemoveFileGroupRequest.newBuilder()
356                         .setGroupName(FILE_GROUP_NAME)
357                         .setAccountOptional(Optional.of(account))
358                         .build())
359                 .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS))
360         .isTrue();
361 
362     ClientFileGroup clientFileGroup = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
363     verifyClientFile(clientFileGroup.getFileList().get(0), FILE_ID, FILE_SIZE);
364   }
365 
366   @Test
remove_withoutAccount_fileGroupRemoved()367   public void remove_withoutAccount_fileGroupRemoved() throws Exception {
368     mobileDataDownload =
369         builderForTest()
370             .setFileDownloaderSupplier(
371                 () ->
372                     new TestFileDownloader(
373                         TEST_DATA_RELATIVE_PATH,
374                         fileStorage,
375                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
376             .addFileGroupPopulator(new TestFileGroupPopulator(context))
377             .build();
378 
379     waitForHandleTask();
380 
381     // Remove the file group will make the file group not accessible from clients.
382     assertThat(
383             mobileDataDownload
384                 .removeFileGroup(
385                     RemoveFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
386                 .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS))
387         .isTrue();
388 
389     ClientFileGroup clientFileGroup =
390         mobileDataDownload
391             .getFileGroup(GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
392             .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
393     assertThat(clientFileGroup).isNull();
394   }
395 
396   @Test
removeFileGroupsByFilter_removesMatchingGroups()397   public void removeFileGroupsByFilter_removesMatchingGroups() throws Exception {
398     mobileDataDownload =
399         builderForTest()
400             .setFileDownloaderSupplier(
401                 () ->
402                     new TestFileDownloader(
403                         TEST_DATA_RELATIVE_PATH,
404                         fileStorage,
405                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
406             .build();
407 
408     // Remove All Groups to clear state
409     mobileDataDownload
410         .removeFileGroupsByFilter(RemoveFileGroupsByFilterRequest.newBuilder().build())
411         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
412 
413     // Tear down: remove remaining group to prevent cross test errors
414     mobileDataDownload
415         .removeFileGroupsByFilter(RemoveFileGroupsByFilterRequest.newBuilder().build())
416         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
417   }
418 
419   @Test
removeFileGroupsByFilter_whenAccountSpecified_removesMatchingAccountDependentGroups()420   public void removeFileGroupsByFilter_whenAccountSpecified_removesMatchingAccountDependentGroups()
421       throws Exception {
422     mobileDataDownload =
423         builderForTest()
424             .setFileDownloaderSupplier(
425                 () ->
426                     new TestFileDownloader(
427                         TEST_DATA_RELATIVE_PATH,
428                         fileStorage,
429                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
430             .build();
431 
432     // Remove all groups
433     mobileDataDownload
434         .removeFileGroupsByFilter(RemoveFileGroupsByFilterRequest.newBuilder().build())
435         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
436 
437     // Setup account
438     Account account = AccountUtil.create("name", "google");
439 
440     // Setup two groups, 1 with account and 1 without an account
441     DataFileGroup fileGroupWithoutAccount =
442         TestFileGroupPopulator.createDataFileGroup(
443                 FILE_GROUP_NAME,
444                 context.getPackageName(),
445                 new String[] {FILE_ID},
446                 new int[] {FILE_SIZE},
447                 new String[] {FILE_CHECKSUM},
448                 new String[] {FILE_URL},
449                 DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK)
450             .toBuilder()
451             .build();
452     DataFileGroup fileGroupWithAccount =
453         fileGroupWithoutAccount.toBuilder().setGroupName(FILE_GROUP_NAME + "_2").build();
454 
455     // Add both groups to MDD
456     mobileDataDownload
457         .addFileGroup(
458             AddFileGroupRequest.newBuilder().setDataFileGroup(fileGroupWithoutAccount).build())
459         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
460     mobileDataDownload
461         .addFileGroup(
462             AddFileGroupRequest.newBuilder()
463                 .setDataFileGroup(fileGroupWithAccount)
464                 .setAccountOptional(Optional.of(account))
465                 .build())
466         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
467 
468     // Verify that both groups are present
469     assertThat(
470             mobileDataDownload
471                 .getFileGroupsByFilter(
472                     GetFileGroupsByFilterRequest.newBuilder().setIncludeAllGroups(true).build())
473                 .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS))
474         .hasSize(2);
475 
476     // Remove file groups with given account and source
477     mobileDataDownload
478         .removeFileGroupsByFilter(
479             RemoveFileGroupsByFilterRequest.newBuilder()
480                 .setAccountOptional(Optional.of(account))
481                 .build())
482         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
483 
484     // Check that only account-independent group remains
485     ImmutableList<ClientFileGroup> remainingGroups =
486         mobileDataDownload
487             .getFileGroupsByFilter(
488                 GetFileGroupsByFilterRequest.newBuilder().setIncludeAllGroups(true).build())
489             .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
490     assertThat(remainingGroups).hasSize(1);
491     assertThat(remainingGroups.get(0).getGroupName()).isEqualTo(FILE_GROUP_NAME);
492 
493     // Tear down: remove remaining group to prevent cross test errors
494     mobileDataDownload
495         .removeFileGroupsByFilter(RemoveFileGroupsByFilterRequest.newBuilder().build())
496         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
497   }
498 
499   @Test
500   public void
removeFileGroupsByFilter_whenAccountNotSpecified_removesMatchingAccountIndependentGroups()501       removeFileGroupsByFilter_whenAccountNotSpecified_removesMatchingAccountIndependentGroups()
502           throws Exception {
503     mobileDataDownload =
504         builderForTest()
505             .setFileDownloaderSupplier(
506                 () ->
507                     new TestFileDownloader(
508                         TEST_DATA_RELATIVE_PATH,
509                         fileStorage,
510                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
511             .build();
512 
513     waitForHandleTask();
514 
515     // Remove all groups
516     mobileDataDownload
517         .removeFileGroupsByFilter(RemoveFileGroupsByFilterRequest.newBuilder().build())
518         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
519 
520     // Setup account
521     Account account = AccountUtil.create("name", "google");
522 
523     // Setup two groups, 1 with account and 1 without an account
524     DataFileGroup fileGroupWithoutAccount =
525         TestFileGroupPopulator.createDataFileGroup(
526                 FILE_GROUP_NAME,
527                 context.getPackageName(),
528                 new String[] {FILE_ID},
529                 new int[] {FILE_SIZE},
530                 new String[] {FILE_CHECKSUM},
531                 new String[] {FILE_URL},
532                 DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK)
533             .toBuilder()
534             .build();
535     DataFileGroup fileGroupWithAccount =
536         fileGroupWithoutAccount.toBuilder().setGroupName(FILE_GROUP_NAME + "_2").build();
537 
538     // Add both groups to MDD
539     mobileDataDownload
540         .addFileGroup(
541             AddFileGroupRequest.newBuilder().setDataFileGroup(fileGroupWithoutAccount).build())
542         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
543     mobileDataDownload
544         .addFileGroup(
545             AddFileGroupRequest.newBuilder()
546                 .setDataFileGroup(fileGroupWithAccount)
547                 .setAccountOptional(Optional.of(account))
548                 .build())
549         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
550 
551     // Verify that both groups are present
552     assertThat(
553             mobileDataDownload
554                 .getFileGroupsByFilter(
555                     GetFileGroupsByFilterRequest.newBuilder().setIncludeAllGroups(true).build())
556                 .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS))
557         .hasSize(2);
558 
559     // Remove file groups with given source only
560     mobileDataDownload
561         .removeFileGroupsByFilter(RemoveFileGroupsByFilterRequest.newBuilder().build())
562         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
563 
564     // Check that only account-dependent group remains
565     ImmutableList<ClientFileGroup> remainingGroups =
566         mobileDataDownload
567             .getFileGroupsByFilter(
568                 GetFileGroupsByFilterRequest.newBuilder().setIncludeAllGroups(true).build())
569             .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
570     assertThat(remainingGroups).hasSize(1);
571     assertThat(remainingGroups.get(0).getGroupName()).isEqualTo(FILE_GROUP_NAME + "_2");
572 
573     // Tear down: remove remaining group to prevent cross test errors
574     mobileDataDownload
575         .removeFileGroupsByFilter(
576             RemoveFileGroupsByFilterRequest.newBuilder()
577                 .setAccountOptional(Optional.of(account))
578                 .build())
579         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
580   }
581 
582   @Test
download_failure_throwsDownloadException()583   public void download_failure_throwsDownloadException() throws Exception {
584     flags.mddDefaultSampleInterval = Optional.of(1);
585 
586     mobileDataDownload =
587         builderForTest()
588             .setFileDownloaderSupplier(
589                 () ->
590                     new TestFileDownloader(
591                         TEST_DATA_RELATIVE_PATH,
592                         fileStorage,
593                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
594             .addFileGroupPopulator(new TestFileGroupPopulator(context))
595             .build();
596 
597     waitForHandleTask();
598 
599     DataFileGroup dataFileGroup =
600         TestFileGroupPopulator.createDataFileGroup(
601             FILE_GROUP_NAME,
602             context.getPackageName(),
603             new String[] {"one", "two"},
604             new int[] {1000, 2000},
605             new String[] {"checksum1", "checksum2"},
606             new String[] {
607               "http://www.gstatic.com/", // This url is not secure.
608               "https://www.gstatic.com/does_not_exist" // This url does not exist.
609             },
610             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
611 
612     assertThat(
613             mobileDataDownload
614                 .addFileGroup(
615                     AddFileGroupRequest.newBuilder().setDataFileGroup(dataFileGroup).build())
616                 .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS))
617         .isTrue();
618 
619     ListenableFuture<ClientFileGroup> downloadFuture =
620         mobileDataDownload.downloadFileGroup(
621             DownloadFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build());
622 
623     ExecutionException exception = assertThrows(ExecutionException.class, downloadFuture::get);
624     assertThat(exception).hasCauseThat().isInstanceOf(AggregateException.class);
625     AggregateException cause = (AggregateException) exception.getCause();
626     assertThat(cause).isNotNull();
627     ImmutableList<Throwable> failures = cause.getFailures();
628     assertThat(failures).hasSize(2);
629     assertThat(failures.get(0)).isInstanceOf(DownloadException.class);
630     assertThat(failures.get(1)).isInstanceOf(DownloadException.class);
631   }
632 
633   @Test
download_failure_logsEvent()634   public void download_failure_logsEvent() throws Exception {
635     flags.mddDefaultSampleInterval = Optional.of(1);
636 
637     mobileDataDownload =
638         builderForTest()
639             .setFileDownloaderSupplier(
640                 () ->
641                     new TestFileDownloader(
642                         TEST_DATA_RELATIVE_PATH,
643                         fileStorage,
644                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
645             .addFileGroupPopulator(new TestFileGroupPopulator(context))
646             .build();
647 
648     DataFileGroup dataFileGroup =
649         TestFileGroupPopulator.createDataFileGroup(
650             FILE_GROUP_NAME,
651             context.getPackageName(),
652             new String[] {"one", "two"},
653             new int[] {1000, 2000},
654             new String[] {"checksum1", "checksum2"},
655             new String[] {
656               "http://www.gstatic.com/", // This url is not secure.
657               "https://www.gstatic.com/does_not_exist" // This url does not exist.
658             },
659             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
660 
661     assertThat(
662             mobileDataDownload
663                 .addFileGroup(
664                     AddFileGroupRequest.newBuilder().setDataFileGroup(dataFileGroup).build())
665                 .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS))
666         .isTrue();
667 
668     ListenableFuture<ClientFileGroup> downloadFuture =
669         mobileDataDownload.downloadFileGroup(
670             DownloadFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build());
671 
672     assertThrows(ExecutionException.class, downloadFuture::get);
673 
674     if (controlExecutorType.equals(ExecutorType.SINGLE_THREADED)) {
675       // Single-threaded executor step requires some time to allow logging to finish.
676       // TODO: Investigate whether TestingTaskBarrier can be used here to wait for
677       // executor become idle.
678       Thread.sleep(500);
679     }
680 
681     ArgumentCaptor<MddLogData> logDataCaptor = ArgumentCaptor.forClass(MddLogData.class);
682     // 1068 is the tag number for MddClientEvent.Code.DATA_DOWNLOAD_RESULT_LOG.
683     verify(mockLogger, times(2)).log(logDataCaptor.capture(), /* eventCode= */ eq(1068));
684 
685     List<MddLogData> logData = logDataCaptor.getAllValues();
686     assertThat(logData).hasSize(2);
687 
688     MddDownloadResultLog downloadResultLog1 = logData.get(0).getMddDownloadResultLog();
689     MddDownloadResultLog downloadResultLog2 = logData.get(1).getMddDownloadResultLog();
690     assertThat(downloadResultLog1.getResult()).isEqualTo(MddDownloadResult.Code.INSECURE_URL_ERROR);
691     assertThat(downloadResultLog1.getDataDownloadFileGroupStats().getFileGroupName())
692         .isEqualTo(FILE_GROUP_NAME);
693     assertThat(downloadResultLog1.getDataDownloadFileGroupStats().getOwnerPackage())
694         .isEqualTo(context.getPackageName());
695     assertThat(downloadResultLog2.getResult())
696         .isEqualTo(MddDownloadResult.Code.ANDROID_DOWNLOADER_HTTP_ERROR);
697     assertThat(downloadResultLog2.getDataDownloadFileGroupStats().getFileGroupName())
698         .isEqualTo(FILE_GROUP_NAME);
699     assertThat(downloadResultLog2.getDataDownloadFileGroupStats().getOwnerPackage())
700         .isEqualTo(context.getPackageName());
701   }
702 
703   @Test
download_zipFile_unzippedAfterDownload()704   public void download_zipFile_unzippedAfterDownload() throws Exception {
705     mobileDataDownload =
706         builderForTest()
707             .setFileDownloaderSupplier(
708                 () ->
709                     new TestFileDownloader(
710                         TEST_DATA_RELATIVE_PATH,
711                         fileStorage,
712                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
713             .addFileGroupPopulator(new ZipFolderFileGroupPopulator(context))
714             .build();
715 
716     waitForHandleTask();
717 
718     ClientFileGroup clientFileGroup =
719         getAndVerifyClientFileGroup(ZipFolderFileGroupPopulator.FILE_GROUP_NAME, 3);
720 
721     for (ClientFile clientFile : clientFileGroup.getFileList()) {
722       if ("/zip1.txt".equals(clientFile.getFileId())) {
723         verifyClientFile(clientFile, "/zip1.txt", 11);
724       } else if ("/zip2.txt".equals(clientFile.getFileId())) {
725         verifyClientFile(clientFile, "/zip2.txt", 11);
726       } else if ("/sub_folder/zip3.txt".equals(clientFile.getFileId())) {
727         verifyClientFile(clientFile, "/sub_folder/zip3.txt", 25);
728       } else {
729         fail("Unexpect file:" + clientFile.getFileId());
730       }
731     }
732   }
733 
734   @Test
download_cancelDuringDownload_downloadCancelled()735   public void download_cancelDuringDownload_downloadCancelled() throws Exception {
736     BlockingFileDownloader blockingFileDownloader = new BlockingFileDownloader(DOWNLOAD_EXECUTOR);
737 
738     Supplier<FileDownloader> fakeFileDownloaderSupplier = () -> blockingFileDownloader;
739 
740     mobileDataDownload =
741         builderForTest().setFileDownloaderSupplier(fakeFileDownloaderSupplier).build();
742 
743     // Register the file group and trigger download.
744     mobileDataDownload
745         .addFileGroup(
746             AddFileGroupRequest.newBuilder()
747                 .setDataFileGroup(
748                     TestFileGroupPopulator.createDataFileGroup(
749                         FILE_GROUP_NAME,
750                         context.getPackageName(),
751                         new String[] {FILE_ID},
752                         new int[] {FILE_SIZE},
753                         new String[] {FILE_CHECKSUM},
754                         new String[] {FILE_URL},
755                         DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK))
756                 .build())
757         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
758     ListenableFuture<ClientFileGroup> downloadFuture =
759         mobileDataDownload.downloadFileGroup(
760             DownloadFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build());
761 
762     // Wait for download to be scheduled. The future shouldn't be done yet.
763     blockingFileDownloader.waitForDownloadStarted();
764     assertThat(downloadFuture.isDone()).isFalse();
765 
766     // Now remove the file group from MDD, which would cancel any ongoing download.
767     mobileDataDownload
768         .removeFileGroup(RemoveFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
769         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
770     // Now let the download future finish.
771     blockingFileDownloader.finishDownloading();
772 
773     // Make sure that the download has been canceled and leads to cancelled future.
774     ExecutionException exception =
775         assertThrows(
776             ExecutionException.class,
777             () -> downloadFuture.get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS));
778     assertThat(exception).hasCauseThat().isInstanceOf(AggregateException.class);
779     AggregateException cause = (AggregateException) exception.getCause();
780     assertThat(cause).isNotNull();
781     ImmutableList<Throwable> failures = cause.getFailures();
782     assertThat(failures).hasSize(1);
783     assertThat(failures.get(0)).isInstanceOf(CancellationException.class);
784   }
785 
786   @Test
download_twoStepDownload_targetFileDownloaded()787   public void download_twoStepDownload_targetFileDownloaded() throws Exception {
788     mobileDataDownload =
789         builderForTest()
790             .setFileDownloaderSupplier(
791                 () ->
792                     new TestFileDownloader(
793                         TEST_DATA_RELATIVE_PATH,
794                         fileStorage,
795                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
796             .addFileGroupPopulator(new TwoStepPopulator(context, fileStorage))
797             .build();
798 
799     // Add step1 file group to MDD.
800     DataFileGroup step1FileGroup =
801         TestFileGroupPopulator.createDataFileGroup(
802             "step1-file-group",
803             context.getPackageName(),
804             new String[] {"step1_id"},
805             new int[] {57},
806             new String[] {""},
807             new String[] {"https://www.gstatic.com/icing/idd/sample_group/step1.txt"},
808             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
809 
810     ListenableFuture<Boolean> unused =
811         mobileDataDownload.addFileGroup(
812             AddFileGroupRequest.newBuilder().setDataFileGroup(step1FileGroup).build());
813 
814     // This will trigger refreshing of FileGroupPopulators and downloading.
815     mobileDataDownload
816         .handleTask(TaskScheduler.CELLULAR_CHARGING_PERIODIC_TASK)
817         .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
818 
819     // Now verify that the step1-file-group is downloaded and then TwoStepPopulator will add
820     // step2-file-group and it was downloaded too in one cycle (one call of handleTask).
821 
822     // Verify step1-file-group.
823     ClientFileGroup clientFileGroup = getAndVerifyClientFileGroup("step1-file-group", 1);
824     verifyClientFile(clientFileGroup.getFile(0), "step1_id", 57);
825 
826     // Verify step2-file-group.
827     clientFileGroup = getAndVerifyClientFileGroup("step2-file-group", 1);
828     verifyClientFile(clientFileGroup.getFile(0), "step2_id", 13);
829   }
830 
831   @Test
download_relativeFilePaths_createsSymlinks()832   public void download_relativeFilePaths_createsSymlinks() throws Exception {
833     AndroidUriAdapter adapter = AndroidUriAdapter.forContext(context);
834     mobileDataDownload =
835         builderForTest()
836             .setFileDownloaderSupplier(
837                 () ->
838                     new TestFileDownloader(
839                         TEST_DATA_RELATIVE_PATH,
840                         fileStorage,
841                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
842             .build();
843 
844     DataFileGroup fileGroup =
845         DataFileGroup.newBuilder()
846             .setGroupName(FILE_GROUP_NAME)
847             .setOwnerPackage(context.getPackageName())
848             .setPreserveFilenamesAndIsolateFiles(true)
849             .addFile(
850                 DataFile.newBuilder()
851                     .setFileId(FILE_ID)
852                     .setByteSize(FILE_SIZE)
853                     .setChecksumType(DataFile.ChecksumType.DEFAULT)
854                     .setChecksum(FILE_CHECKSUM)
855                     .setUrlToDownload(FILE_URL)
856                     .setRelativeFilePath("relative_path")
857                     .build())
858             .build();
859 
860     mobileDataDownload
861         .addFileGroup(AddFileGroupRequest.newBuilder().setDataFileGroup(fileGroup).build())
862         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
863 
864     mobileDataDownload
865         .downloadFileGroup(
866             DownloadFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
867         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
868 
869     // verify symlink structure, we can't get access to the full internal file uri, but we can tell
870     // the start of it
871     Uri expectedFileUri =
872         DirectoryUtil.getBaseDownloadDirectory(context, Optional.absent())
873             .buildUpon()
874             .appendPath(DirectoryUtil.MDD_STORAGE_SYMLINKS)
875             .appendPath(DirectoryUtil.MDD_STORAGE_ALL_GOOGLE_APPS)
876             .appendPath(FILE_GROUP_NAME)
877             .build();
878     // we can't get access to the full internal target file uri, but we know the start of it
879     Uri expectedStartTargetUri =
880         DirectoryUtil.getBaseDownloadDirectory(context, Optional.absent())
881             .buildUpon()
882             .appendPath(DirectoryUtil.MDD_STORAGE_ALL_GOOGLE_APPS)
883             .appendPath("datadownloadfile_")
884             .build();
885 
886     ClientFileGroup clientFileGroup =
887         mobileDataDownload
888             .getFileGroup(GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
889             .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
890 
891     Uri fileUri = Uri.parse(clientFileGroup.getFile(0).getFileUri());
892     Uri targetUri =
893         AndroidUri.builder(context)
894             .fromAbsolutePath(readlink(adapter.toFile(fileUri).getAbsolutePath()))
895             .build();
896 
897     assertThat(fileUri.toString()).contains(expectedFileUri.toString());
898     assertThat(targetUri.toString()).contains(expectedStartTargetUri.toString());
899     assertThat(fileStorage.exists(fileUri)).isTrue();
900     assertThat(fileStorage.exists(targetUri)).isTrue();
901   }
902 
903   @Test
remove_relativeFilePaths_removesSymlinks()904   public void remove_relativeFilePaths_removesSymlinks() throws Exception {
905     mobileDataDownload =
906         builderForTest()
907             .setFileDownloaderSupplier(
908                 () ->
909                     new TestFileDownloader(
910                         TEST_DATA_RELATIVE_PATH,
911                         fileStorage,
912                         MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR)))
913             .build();
914 
915     DataFileGroup fileGroup =
916         DataFileGroup.newBuilder()
917             .setGroupName(FILE_GROUP_NAME)
918             .setOwnerPackage(context.getPackageName())
919             .setPreserveFilenamesAndIsolateFiles(true)
920             .addFile(
921                 DataFile.newBuilder()
922                     .setFileId(FILE_ID)
923                     .setByteSize(FILE_SIZE)
924                     .setChecksumType(DataFile.ChecksumType.DEFAULT)
925                     .setChecksum(FILE_CHECKSUM)
926                     .setUrlToDownload(FILE_URL)
927                     .setRelativeFilePath("relative_path")
928                     .build())
929             .build();
930 
931     mobileDataDownload
932         .addFileGroup(AddFileGroupRequest.newBuilder().setDataFileGroup(fileGroup).build())
933         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
934 
935     mobileDataDownload
936         .downloadFileGroup(
937             DownloadFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
938         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
939 
940     ClientFileGroup clientFileGroup =
941         mobileDataDownload
942             .getFileGroup(GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
943             .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
944 
945     Uri fileUri = Uri.parse(clientFileGroup.getFile(0).getFileUri());
946 
947     // Verify that file uri gets created
948     assertThat(fileStorage.exists(fileUri)).isTrue();
949 
950     mobileDataDownload
951         .removeFileGroup(RemoveFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_NAME).build())
952         .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
953 
954     // Verify that file uri still exists even though file group is stale
955     assertThat(fileStorage.exists(fileUri)).isTrue();
956 
957     mobileDataDownload.maintenance().get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
958 
959     // Verify that file uri gets removed, once maintenance runs
960     if (flags.mddEnableGarbageCollection()) {
961       // cl/439051122 created a temporary FALSE override targeted to ASGA devices. This test only
962       // makes sense if the flag is true, but all_on testing doesn't respect diversion criteria in
963       // the launch. So we skip it for now.
964       // TODO(b/226551373): remove this once AsgaDisableMddLibGcLaunch is turned down
965       assertThat(fileStorage.exists(fileUri)).isFalse();
966     }
967   }
968 
969   @Test
handleTask_duplicateInvocations_logsDownloadCompleteOnce()970   public void handleTask_duplicateInvocations_logsDownloadCompleteOnce() throws Exception {
971     // Override the feature flag to log at 100%.
972     flags.mddDefaultSampleInterval = Optional.of(1);
973 
974     TestFileDownloader testFileDownloader =
975         new TestFileDownloader(
976             TEST_DATA_RELATIVE_PATH,
977             fileStorage,
978             MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR));
979     BlockingFileDownloader blockingFileDownloader =
980         new BlockingFileDownloader(DOWNLOAD_EXECUTOR, testFileDownloader);
981 
982     Supplier<FileDownloader> fakeFileDownloaderSupplier = () -> blockingFileDownloader;
983 
984     mobileDataDownload =
985         builderForTest().setFileDownloaderSupplier(fakeFileDownloaderSupplier).build();
986 
987     // Use test populator to add the group as pending.
988     TestFileGroupPopulator populator = new TestFileGroupPopulator(context);
989     populator.refreshFileGroups(mobileDataDownload).get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
990 
991     // Call handle task in non-blocking way and use blocking file downloader to let handleTask1 wait
992     // at the download stage
993     ListenableFuture<Void> handleTask1Future =
994         mobileDataDownload.handleTask(TaskScheduler.WIFI_CHARGING_PERIODIC_TASK);
995 
996     blockingFileDownloader.waitForDownloadStarted();
997 
998     ListenableFuture<Void> handleTask2Future =
999         mobileDataDownload.handleTask(TaskScheduler.CELLULAR_CHARGING_PERIODIC_TASK);
1000 
1001     // Trigger a complete so the download "completes" after both tasks have been started.
1002     blockingFileDownloader.finishDownloading();
1003 
1004     // Wait for both futures to complete so we can make assertions about the events logged
1005     handleTask2Future.get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
1006     handleTask1Future.get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
1007 
1008     // Check that group is downloaded.
1009     ClientFileGroup unused = getAndVerifyClientFileGroup(FILE_GROUP_NAME, 1);
1010 
1011     if (controlExecutorType.equals(ExecutorType.SINGLE_THREADED)) {
1012       // Single-threaded executor step requires some time to allow logging to finish.
1013       // TODO: Investigate whether TestingTaskBarrier can be used here to wait for
1014       // executor become idle.
1015       Thread.sleep(500);
1016     }
1017 
1018     // Check that logger only logged 1 download complete event
1019     ArgumentCaptor<MddLogData> logDataCompleteCaptor = ArgumentCaptor.forClass(MddLogData.class);
1020     // 1007 is the tag number for MddClientEvent.Code.EVENT_CODE_UNSPECIFIED.
1021     verify(mockLogger, times(1)).log(logDataCompleteCaptor.capture(), /* eventCode= */ eq(1007));
1022   }
1023 
builderForTest()1024   private MobileDataDownloadBuilder builderForTest() {
1025     return MobileDataDownloadBuilder.newBuilder()
1026         .setContext(context)
1027         .setControlExecutor(controlExecutor)
1028         .setTaskScheduler(Optional.of(mockTaskScheduler))
1029         .setLoggerOptional(Optional.of(mockLogger))
1030         .setDeltaDecoderOptional(Optional.absent())
1031         .setFileStorage(fileStorage)
1032         .setNetworkUsageMonitor(networkUsageMonitor)
1033         .setFlagsOptional(Optional.of(flags));
1034   }
1035 
1036   /** Creates MDD object and triggers handleTask to refresh and download file groups. */
waitForHandleTask()1037   private void waitForHandleTask()
1038       throws InterruptedException, ExecutionException, TimeoutException {
1039     // This will trigger refreshing of FileGroupPopulators and downloading.
1040     mobileDataDownload
1041         .handleTask(TaskScheduler.CELLULAR_CHARGING_PERIODIC_TASK)
1042         .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS);
1043 
1044     String debugString = mobileDataDownload.getDebugInfoAsString();
1045     Log.i(TAG, "MDD Lib dump:");
1046     for (String line : debugString.split("\n", -1)) {
1047       Log.i(TAG, line);
1048     }
1049   }
1050 
getAndVerifyClientFileGroup(String fileGroupName, int fileCount)1051   private ClientFileGroup getAndVerifyClientFileGroup(String fileGroupName, int fileCount)
1052       throws ExecutionException, TimeoutException, InterruptedException {
1053     ClientFileGroup clientFileGroup =
1054         mobileDataDownload
1055             .getFileGroup(GetFileGroupRequest.newBuilder().setGroupName(fileGroupName).build())
1056             .get(MAX_MDD_API_WAIT_TIME_SECS, SECONDS);
1057     assertThat(clientFileGroup).isNotNull();
1058     assertThat(clientFileGroup.getGroupName()).isEqualTo(fileGroupName);
1059     assertThat(clientFileGroup.getFileCount()).isEqualTo(fileCount);
1060 
1061     return clientFileGroup;
1062   }
1063 
verifyClientFile(ClientFile clientFile, String fileId, int fileSize)1064   private void verifyClientFile(ClientFile clientFile, String fileId, int fileSize)
1065       throws IOException {
1066     assertThat(clientFile.getFileId()).isEqualTo(fileId);
1067     Uri androidUri = Uri.parse(clientFile.getFileUri());
1068     assertThat(fileStorage.fileSize(androidUri)).isEqualTo(fileSize);
1069   }
1070 }
1071