• 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 com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_CHECKSUM;
19 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_ID;
20 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_SIZE;
21 import static com.google.android.libraries.mobiledatadownload.TestFileGroupPopulator.FILE_URL;
22 import static com.google.android.libraries.mobiledatadownload.testing.MddTestDependencies.ExecutorType;
23 import static com.google.common.truth.Truth.assertThat;
24 import static java.util.concurrent.TimeUnit.SECONDS;
25 import static org.mockito.Mockito.eq;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 
29 import android.app.blob.BlobStoreManager;
30 import android.content.Context;
31 import android.net.Uri;
32 import android.util.Log;
33 import androidx.test.core.app.ApplicationProvider;
34 import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader;
35 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
36 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidFileBackend;
37 import com.google.android.libraries.mobiledatadownload.file.backends.BlobStoreBackend;
38 import com.google.android.libraries.mobiledatadownload.file.backends.BlobUri;
39 import com.google.android.libraries.mobiledatadownload.file.backends.JavaFileBackend;
40 import com.google.android.libraries.mobiledatadownload.file.transforms.CompressTransform;
41 import com.google.android.libraries.mobiledatadownload.monitor.DownloadProgressMonitor;
42 import com.google.android.libraries.mobiledatadownload.monitor.NetworkUsageMonitor;
43 import com.google.android.libraries.mobiledatadownload.testing.TestFileDownloader;
44 import com.google.android.libraries.mobiledatadownload.testing.TestFlags;
45 import com.google.common.base.Optional;
46 import com.google.common.base.Supplier;
47 import com.google.common.collect.ImmutableList;
48 import com.google.common.io.BaseEncoding;
49 import com.google.common.util.concurrent.ListeningExecutorService;
50 import com.google.common.util.concurrent.MoreExecutors;
51 import com.google.mobiledatadownload.ClientConfigProto.ClientFile;
52 import com.google.mobiledatadownload.ClientConfigProto.ClientFileGroup;
53 import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup;
54 import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions.DeviceNetworkPolicy;
55 import com.google.mobiledatadownload.LogProto.MddLogData;
56 import com.google.testing.junit.testparameterinjector.TestParameter;
57 import com.google.testing.junit.testparameterinjector.TestParameterInjector;
58 import java.io.OutputStream;
59 import java.security.MessageDigest;
60 import java.util.Calendar;
61 import java.util.List;
62 import java.util.concurrent.Executors;
63 import java.util.concurrent.ScheduledExecutorService;
64 import org.junit.After;
65 import org.junit.Before;
66 import org.junit.Rule;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 import org.mockito.ArgumentCaptor;
70 import org.mockito.Mock;
71 import org.mockito.junit.MockitoJUnit;
72 import org.mockito.junit.MockitoRule;
73 
74 @RunWith(TestParameterInjector.class)
75 public class DownloadFileGroupAndroidSharingIntegrationTest {
76 
77   private static final String TAG = "DownloadFileGroupIntegrationTest";
78   private static final int MAX_DOWNLOAD_FILE_GROUP_WAIT_TIME_SECS = 300;
79 
80   private static final String TEST_DATA_RELATIVE_PATH =
81       "third_party/java_src/android_libs/mobiledatadownload/javatests/com/google/android/libraries/mobiledatadownload/testdata/";
82 
83   private static final ScheduledExecutorService DOWNLOAD_EXECUTOR =
84       Executors.newScheduledThreadPool(2);
85 
86   private static final String FILE_GROUP_TO_SHARE_1 = "test-group-1";
87   private static final String FILE_GROUP_TO_SHARE_2 = "test-group-2";
88 
89   private static final String FILE_ID_1 = "test-file-to-share-1";
90   private static final String FILE_CHECKSUM_1 = "fcc96b272633cdf6c4bbd2d77512cca51bfb1dbd"; // SHA_1
91   static final String FILE_ANDROID_SHARING_CHECKSUM_1 =
92       "225017b5d5ec35732940af813b1ab7be5191e4c52659953e75a1a36a1398c48d"; // SHA_256
93   static final int FILE_SIZE_1 = 57;
94   static final String FILE_URL_1 = "https://www.gstatic.com/icing/idd/sample_group/step1.txt";
95 
96   private static final String FILE_ID_2 = "test-file-to-share-2";
97   private static final String FILE_CHECKSUM_2 = "22d565c9511c5752baab8a3bbf7b955bd2ca66fd"; // SHA_1
98   static final String FILE_ANDROID_SHARING_CHECKSUM_2 =
99       "98863d56d683f6f1fdf17b38873a481f47a3216e05314750f9b384220af418ab"; // SHA_256
100   static final int FILE_SIZE_2 = 13;
101   static final String FILE_URL_2 = "https://www.gstatic.com/icing/idd/sample_group/step2.txt";
102 
103   private static final Context context = ApplicationProvider.getApplicationContext();
104 
105   @Mock private TaskScheduler mockTaskScheduler;
106   @Mock private NetworkUsageMonitor mockNetworkUsageMonitor;
107   @Mock private DownloadProgressMonitor mockDownloadProgressMonitor;
108   @Mock private Logger mockLogger;
109 
110   private SynchronousFileStorage fileStorage;
111   private BlobStoreBackend blobStoreBackend;
112   private BlobStoreManager blobStoreManager;
113   private MobileDataDownload mobileDataDownload;
114   private ListeningExecutorService controlExecutor;
115 
116   private final TestFlags flags = new TestFlags();
117 
118   @Rule public final MockitoRule mocks = MockitoJUnit.rule();
119 
120   // TODO(b/226405643): Some tests seem to fail due to BlobStore not clearing out files across runs.
121   // Investigate why this is happening and enable single-threaded tests.
122   @TestParameter({"MULTI_THREADED"})
123   ExecutorType controlExecutorType;
124 
125   @Before
setUp()126   public void setUp() throws Exception {
127 
128     flags.mddAndroidSharingSampleInterval = Optional.of(1);
129 
130     flags.mddDefaultSampleInterval = Optional.of(1);
131 
132     blobStoreBackend = new BlobStoreBackend(context);
133     blobStoreManager = (BlobStoreManager) context.getSystemService(Context.BLOB_STORE_SERVICE);
134 
135     fileStorage =
136         new SynchronousFileStorage(
137             /* backends= */ ImmutableList.of(
138                 AndroidFileBackend.builder(context).build(),
139                 blobStoreBackend,
140                 new JavaFileBackend()),
141             /* transforms= */ ImmutableList.of(new CompressTransform()),
142             /* monitors= */ ImmutableList.of(mockNetworkUsageMonitor, mockDownloadProgressMonitor));
143 
144     controlExecutor = controlExecutorType.executor();
145   }
146 
147   @After
tearDown()148   public void tearDown() throws Exception {
149     mobileDataDownload.clear().get();
150   }
151 
computeDigest(byte[] byteContent, String algorithm)152   private static String computeDigest(byte[] byteContent, String algorithm) throws Exception {
153     MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
154     if (messageDigest == null) {
155       return "";
156     }
157     return BaseEncoding.base16().lowerCase().encode(messageDigest.digest(byteContent));
158   }
159 
160   @Test
oneAndroidSharedFile_blobStoreBackendNotRegistered_fileDownloadedAndStoredLocally()161   public void oneAndroidSharedFile_blobStoreBackendNotRegistered_fileDownloadedAndStoredLocally()
162       throws Exception {
163     fileStorage =
164         new SynchronousFileStorage(
165             /* backends= */ ImmutableList.of(
166                 AndroidFileBackend.builder(context).build(), new JavaFileBackend()),
167             /* transforms= */ ImmutableList.of(new CompressTransform()),
168             /* monitors= */ ImmutableList.of(mockNetworkUsageMonitor, mockDownloadProgressMonitor));
169 
170     mobileDataDownload = builderForTest().setFileStorage(fileStorage).build();
171 
172     Uri androidUri =
173         BlobUri.builder(context).setBlobParameters(FILE_ANDROID_SHARING_CHECKSUM_1).build();
174 
175     // A file group with one android-shared file and one non-androidShared file
176     DataFileGroup groupWithFileToShare =
177         TestFileGroupPopulator.createDataFileGroup(
178             FILE_GROUP_TO_SHARE_1,
179             context.getPackageName(),
180             new String[] {FILE_ID_1, FILE_ID},
181             new int[] {FILE_SIZE_1, FILE_SIZE},
182             new String[] {FILE_CHECKSUM_1, FILE_CHECKSUM},
183             new String[] {FILE_ANDROID_SHARING_CHECKSUM_1, ""},
184             new String[] {FILE_URL_1, FILE_URL},
185             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
186 
187     assertThat(
188             mobileDataDownload
189                 .addFileGroup(
190                     AddFileGroupRequest.newBuilder().setDataFileGroup(groupWithFileToShare).build())
191                 .get())
192         .isTrue();
193 
194     mobileDataDownload
195         .downloadFileGroup(
196             DownloadFileGroupRequest.newBuilder()
197                 .setGroupName(FILE_GROUP_TO_SHARE_1)
198                 .setListenerOptional(
199                     Optional.of(
200                         new DownloadListener() {
201                           @Override
202                           public void onProgress(long currentSize) {
203                             Log.i(TAG, "onProgress " + currentSize);
204                           }
205 
206                           @Override
207                           public void onComplete(ClientFileGroup clientFileGroup) {
208                             Log.i(TAG, "onComplete " + clientFileGroup.getGroupName());
209                           }
210                         }))
211                 .build())
212         .get(MAX_DOWNLOAD_FILE_GROUP_WAIT_TIME_SECS, SECONDS);
213 
214     ClientFileGroup clientFileGroup =
215         mobileDataDownload
216             .getFileGroup(
217                 GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_TO_SHARE_1).build())
218             .get();
219 
220     assertThat(clientFileGroup).isNotNull();
221     assertThat(clientFileGroup.getGroupName()).isEqualTo(FILE_GROUP_TO_SHARE_1);
222     assertThat(clientFileGroup.getFileCount()).isEqualTo(2);
223 
224     ClientFile clientFile = clientFileGroup.getFileList().get(0);
225     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID_1);
226     Uri uri = Uri.parse(clientFile.getFileUri());
227     assertThat(uri).isNotEqualTo(androidUri);
228     assertThat(fileStorage.fileSize(uri)).isEqualTo(FILE_SIZE_1);
229 
230     clientFile = clientFileGroup.getFileList().get(1);
231     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID);
232     uri = Uri.parse(clientFile.getFileUri());
233     assertThat(fileStorage.fileSize(uri)).isEqualTo(FILE_SIZE);
234 
235     ArgumentCaptor<MddLogData> logDataCaptor = ArgumentCaptor.forClass(MddLogData.class);
236     // 1073 is the tag number for MddClientEvent.Code.EVENT_CODE_UNSPECIFIED.
237     verify(mockLogger, times(2)).log(logDataCaptor.capture(), /* eventCode= */ eq(1073));
238 
239     List<MddLogData> logData = logDataCaptor.getAllValues();
240     Void log1 = null;
241     Void log2 = null;
242     assertThat(logData).hasSize(2);
243 
244     Void androidSharingLog = null;
245     assertThat(log1).isEqualTo(androidSharingLog);
246     assertThat(log2).isEqualTo(androidSharingLog);
247   }
248 
249   @Test
oneAndroidSharedFile_twoFileGroups_downloadedOnlyOnce()250   public void oneAndroidSharedFile_twoFileGroups_downloadedOnlyOnce() throws Exception {
251     mobileDataDownload = builderForTest().build();
252 
253     Uri androidUri =
254         BlobUri.builder(context).setBlobParameters(FILE_ANDROID_SHARING_CHECKSUM_1).build();
255     assertThat(fileStorage.exists(androidUri)).isFalse();
256 
257     // groupWithFileToShare1 and groupWithFileToShare2 contain the same file configured to be
258     // shared.
259     DataFileGroup groupWithFileToShare1 =
260         TestFileGroupPopulator.createDataFileGroup(
261             FILE_GROUP_TO_SHARE_1,
262             context.getPackageName(),
263             new String[] {FILE_ID_1},
264             new int[] {FILE_SIZE_1},
265             new String[] {FILE_CHECKSUM_1},
266             new String[] {FILE_ANDROID_SHARING_CHECKSUM_1},
267             new String[] {FILE_URL_1},
268             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
269 
270     long oneDayLaterInSeconds =
271         Calendar.getInstance().getTimeInMillis() / 1000 + 86400; // in one day
272     groupWithFileToShare1 =
273         groupWithFileToShare1.toBuilder().setExpirationDate(oneDayLaterInSeconds).build();
274 
275     DataFileGroup groupWithFileToShare2 =
276         TestFileGroupPopulator.createDataFileGroup(
277             FILE_GROUP_TO_SHARE_2,
278             context.getPackageName(),
279             new String[] {FILE_ID_1},
280             new int[] {FILE_SIZE_1},
281             new String[] {FILE_CHECKSUM_1},
282             new String[] {FILE_ANDROID_SHARING_CHECKSUM_1},
283             new String[] {FILE_URL_1},
284             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
285 
286     long twoDaysLaterInSeconds =
287         Calendar.getInstance().getTimeInMillis() / 1000 + 172800; // in two days
288     groupWithFileToShare2 =
289         groupWithFileToShare2.toBuilder().setExpirationDate(twoDaysLaterInSeconds).build();
290 
291     assertThat(
292             mobileDataDownload
293                 .addFileGroup(
294                     AddFileGroupRequest.newBuilder()
295                         .setDataFileGroup(groupWithFileToShare1)
296                         .build())
297                 .get())
298         .isTrue();
299 
300     assertThat(
301             mobileDataDownload
302                 .addFileGroup(
303                     AddFileGroupRequest.newBuilder()
304                         .setDataFileGroup(groupWithFileToShare2)
305                         .build())
306                 .get())
307         .isTrue();
308 
309     mobileDataDownload
310         .downloadFileGroup(
311             DownloadFileGroupRequest.newBuilder()
312                 .setGroupName(FILE_GROUP_TO_SHARE_1)
313                 .setListenerOptional(
314                     Optional.of(
315                         new DownloadListener() {
316                           @Override
317                           public void onProgress(long currentSize) {
318                             Log.i(TAG, "onProgress " + currentSize);
319                           }
320 
321                           @Override
322                           public void onComplete(ClientFileGroup clientFileGroup) {
323                             Log.i(TAG, "onComplete " + clientFileGroup.getGroupName());
324                           }
325                         }))
326                 .build())
327         .get(MAX_DOWNLOAD_FILE_GROUP_WAIT_TIME_SECS, SECONDS);
328 
329     ClientFileGroup clientFileGroup =
330         mobileDataDownload
331             .getFileGroup(
332                 GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_TO_SHARE_1).build())
333             .get();
334 
335     assertThat(clientFileGroup).isNotNull();
336     assertThat(clientFileGroup.getGroupName()).isEqualTo(FILE_GROUP_TO_SHARE_1);
337     assertThat(clientFileGroup.getFileCount()).isEqualTo(1);
338 
339     ClientFile clientFile = clientFileGroup.getFileList().get(0);
340     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID_1);
341     Uri uri = Uri.parse(clientFile.getFileUri());
342 
343     // The file is now available in the android shared storage.
344     assertThat(uri).isEqualTo(androidUri);
345     assertThat(fileStorage.exists(uri)).isTrue();
346     assertThat(blobStoreManager.getLeasedBlobs()).hasSize(1);
347 
348     mobileDataDownload
349         .downloadFileGroup(
350             DownloadFileGroupRequest.newBuilder()
351                 .setGroupName(FILE_GROUP_TO_SHARE_2)
352                 .setListenerOptional(
353                     Optional.of(
354                         new DownloadListener() {
355                           @Override
356                           public void onProgress(long currentSize) {
357                             Log.i(TAG, "onProgress " + currentSize);
358                           }
359 
360                           @Override
361                           public void onComplete(ClientFileGroup clientFileGroup) {
362                             Log.i(TAG, "onComplete " + clientFileGroup.getGroupName());
363                           }
364                         }))
365                 .build())
366         .get(MAX_DOWNLOAD_FILE_GROUP_WAIT_TIME_SECS, SECONDS);
367 
368     String debugString = mobileDataDownload.getDebugInfoAsString();
369     Log.i(TAG, "MDD Lib dump:");
370     for (String line : debugString.split("\n", -1)) {
371       Log.i(TAG, line);
372     }
373 
374     clientFileGroup =
375         mobileDataDownload
376             .getFileGroup(
377                 GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_TO_SHARE_2).build())
378             .get();
379 
380     assertThat(clientFileGroup).isNotNull();
381     assertThat(clientFileGroup.getGroupName()).isEqualTo(FILE_GROUP_TO_SHARE_2);
382     assertThat(clientFileGroup.getFileCount()).isEqualTo(1);
383 
384     clientFile = clientFileGroup.getFileList().get(0);
385     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID_1);
386     uri = Uri.parse(clientFile.getFileUri());
387 
388     // The file is still available in the android shared storage.
389     assertThat(uri).isEqualTo(androidUri);
390     assertThat(fileStorage.exists(uri)).isTrue();
391     assertThat(blobStoreManager.getLeasedBlobs()).hasSize(1);
392 
393     ArgumentCaptor<MddLogData> logDataCaptor = ArgumentCaptor.forClass(MddLogData.class);
394     // 1073 is the tag number for MddClientEvent.Code.EVENT_CODE_UNSPECIFIED.
395     verify(mockLogger, times(2)).log(logDataCaptor.capture(), /* eventCode= */ eq(1073));
396 
397     List<MddLogData> logData = logDataCaptor.getAllValues();
398     assertThat(logData).hasSize(2);
399 
400     Void log1 = null;
401     Void log2 = null;
402   }
403 
404   @Test
fileAvailableInSharedStorage_neverDownloaded()405   public void fileAvailableInSharedStorage_neverDownloaded() throws Exception {
406     mobileDataDownload = builderForTest().build();
407 
408     byte[] content = "fileAvailableInSharedStorage_neverDownloaded".getBytes();
409     String androidChecksum = computeDigest(content, "SHA-256");
410     String checksum = computeDigest(content, "SHA-1");
411     Uri androidUri = BlobUri.builder(context).setBlobParameters(androidChecksum).build();
412 
413     assertThat(blobStoreBackend.exists(androidUri)).isFalse();
414 
415     // Write file in the shared storage
416     try (OutputStream out = blobStoreBackend.openForWrite(androidUri)) {
417       assertThat(out).isNotNull();
418       out.write(content);
419     }
420     assertThat(blobStoreBackend.exists(androidUri)).isTrue();
421     assertThat(blobStoreManager.getLeasedBlobs()).isEmpty();
422 
423     // A file group with one android-shared file and one non-androidShared file
424     DataFileGroup groupWithFileToShare1 =
425         TestFileGroupPopulator.createDataFileGroup(
426             FILE_GROUP_TO_SHARE_1,
427             context.getPackageName(),
428             new String[] {FILE_ID_1, FILE_ID},
429             new int[] {content.length, FILE_SIZE},
430             new String[] {checksum, FILE_CHECKSUM},
431             new String[] {androidChecksum, ""},
432             new String[] {"https://random-url", FILE_URL},
433             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
434 
435     assertThat(
436             mobileDataDownload
437                 .addFileGroup(
438                     AddFileGroupRequest.newBuilder()
439                         .setDataFileGroup(groupWithFileToShare1)
440                         .build())
441                 .get())
442         .isTrue();
443 
444     mobileDataDownload
445         .downloadFileGroup(
446             DownloadFileGroupRequest.newBuilder()
447                 .setGroupName(FILE_GROUP_TO_SHARE_1)
448                 .setListenerOptional(
449                     Optional.of(
450                         new DownloadListener() {
451                           @Override
452                           public void onProgress(long currentSize) {
453                             Log.i(TAG, "onProgress " + currentSize);
454                           }
455 
456                           @Override
457                           public void onComplete(ClientFileGroup clientFileGroup) {
458                             Log.i(TAG, "onComplete " + clientFileGroup.getGroupName());
459                           }
460                         }))
461                 .build())
462         .get(MAX_DOWNLOAD_FILE_GROUP_WAIT_TIME_SECS, SECONDS);
463 
464     ClientFileGroup clientFileGroup =
465         mobileDataDownload
466             .getFileGroup(
467                 GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_TO_SHARE_1).build())
468             .get();
469 
470     assertThat(clientFileGroup).isNotNull();
471     assertThat(clientFileGroup.getGroupName()).isEqualTo(FILE_GROUP_TO_SHARE_1);
472     assertThat(clientFileGroup.getFileCount()).isEqualTo(2);
473 
474     ClientFile clientFile = clientFileGroup.getFileList().get(0);
475     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID_1);
476     Uri uri = Uri.parse(clientFile.getFileUri());
477 
478     // The file is available in the android shared storage.
479     assertThat(uri).isEqualTo(androidUri);
480     assertThat(fileStorage.exists(androidUri)).isTrue();
481     assertThat(blobStoreManager.getLeasedBlobs()).hasSize(1);
482 
483     clientFile = clientFileGroup.getFileList().get(1);
484     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID);
485     uri = Uri.parse(clientFile.getFileUri());
486     assertThat(fileStorage.fileSize(uri)).isEqualTo(FILE_SIZE);
487 
488     ArgumentCaptor<MddLogData> logDataCaptor = ArgumentCaptor.forClass(MddLogData.class);
489     // 1073 is the tag number for MddClientEvent.Code.EVENT_CODE_UNSPECIFIED.
490     verify(mockLogger).log(logDataCaptor.capture(), /* eventCode= */ eq(1073));
491 
492     List<MddLogData> logData = logDataCaptor.getAllValues();
493     assertThat(logData).hasSize(1);
494 
495     Void log1 = null;
496   }
497 
498   @Test
fileDownloadedForFirstFileGroup_thenSharedForSecondFileGroup()499   public void fileDownloadedForFirstFileGroup_thenSharedForSecondFileGroup() throws Exception {
500     mobileDataDownload = builderForTest().build();
501 
502     Uri androidUri =
503         BlobUri.builder(context).setBlobParameters(FILE_ANDROID_SHARING_CHECKSUM_2).build();
504     assertThat(blobStoreBackend.exists(androidUri)).isFalse();
505 
506     // Create non-android-shared file group.
507     DataFileGroup groupWithFileToShare1 =
508         TestFileGroupPopulator.createDataFileGroup(
509             FILE_GROUP_TO_SHARE_1,
510             context.getPackageName(),
511             new String[] {FILE_ID_2},
512             new int[] {FILE_SIZE_2},
513             new String[] {FILE_CHECKSUM_2},
514             new String[] {FILE_URL_2},
515             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
516 
517     long laterTimeSecs = Calendar.getInstance().getTimeInMillis() / 1000 + 86400; // in one day
518     groupWithFileToShare1 =
519         groupWithFileToShare1.toBuilder().setExpirationDate(laterTimeSecs).build();
520 
521     assertThat(
522             mobileDataDownload
523                 .addFileGroup(
524                     AddFileGroupRequest.newBuilder()
525                         .setDataFileGroup(groupWithFileToShare1)
526                         .build())
527                 .get())
528         .isTrue();
529 
530     // groupWithFileToShare2 has the same file as the previous file group but it has been configured
531     // to be share.
532     DataFileGroup groupWithFileToShare2 =
533         TestFileGroupPopulator.createDataFileGroup(
534             FILE_GROUP_TO_SHARE_2,
535             context.getPackageName(),
536             new String[] {FILE_ID_2},
537             new int[] {FILE_SIZE_2},
538             new String[] {FILE_CHECKSUM_2},
539             new String[] {FILE_ANDROID_SHARING_CHECKSUM_2},
540             new String[] {FILE_URL_2},
541             DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK);
542 
543     assertThat(
544             mobileDataDownload
545                 .addFileGroup(
546                     AddFileGroupRequest.newBuilder()
547                         .setDataFileGroup(groupWithFileToShare2)
548                         .build())
549                 .get())
550         .isTrue();
551 
552     mobileDataDownload
553         .downloadFileGroup(
554             DownloadFileGroupRequest.newBuilder()
555                 .setGroupName(FILE_GROUP_TO_SHARE_1)
556                 .setListenerOptional(
557                     Optional.of(
558                         new DownloadListener() {
559                           @Override
560                           public void onProgress(long currentSize) {
561                             Log.i(TAG, "onProgress " + currentSize);
562                           }
563 
564                           @Override
565                           public void onComplete(ClientFileGroup clientFileGroup) {
566                             Log.i(TAG, "onComplete " + clientFileGroup.getGroupName());
567                           }
568                         }))
569                 .build())
570         .get(MAX_DOWNLOAD_FILE_GROUP_WAIT_TIME_SECS, SECONDS);
571 
572     ClientFileGroup clientFileGroup =
573         mobileDataDownload
574             .getFileGroup(
575                 GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_TO_SHARE_1).build())
576             .get();
577 
578     assertThat(clientFileGroup).isNotNull();
579     assertThat(clientFileGroup.getGroupName()).isEqualTo(FILE_GROUP_TO_SHARE_1);
580     assertThat(clientFileGroup.getFileCount()).isEqualTo(1);
581 
582     ClientFile clientFile = clientFileGroup.getFileList().get(0);
583     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID_2);
584 
585     // File stored locally
586     Uri localUri = Uri.parse(clientFile.getFileUri());
587     assertThat(localUri).isNotEqualTo(androidUri);
588     assertThat(blobStoreManager.getLeasedBlobs()).isEmpty();
589 
590     mobileDataDownload
591         .downloadFileGroup(
592             DownloadFileGroupRequest.newBuilder()
593                 .setGroupName(FILE_GROUP_TO_SHARE_2)
594                 .setListenerOptional(
595                     Optional.of(
596                         new DownloadListener() {
597                           @Override
598                           public void onProgress(long currentSize) {
599                             Log.i(TAG, "onProgress " + currentSize);
600                           }
601 
602                           @Override
603                           public void onComplete(ClientFileGroup clientFileGroup) {
604                             Log.i(TAG, "onComplete " + clientFileGroup.getGroupName());
605                           }
606                         }))
607                 .build())
608         .get(MAX_DOWNLOAD_FILE_GROUP_WAIT_TIME_SECS, SECONDS);
609 
610     String debugString = mobileDataDownload.getDebugInfoAsString();
611     Log.i(TAG, "MDD Lib dump:");
612     for (String line : debugString.split("\n", -1)) {
613       Log.i(TAG, line);
614     }
615 
616     clientFileGroup =
617         mobileDataDownload
618             .getFileGroup(
619                 GetFileGroupRequest.newBuilder().setGroupName(FILE_GROUP_TO_SHARE_2).build())
620             .get();
621 
622     assertThat(clientFileGroup).isNotNull();
623     assertThat(clientFileGroup.getGroupName()).isEqualTo(FILE_GROUP_TO_SHARE_2);
624     assertThat(clientFileGroup.getFileCount()).isEqualTo(1);
625 
626     clientFile = clientFileGroup.getFileList().get(0);
627     assertThat(clientFile.getFileId()).isEqualTo(FILE_ID_2);
628     Uri uri = Uri.parse(clientFile.getFileUri());
629 
630     // The file is now available in the shared storage.
631     assertThat(uri).isNotEqualTo(localUri);
632     assertThat(uri).isEqualTo(androidUri);
633     assertThat(fileStorage.exists(uri)).isTrue();
634     assertThat(blobStoreManager.getLeasedBlobs()).hasSize(1);
635 
636     ArgumentCaptor<MddLogData> logDataCaptor = ArgumentCaptor.forClass(MddLogData.class);
637     // 1073 is the tag number for MddClientEvent.Code.EVENT_CODE_UNSPECIFIED.
638     verify(mockLogger).log(logDataCaptor.capture(), /* eventCode= */ eq(1073));
639 
640     List<MddLogData> logData = logDataCaptor.getAllValues();
641     assertThat(logData).hasSize(1);
642 
643     Void log1 = null;
644   }
645 
builderForTest()646   private MobileDataDownloadBuilder builderForTest() {
647     Supplier<FileDownloader> fileDownloaderSupplier =
648         () ->
649             new TestFileDownloader(
650                 TEST_DATA_RELATIVE_PATH,
651                 fileStorage,
652                 MoreExecutors.listeningDecorator(DOWNLOAD_EXECUTOR));
653 
654     return MobileDataDownloadBuilder.newBuilder()
655         .setContext(context)
656         .setControlExecutor(controlExecutor)
657         .setFileDownloaderSupplier(fileDownloaderSupplier)
658         .setTaskScheduler(Optional.of(mockTaskScheduler))
659         .setDeltaDecoderOptional(Optional.absent())
660         .setFileStorage(fileStorage)
661         .setNetworkUsageMonitor(mockNetworkUsageMonitor)
662         .setDownloadMonitorOptional(Optional.of(mockDownloadProgressMonitor))
663         .setLoggerOptional(Optional.of(mockLogger))
664         .setFlagsOptional(Optional.of(flags));
665   }
666 }
667