• 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.internal;
17 
18 import static com.google.android.libraries.mobiledatadownload.internal.SharedFileManager.MDD_SHARED_FILE_MANAGER_METADATA;
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.util.concurrent.Futures.immediateFuture;
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.anyInt;
23 import static org.mockito.ArgumentMatchers.anyList;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.ArgumentMatchers.isA;
26 import static org.mockito.Mockito.never;
27 import static org.mockito.Mockito.times;
28 import static org.mockito.Mockito.verify;
29 import static org.mockito.Mockito.verifyNoInteractions;
30 import static org.mockito.Mockito.when;
31 
32 import android.content.Context;
33 import android.content.SharedPreferences;
34 import android.net.Uri;
35 import android.os.Build;
36 import androidx.test.core.app.ApplicationProvider;
37 import com.google.mobiledatadownload.internal.MetadataProto.DataFile;
38 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal;
39 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal.AllowedReaders;
40 import com.google.mobiledatadownload.internal.MetadataProto.DeltaFile;
41 import com.google.mobiledatadownload.internal.MetadataProto.DeltaFile.DiffDecoder;
42 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions;
43 import com.google.mobiledatadownload.internal.MetadataProto.FileStatus;
44 import com.google.mobiledatadownload.internal.MetadataProto.GroupKey;
45 import com.google.mobiledatadownload.internal.MetadataProto.NewFileKey;
46 import com.google.mobiledatadownload.internal.MetadataProto.SharedFile;
47 import com.google.android.libraries.mobiledatadownload.DownloadException;
48 import com.google.android.libraries.mobiledatadownload.DownloadException.DownloadResultCode;
49 import com.google.android.libraries.mobiledatadownload.FileSource;
50 import com.google.android.libraries.mobiledatadownload.SilentFeedback;
51 import com.google.android.libraries.mobiledatadownload.delta.DeltaDecoder;
52 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
53 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidFileBackend;
54 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidUri;
55 import com.google.android.libraries.mobiledatadownload.file.backends.BlobUri;
56 import com.google.android.libraries.mobiledatadownload.file.spi.Backend;
57 import com.google.android.libraries.mobiledatadownload.file.transforms.CompressTransform;
58 import com.google.android.libraries.mobiledatadownload.internal.Migrations.FileKeyVersion;
59 import com.google.android.libraries.mobiledatadownload.internal.downloader.DownloaderCallbackImpl;
60 import com.google.android.libraries.mobiledatadownload.internal.downloader.MddFileDownloader;
61 import com.google.android.libraries.mobiledatadownload.internal.logging.EventLogger;
62 import com.google.android.libraries.mobiledatadownload.internal.util.DirectoryUtil;
63 import com.google.android.libraries.mobiledatadownload.internal.util.FileGroupUtil;
64 import com.google.android.libraries.mobiledatadownload.internal.util.SharedPreferencesUtil;
65 import com.google.android.libraries.mobiledatadownload.monitor.DownloadProgressMonitor;
66 import com.google.android.libraries.mobiledatadownload.testing.TestFlags;
67 import com.google.common.base.Optional;
68 import com.google.common.collect.ImmutableList;
69 import com.google.common.util.concurrent.Futures;
70 import com.google.common.util.concurrent.MoreExecutors;
71 import com.google.mobiledatadownload.LogEnumsProto.MddClientEvent;
72 import com.google.protobuf.ByteString;
73 import java.io.File;
74 import java.io.FileOutputStream;
75 import java.io.IOException;
76 import java.util.Arrays;
77 import java.util.Collection;
78 import java.util.concurrent.ExecutionException;
79 import java.util.concurrent.Executor;
80 import java.util.concurrent.Executors;
81 import org.junit.After;
82 import org.junit.Assert;
83 import org.junit.Before;
84 import org.junit.Rule;
85 import org.junit.Test;
86 import org.junit.runner.RunWith;
87 import org.mockito.Mock;
88 import org.mockito.junit.MockitoJUnit;
89 import org.mockito.junit.MockitoRule;
90 import org.robolectric.ParameterizedRobolectricTestRunner;
91 import org.robolectric.ParameterizedRobolectricTestRunner.Parameter;
92 import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
93 import org.robolectric.annotation.Config;
94 import org.robolectric.util.ReflectionHelpers;
95 
96 @RunWith(ParameterizedRobolectricTestRunner.class)
97 @Config(shadows = {})
98 public class SharedFileManagerTest {
99 
100   @Parameters(
101       name =
102           "runAfterMigratedToAddDownloadTransform = {0}, runAfterMigratedToUseChecksumOnly = {1}")
parameters()103   public static Collection<Object[]> parameters() {
104     return Arrays.asList(new Object[][] {{false, false}, {true, false}, {true, true}});
105   }
106 
107   @Parameter(value = 0)
108   public boolean runAfterMigratedToAddDownloadTransform;
109 
110   @Parameter(value = 1)
111   public boolean runAfterMigratedToUseChecksumOnly;
112 
113   private static final DownloadConditions DOWNLOAD_CONDITIONS =
114       DownloadConditions.getDefaultInstance();
115 
116   private static final int TRAFFIC_TAG = 1000;
117 
118   private Context context;
119   private SynchronousFileStorage fileStorage;
120   private static final long FILE_GROUP_EXPIRATION_DATE_SECS = 10;
121   private static final String TEST_GROUP = "test-group";
122   private static final int VERSION_NUMBER = 7;
123   private static final long BUILD_ID = 0;
124   private static final String VARIANT_ID = "";
125   private static final DataFileGroupInternal FILE_GROUP =
126       MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder()
127           .setFileGroupVersionNumber(VERSION_NUMBER)
128           .build();
129   private static final GroupKey GROUP_KEY =
130       FileGroupUtil.createGroupKey(FILE_GROUP.getGroupName(), FILE_GROUP.getOwnerPackage());
131   private static final Executor CONTROL_EXECUTOR =
132       MoreExecutors.newSequentialExecutor(Executors.newCachedThreadPool());
133   private SharedFileManager sfm;
134 
135   // This is currently not mocked as the class was split from SharedFileManager, and this ensures
136   // that all tests still run the same way.
137   private SharedFilesMetadata sharedFilesMetadata;
138   private File publicDirectory;
139   private File privateDirectory;
140   private Optional<DeltaDecoder> deltaDecoder;
141   private final TestFlags flags = new TestFlags();
142 
143   @Mock SilentFeedback mockSilentFeedback;
144   @Mock MddFileDownloader mockDownloader;
145   @Mock DownloadProgressMonitor mockDownloadMonitor;
146   @Mock EventLogger eventLogger;
147   @Mock FileGroupsMetadata fileGroupsMetadata;
148   @Mock Backend mockBackend;
149 
150   @Rule public final MockitoRule mocks = MockitoJUnit.rule();
151 
152   @Before
setUp()153   public void setUp() throws Exception {
154 
155     context = ApplicationProvider.getApplicationContext();
156 
157     when(mockBackend.name()).thenReturn("blobstore");
158     fileStorage =
159         new SynchronousFileStorage(
160             Arrays.asList(AndroidFileBackend.builder(context).build(), mockBackend),
161             ImmutableList.of(new CompressTransform()));
162 
163     when(fileGroupsMetadata.read(any())).thenReturn(immediateFuture(null));
164 
165     sharedFilesMetadata =
166         new SharedPreferencesSharedFilesMetadata(
167             context, mockSilentFeedback, Optional.absent(), flags);
168 
169     deltaDecoder = Optional.absent();
170     sfm =
171         new SharedFileManager(
172             context,
173             mockSilentFeedback,
174             sharedFilesMetadata,
175             fileStorage,
176             mockDownloader,
177             deltaDecoder,
178             Optional.of(mockDownloadMonitor),
179             eventLogger,
180             flags,
181             fileGroupsMetadata,
182             Optional.absent(),
183             CONTROL_EXECUTOR);
184 
185     // TODO(b/117571083): Replace with fileStorage API.
186     File downloadDirectory =
187         new File(context.getFilesDir(), DirectoryUtil.MDD_STORAGE_MODULE + "/" + "shared");
188     publicDirectory = new File(downloadDirectory, DirectoryUtil.MDD_STORAGE_ALL_GOOGLE_APPS);
189     privateDirectory =
190         new File(downloadDirectory, DirectoryUtil.MDD_STORAGE_ONLY_GOOGLE_PLAY_SERVICES);
191     publicDirectory.mkdirs();
192     privateDirectory.mkdirs();
193 
194     if (runAfterMigratedToUseChecksumOnly) {
195       Migrations.setCurrentVersion(context, FileKeyVersion.USE_CHECKSUM_ONLY);
196     } else if (runAfterMigratedToAddDownloadTransform) {
197       Migrations.setCurrentVersion(context, FileKeyVersion.ADD_DOWNLOAD_TRANSFORM);
198     }
199   }
200 
201   @After
tearDown()202   public void tearDown() throws Exception {
203     SharedPreferencesUtil.getSharedPreferences(
204             context, MDD_SHARED_FILE_MANAGER_METADATA, Optional.absent())
205         .edit()
206         .clear()
207         .commit();
208 
209     // Reset to avoid exception in the call below.
210     fileStorage.deleteRecursively(
211         DirectoryUtil.getBaseDownloadDirectory(context, Optional.absent()));
212   }
213 
214   @Test
init_migrateToNewKey_enabled_v23ToV24()215   public void init_migrateToNewKey_enabled_v23ToV24() throws Exception {
216     Migrations.setMigratedToNewFileKey(context, false);
217 
218     assertThat(Migrations.isMigratedToNewFileKey(context)).isFalse();
219 
220     SharedPreferences sfmMetadata =
221         SharedPreferencesUtil.getSharedPreferences(
222             context, MDD_SHARED_FILE_MANAGER_METADATA, Optional.absent());
223     sfmMetadata
224         .edit()
225         .putBoolean(SharedFileManager.PREFS_KEY_MIGRATED_TO_NEW_FILE_KEY, true)
226         .commit();
227 
228     assertThat(sfm.init().get()).isTrue();
229 
230     assertThat(Migrations.isMigratedToNewFileKey(context)).isTrue();
231   }
232 
233   @Test
testSubscribeAndUnsubscribeSingleFile()234   public void testSubscribeAndUnsubscribeSingleFile() throws Exception {
235     DataFile file = MddTestUtil.createDataFile("fileId", 0);
236     NewFileKey newFileKey =
237         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
238 
239     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
240 
241     // Make sure the file entry was stored.
242     assertThat(sharedFilesMetadata.read(newFileKey)).isNotNull();
243 
244     // Unsubscribe and ensure entry for file was deleted.
245     assertThat(sfm.removeFileEntry(newFileKey).get()).isTrue();
246     assertThat(sharedFilesMetadata.read(newFileKey).get()).isNull();
247   }
248 
249   @Test
testMultipleSubscribes()250   public void testMultipleSubscribes() throws Exception {
251     DataFile file = MddTestUtil.createDataFile("fileId", 0);
252 
253     NewFileKey newFileKey =
254         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
255     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
256     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
257 
258     // Unsubscribe once. It should not matter how many subscribes were previously called. An
259     // unsubscribe should remove the entry.
260     assertThat(sfm.removeFileEntry(newFileKey).get()).isTrue();
261     assertThat(sharedFilesMetadata.read(newFileKey).get()).isNull();
262   }
263 
264   @Test
testRemoveFileEntry_nonexistentFile()265   public void testRemoveFileEntry_nonexistentFile() throws Exception {
266     DataFile file = MddTestUtil.createDataFile("fileId", 0);
267 
268     // Try to unsubscribe from a file that was never subscribed to and ensure that this won't add
269     // an entry for the file.
270     NewFileKey newFileKey =
271         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
272     assertThat(sfm.removeFileEntry(newFileKey).get()).isFalse();
273     assertThat(sharedFilesMetadata.read(newFileKey).get()).isNull();
274 
275     verifyNoInteractions(mockDownloader);
276   }
277 
278   @Test
testRemoveFileEntry_partialDownloadFileNotDeleted()279   public void testRemoveFileEntry_partialDownloadFileNotDeleted() throws Exception {
280     DataFile file = MddTestUtil.createDataFile("fileId", 0);
281 
282     NewFileKey newFileKey =
283         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
284     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
285 
286     // Download the file, but do not update shared prefs to say it is downloaded.
287     File onDeviceFile = simulateDownload(file, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
288 
289     assertThat(onDeviceFile.exists()).isTrue();
290 
291     Uri uri = sfm.getOnDeviceUri(newFileKey).get();
292 
293     // Ensure that deregister has actually deleted the file on disk.
294     assertThat(sfm.removeFileEntry(newFileKey).get()).isTrue();
295     assertThat(sharedFilesMetadata.read(newFileKey).get()).isNull();
296     // The partial download file should be deleted
297     assertThat(onDeviceFile.exists()).isTrue();
298 
299     verify(mockDownloader).stopDownloading(newFileKey.getChecksum(), uri);
300   }
301 
302   @Test
testStartImport_startsInlineFileCopy()303   public void testStartImport_startsInlineFileCopy() throws Exception {
304     FileSource inlineSource = FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"));
305     DataFile file =
306         MddTestUtil.createDataFile("fileId", 0).toBuilder()
307             .setUrlToDownload("inlinefile:123")
308             .build();
309     NewFileKey newFileKey =
310         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
311 
312     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
313     Uri fileUri = sfm.getOnDeviceUri(newFileKey).get();
314     when(fileGroupsMetadata.read(GROUP_KEY)).thenReturn(Futures.immediateFuture(FILE_GROUP));
315     when(mockDownloader.startCopying(
316             eq(newFileKey.getChecksum()),
317             eq(fileUri),
318             eq(file.getUrlToDownload()),
319             eq(file.getByteSize()),
320             eq(DOWNLOAD_CONDITIONS),
321             isA(DownloaderCallbackImpl.class),
322             any()))
323         .thenReturn(Futures.immediateVoidFuture());
324 
325     sfm.startImport(GROUP_KEY, file, newFileKey, DOWNLOAD_CONDITIONS, inlineSource).get();
326 
327     SharedFile sharedFile = sharedFilesMetadata.read(newFileKey).get();
328     assertThat(sharedFile.getFileStatus()).isEqualTo(FileStatus.DOWNLOAD_IN_PROGRESS);
329   }
330 
331   @Test
testStartImport_whenFileAlreadyDownloaded_returnsEarly()332   public void testStartImport_whenFileAlreadyDownloaded_returnsEarly() throws Exception {
333     FileSource inlineSource = FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"));
334     DataFile file =
335         MddTestUtil.createDataFile("fileId", 0).toBuilder()
336             .setUrlToDownload("inlinefile:123")
337             .build();
338     NewFileKey newFileKey =
339         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
340 
341     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
342     File onDeviceFile = simulateDownload(file, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
343     changeFileStatusAs(newFileKey, FileStatus.DOWNLOAD_COMPLETE);
344 
345     // File is already downloaded, so we should return early
346     sfm.startImport(GROUP_KEY, file, newFileKey, DOWNLOAD_CONDITIONS, inlineSource).get();
347     onDeviceFile.delete();
348 
349     verify(mockDownloader, times(0))
350         .startCopying(any(), any(), any(), anyInt(), any(), any(), any());
351   }
352 
353   @Test
testStartImport_whenUnreservedEntry_throws()354   public void testStartImport_whenUnreservedEntry_throws() throws Exception {
355     FileSource inlineSource = FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"));
356     DataFile file =
357         MddTestUtil.createDataFile("fileId", 0).toBuilder()
358             .setUrlToDownload("inlinefile:123")
359             .build();
360     NewFileKey newFileKey =
361         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
362 
363     ExecutionException ex =
364         Assert.assertThrows(
365             ExecutionException.class,
366             () ->
367                 sfm.startImport(GROUP_KEY, file, newFileKey, DOWNLOAD_CONDITIONS, inlineSource)
368                     .get());
369 
370     assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class);
371     DownloadException dex = (DownloadException) ex.getCause();
372 
373     assertThat(dex.getDownloadResultCode())
374         .isEqualTo(DownloadResultCode.SHARED_FILE_NOT_FOUND_ERROR);
375   }
376 
377   @Test
testStartImport_whenNotInlineFileUrlScheme_throws()378   public void testStartImport_whenNotInlineFileUrlScheme_throws() throws Exception {
379     FileSource inlineSource = FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"));
380     DataFile file = MddTestUtil.createDataFile("fileId", 0);
381     NewFileKey newFileKey =
382         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
383 
384     ExecutionException ex =
385         Assert.assertThrows(
386             ExecutionException.class,
387             () ->
388                 sfm.startImport(GROUP_KEY, file, newFileKey, DOWNLOAD_CONDITIONS, inlineSource)
389                     .get());
390 
391     assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class);
392     DownloadException dex = (DownloadException) ex.getCause();
393     assertThat(dex.getDownloadResultCode())
394         .isEqualTo(DownloadResultCode.INVALID_INLINE_FILE_URL_SCHEME);
395   }
396 
397   @Test
testNotifyCurrentSize_partialDownloadFile()398   public void testNotifyCurrentSize_partialDownloadFile() throws Exception {
399     DataFile file = MddTestUtil.createDataFile("fileId", 0);
400 
401     NewFileKey newFileKey =
402         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
403     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
404 
405     // Download the file, but do not update shared prefs to say it is downloaded.
406     File onDeviceFile = simulateDownload(file, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
407     Uri fileUri = sfm.getOnDeviceUri(newFileKey).get();
408 
409     when(fileGroupsMetadata.read(GROUP_KEY)).thenReturn(Futures.immediateFuture(FILE_GROUP));
410     when(mockDownloader.startDownloading(
411             eq(newFileKey.getChecksum()),
412             eq(GROUP_KEY),
413             eq(VERSION_NUMBER),
414             eq(BUILD_ID),
415             eq(VARIANT_ID),
416             eq(fileUri),
417             eq(file.getUrlToDownload()),
418             eq(file.getByteSize()),
419             eq(DOWNLOAD_CONDITIONS),
420             isA(DownloaderCallbackImpl.class),
421             anyInt(),
422             anyList()))
423         .thenReturn(Futures.immediateFuture(null));
424 
425     sfm.startDownload(
426             GROUP_KEY,
427             file,
428             newFileKey,
429             DOWNLOAD_CONDITIONS,
430             TRAFFIC_TAG,
431             /* extraHttpHeaders= */ ImmutableList.of())
432         .get();
433 
434     SharedFile sharedFile = sharedFilesMetadata.read(newFileKey).get();
435     assertThat(sharedFile.getFileStatus()).isEqualTo(FileStatus.DOWNLOAD_IN_PROGRESS);
436     verify(mockDownloadMonitor).notifyCurrentFileSize(TEST_GROUP, onDeviceFile.length());
437   }
438 
439   @Test
testDontDeleteUnsubscribedFiles()440   public void testDontDeleteUnsubscribedFiles() throws Exception {
441     DataFile datafile = MddTestUtil.createDataFile("fileId", 0);
442 
443     NewFileKey newFileKey =
444         SharedFilesMetadata.createKeyFromDataFile(datafile, AllowedReaders.ALL_GOOGLE_APPS);
445     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
446 
447     // "download" the file and update sharedPrefs
448     File onDeviceFile =
449         simulateDownload(datafile, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
450     changeFileStatusAs(newFileKey, FileStatus.DOWNLOAD_COMPLETE);
451 
452     assertThat(onDeviceFile.exists()).isTrue();
453     Uri uri = sfm.getOnDeviceUri(newFileKey).get();
454 
455     // Ensure that deregister has actually deleted the file on disk.
456     assertThat(sfm.removeFileEntry(newFileKey).get()).isTrue();
457     assertThat(sharedFilesMetadata.read(newFileKey).get()).isNull();
458     // The file should not be deleted by the SFM because deletion is handled by ExpirationHandler.
459     assertThat(onDeviceFile.exists()).isTrue();
460 
461     verify(mockDownloader).stopDownloading(newFileKey.getChecksum(), uri);
462   }
463 
464   @Test
testStartDownload_whenInlineFileUrlScheme_fails()465   public void testStartDownload_whenInlineFileUrlScheme_fails() throws Exception {
466     DataFile inlineFile =
467         MddTestUtil.createDataFile("inlineFileId", 0).toBuilder()
468             .setUrlToDownload("inlinefile:abc")
469             .setChecksum("abc")
470             .build();
471     NewFileKey newFileKey =
472         SharedFilesMetadata.createKeyFromDataFile(inlineFile, AllowedReaders.ALL_GOOGLE_APPS);
473 
474     ExecutionException ex =
475         Assert.assertThrows(
476             ExecutionException.class,
477             () ->
478                 sfm.startDownload(
479                         GROUP_KEY,
480                         inlineFile,
481                         newFileKey,
482                         DOWNLOAD_CONDITIONS,
483                         TRAFFIC_TAG,
484                         /* extraHttpHeaders= */ ImmutableList.of())
485                     .get());
486     assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class);
487     DownloadException dex = (DownloadException) ex.getCause();
488     assertThat(dex.getDownloadResultCode())
489         .isEqualTo(DownloadResultCode.INVALID_INLINE_FILE_URL_SCHEME);
490   }
491 
492   @Test
testStartDownload_unsubscribedFile()493   public void testStartDownload_unsubscribedFile() {
494     DataFile file = MddTestUtil.createDataFile("fileId", 0);
495     NewFileKey newFileKey =
496         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
497 
498     ExecutionException ex =
499         Assert.assertThrows(
500             ExecutionException.class,
501             () ->
502                 sfm.startDownload(
503                         GROUP_KEY,
504                         file,
505                         newFileKey,
506                         DOWNLOAD_CONDITIONS,
507                         TRAFFIC_TAG,
508                         /* extraHttpHeaders= */ ImmutableList.of())
509                     .get());
510     assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class);
511     assertThat(ex).hasMessageThat().contains("SHARED_FILE_NOT_FOUND_ERROR");
512 
513     verifyNoInteractions(mockDownloader);
514   }
515 
516   @Test
testStartDownload_newFile()517   public void testStartDownload_newFile() throws Exception {
518     DataFile file = MddTestUtil.createDataFile("fileId", 0);
519     NewFileKey newFileKey =
520         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
521 
522     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
523     Uri fileUri = sfm.getOnDeviceUri(newFileKey).get();
524     when(fileGroupsMetadata.read(GROUP_KEY)).thenReturn(Futures.immediateFuture(FILE_GROUP));
525     when(mockDownloader.startDownloading(
526             eq(newFileKey.getChecksum()),
527             eq(GROUP_KEY),
528             eq(VERSION_NUMBER),
529             eq(BUILD_ID),
530             eq(VARIANT_ID),
531             eq(fileUri),
532             eq(file.getUrlToDownload()),
533             eq(file.getByteSize()),
534             eq(DOWNLOAD_CONDITIONS),
535             isA(DownloaderCallbackImpl.class),
536             anyInt(),
537             anyList()))
538         .thenReturn(Futures.immediateFuture(null));
539 
540     sfm.startDownload(
541             GROUP_KEY,
542             file,
543             newFileKey,
544             DOWNLOAD_CONDITIONS,
545             TRAFFIC_TAG,
546             /* extraHttpHeaders= */ ImmutableList.of())
547         .get();
548 
549     SharedFile sharedFile = sharedFilesMetadata.read(newFileKey).get();
550     assertThat(sharedFile.getFileStatus()).isEqualTo(FileStatus.DOWNLOAD_IN_PROGRESS);
551   }
552 
553   @Test
testStartDownload_downloadedFile()554   public void testStartDownload_downloadedFile() throws Exception {
555     DataFile file = MddTestUtil.createDataFile("fileId", 0);
556     NewFileKey newFileKey =
557         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
558 
559     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
560     File onDeviceFile = simulateDownload(file, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
561     changeFileStatusAs(newFileKey, FileStatus.DOWNLOAD_COMPLETE);
562 
563     // The file is already downloaded, so we should just return DOWNLOADED.
564     sfm.startDownload(
565             GROUP_KEY,
566             file,
567             newFileKey,
568             DOWNLOAD_CONDITIONS,
569             TRAFFIC_TAG,
570             /* extraHttpHeaders= */ ImmutableList.of())
571         .get();
572     onDeviceFile.delete();
573 
574     verify(mockDownloadMonitor).notifyCurrentFileSize(TEST_GROUP, file.getByteSize());
575     verifyNoInteractions(mockDownloader);
576   }
577 
578   @Test
testVerifyDownload_nonExistentFile()579   public void testVerifyDownload_nonExistentFile() throws Exception {
580     DataFile file = MddTestUtil.createDataFile("fileId", 0);
581     NewFileKey newFileKey =
582         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
583 
584     ExecutionException ex =
585         Assert.assertThrows(ExecutionException.class, () -> sfm.getFileStatus(newFileKey).get());
586     assertThat(ex).hasCauseThat().isInstanceOf(SharedFileMissingException.class);
587     ex = Assert.assertThrows(ExecutionException.class, () -> sfm.getOnDeviceUri(newFileKey).get());
588     assertThat(ex).hasCauseThat().isInstanceOf(SharedFileMissingException.class);
589 
590     verifyNoInteractions(mockDownloader);
591   }
592 
593   @Test
testVerifyDownload_fileDownloaded()594   public void testVerifyDownload_fileDownloaded() throws Exception {
595     DataFile file = MddTestUtil.createDataFile("fileId", 0);
596     NewFileKey newFileKey =
597         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
598 
599     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
600     simulateDownload(file, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
601     changeFileStatusAs(newFileKey, FileStatus.DOWNLOAD_COMPLETE);
602 
603     // VerifyDownload should update the onDeviceUri fields for storedFile.
604     assertThat(sfm.getFileStatus(newFileKey).get()).isEqualTo(FileStatus.DOWNLOAD_COMPLETE);
605   }
606 
607   @Test
testVerifyDownload_downloadNotAttempted()608   public void testVerifyDownload_downloadNotAttempted() throws Exception {
609     DataFile file = MddTestUtil.createDataFile("fileId", 0);
610     NewFileKey newFileKey =
611         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
612 
613     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
614 
615     assertThat(sfm.getFileStatus(newFileKey).get()).isEqualTo(FileStatus.SUBSCRIBED);
616 
617     // getOnDeviceUri will populate the onDeviceUri even download was not attempted.
618     assertThat(sfm.getOnDeviceUri(newFileKey).toString()).isNotEmpty();
619 
620     verifyNoInteractions(mockDownloader);
621   }
622 
623   @Test
testVerifyDownload_alreadyDownloaded()624   public void testVerifyDownload_alreadyDownloaded() throws Exception {
625     DataFile file = MddTestUtil.createDataFile("fileId", 0);
626     NewFileKey newFileKey =
627         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
628 
629     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
630     File onDeviceFile = simulateDownload(file, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
631     changeFileStatusAs(newFileKey, FileStatus.DOWNLOAD_COMPLETE);
632 
633     assertThat(sfm.getFileStatus(newFileKey).get()).isEqualTo(FileStatus.DOWNLOAD_COMPLETE);
634     assertThat(sfm.getOnDeviceUri(newFileKey).get())
635         .isEqualTo(AndroidUri.builder(context).fromFile(onDeviceFile).build());
636 
637     onDeviceFile.delete();
638     verifyNoInteractions(mockDownloader);
639   }
640 
641   @Test
findNoDeltaFile_withNoBaseFileOnDevice()642   public void findNoDeltaFile_withNoBaseFileOnDevice() throws Exception {
643     DataFile file = MddTestUtil.createDataFileWithDeltaFile("fileId", 0, 3);
644     assertThat(
645             sfm.findFirstDeltaFileWithBaseFileDownloaded(file, AllowedReaders.ALL_GOOGLE_APPS)
646                 .get())
647         .isNull();
648   }
649 
650   @Test
findExpectedDeltaFile_withDifferentReaderBaseFile()651   public void findExpectedDeltaFile_withDifferentReaderBaseFile() throws Exception {
652     DataFile file = MddTestUtil.createDataFileWithDeltaFile("fileId", 0, 3);
653     markBaseFileDownloaded(
654         file.getDeltaFile(1).getBaseFile().getChecksum(), AllowedReaders.ALL_GOOGLE_APPS);
655     assertThat(
656             sfm.findFirstDeltaFileWithBaseFileDownloaded(
657                     file, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
658                 .get())
659         .isNull();
660   }
661 
662   @Test
findNoDeltaFile_whenDecoderNotSupported()663   public void findNoDeltaFile_whenDecoderNotSupported() throws Exception {
664     deltaDecoder =
665         Optional.of(
666             new DeltaDecoder() {
667               @Override
668               public void decode(Uri baseUri, Uri deltaUri, Uri targetUri) {
669                 throw new UnsupportedOperationException("No delta decoder provided.");
670               }
671 
672               @Override
673               public DiffDecoder getDecoderName() {
674                 return DiffDecoder.UNSPECIFIED;
675               }
676             });
677     sfm =
678         new SharedFileManager(
679             context,
680             mockSilentFeedback,
681             sharedFilesMetadata,
682             fileStorage,
683             mockDownloader,
684             deltaDecoder,
685             Optional.of(mockDownloadMonitor),
686             eventLogger,
687             flags,
688             fileGroupsMetadata,
689             Optional.absent(),
690             CONTROL_EXECUTOR);
691 
692     DataFile file = MddTestUtil.createDataFileWithDeltaFile("fileId", 0, 3);
693     markBaseFileDownloaded(
694         file.getDeltaFile(1).getBaseFile().getChecksum(), AllowedReaders.ALL_GOOGLE_APPS);
695     DeltaFile deltaFile =
696         sfm.findFirstDeltaFileWithBaseFileDownloaded(file, AllowedReaders.ALL_GOOGLE_APPS).get();
697     assertThat(deltaFile).isNull();
698   }
699 
markBaseFileDownloaded(String checksum, AllowedReaders allowedReaders)700   private void markBaseFileDownloaded(String checksum, AllowedReaders allowedReaders)
701       throws Exception {
702     NewFileKey fileKey =
703         NewFileKey.newBuilder().setChecksum(checksum).setAllowedReaders(allowedReaders).build();
704     assertThat(sfm.reserveFileEntry(fileKey).get()).isTrue();
705     changeFileStatusAs(fileKey, FileStatus.DOWNLOAD_COMPLETE);
706   }
707 
708   @Test
testClear()709   public void testClear() throws Exception {
710     // Create two files, one downloaded and the other currently being downloaded.
711     DataFile downloadedFile = MddTestUtil.createDataFile("file", 0);
712     DataFile registeredFile = MddTestUtil.createDataFile("registered-file", 0);
713 
714     NewFileKey downloadedKey =
715         SharedFilesMetadata.createKeyFromDataFile(downloadedFile, AllowedReaders.ALL_GOOGLE_APPS);
716     NewFileKey registeredKey =
717         SharedFilesMetadata.createKeyFromDataFile(registeredFile, AllowedReaders.ALL_GOOGLE_APPS);
718 
719     assertThat(sfm.reserveFileEntry(downloadedKey).get()).isTrue();
720     File onDevicePublicFile =
721         simulateDownload(downloadedFile, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
722     changeFileStatusAs(downloadedKey, FileStatus.DOWNLOAD_COMPLETE);
723 
724     assertThat(sfm.reserveFileEntry(registeredKey).get()).isTrue();
725 
726     assertThat(sfm.getOnDeviceUri(downloadedKey).get())
727         .isEqualTo(AndroidUri.builder(context).fromFile(onDevicePublicFile).build());
728     assertThat(onDevicePublicFile.exists()).isTrue();
729 
730     // Clear should delete all files in our directories.
731     sfm.clear().get();
732 
733     assertThat(onDevicePublicFile.exists()).isFalse();
734   }
735 
736   @Test
testClear_sdkLessthanR()737   public void testClear_sdkLessthanR() throws Exception {
738     // Set scenario: SDK < R, enableAndroidFileSharing flag ON
739     ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.Q);
740 
741     // Create two files, one downloaded and the other currently being downloaded.
742     DataFile downloadedFile = MddTestUtil.createDataFile("file", 0);
743     DataFile registeredFile = MddTestUtil.createDataFile("registered-file", 0);
744 
745     NewFileKey downloadedKey =
746         SharedFilesMetadata.createKeyFromDataFile(downloadedFile, AllowedReaders.ALL_GOOGLE_APPS);
747     NewFileKey registeredKey =
748         SharedFilesMetadata.createKeyFromDataFile(registeredFile, AllowedReaders.ALL_GOOGLE_APPS);
749 
750     assertThat(sfm.reserveFileEntry(downloadedKey).get()).isTrue();
751     File onDevicePublicFile =
752         simulateDownload(downloadedFile, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
753     changeFileStatusAs(downloadedKey, FileStatus.DOWNLOAD_COMPLETE);
754 
755     assertThat(sfm.reserveFileEntry(registeredKey).get()).isTrue();
756 
757     assertThat(sfm.getOnDeviceUri(downloadedKey).get())
758         .isEqualTo(AndroidUri.builder(context).fromFile(onDevicePublicFile).build());
759     assertThat(onDevicePublicFile.exists()).isTrue();
760 
761     // Clear should delete all files in our directories.
762     sfm.clear().get();
763 
764     assertThat(onDevicePublicFile.exists()).isFalse();
765     verify(mockBackend, never()).deleteFile(any());
766     verify(eventLogger, never()).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED);
767   }
768 
769   @Test
testClear_withAndroidSharedFiles()770   public void testClear_withAndroidSharedFiles() throws Exception {
771     // Set scenario: SDK >= R
772     ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.R);
773 
774     // Create three files, one downloaded, the other currently being downloaded and one shared with
775     // the Android Blob Sharing Service.
776     DataFile downloadedFile = MddTestUtil.createDataFile("file", /* fileIndex= */ 0);
777     DataFile registeredFile = MddTestUtil.createDataFile("registered-file", /* fileIndex= */ 1);
778     DataFile sharedFile = MddTestUtil.createSharedDataFile("shared-file", /* fileIndex= */ 2);
779 
780     NewFileKey downloadedKey =
781         SharedFilesMetadata.createKeyFromDataFile(downloadedFile, AllowedReaders.ALL_GOOGLE_APPS);
782     NewFileKey registeredKey =
783         SharedFilesMetadata.createKeyFromDataFile(registeredFile, AllowedReaders.ALL_GOOGLE_APPS);
784     NewFileKey sharedFileKey =
785         SharedFilesMetadata.createKeyFromDataFile(sharedFile, AllowedReaders.ALL_GOOGLE_APPS);
786 
787     assertThat(sfm.reserveFileEntry(downloadedKey).get()).isTrue();
788     File onDevicePublicFile =
789         simulateDownload(downloadedFile, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
790     changeFileStatusAs(downloadedKey, FileStatus.DOWNLOAD_COMPLETE);
791 
792     assertThat(sfm.reserveFileEntry(registeredKey).get()).isTrue();
793 
794     assertThat(sfm.reserveFileEntry(sharedFileKey).get()).isTrue();
795     assertThat(
796             sfm.setAndroidSharedDownloadedFileEntry(
797                     sharedFileKey,
798                     sharedFile.getAndroidSharingChecksum(),
799                     FILE_GROUP_EXPIRATION_DATE_SECS)
800                 .get())
801         .isTrue();
802     Uri allLeasesUri = DirectoryUtil.getBlobStoreAllLeasesUri(context);
803 
804     assertThat(sfm.getOnDeviceUri(downloadedKey).get())
805         .isEqualTo(AndroidUri.builder(context).fromFile(onDevicePublicFile).build());
806     assertThat(onDevicePublicFile.exists()).isTrue();
807 
808     // Clear should delete all files in our directories.
809     sfm.clear().get();
810 
811     assertThat(onDevicePublicFile.exists()).isFalse();
812     verify(mockBackend).deleteFile(allLeasesUri);
813 
814     verify(eventLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED);
815   }
816 
817   @Test
cancelDownload_onDownloadedFile()818   public void cancelDownload_onDownloadedFile() throws Exception {
819     DataFile downloadedFile = MddTestUtil.createDataFile("downloaded-file", 0);
820     NewFileKey downloadedKey =
821         SharedFilesMetadata.createKeyFromDataFile(downloadedFile, AllowedReaders.ALL_GOOGLE_APPS);
822 
823     assertThat(sfm.reserveFileEntry(downloadedKey).get()).isTrue();
824     changeFileStatusAs(downloadedKey, FileStatus.DOWNLOAD_COMPLETE);
825 
826     // Calling cancelDownload on downloaded file is a no-op.
827     sfm.cancelDownload(downloadedKey).get();
828 
829     verifyNoInteractions(mockDownloader);
830   }
831 
832   @Test
cancelDownload_onRegisteredFile()833   public void cancelDownload_onRegisteredFile() throws Exception {
834     DataFile registeredFile = MddTestUtil.createDataFile("registered-file", 0);
835     NewFileKey registeredKey =
836         SharedFilesMetadata.createKeyFromDataFile(registeredFile, AllowedReaders.ALL_GOOGLE_APPS);
837 
838     assertThat(sfm.reserveFileEntry(registeredKey).get()).isTrue();
839 
840     // Calling cancelDownload on registered file will stop the download.
841     sfm.cancelDownload(registeredKey).get();
842 
843     SharedFile sharedFile = sharedFilesMetadata.read(registeredKey).get();
844     assertThat(sharedFile).isNotNull();
845     Uri onDeviceUri =
846         DirectoryUtil.getOnDeviceUri(
847             context,
848             registeredKey.getAllowedReaders(),
849             sharedFile.getFileName(),
850             registeredFile.getChecksum(),
851             mockSilentFeedback,
852             /* instanceId= */ Optional.absent(),
853             false);
854     verify(mockDownloader).stopDownloading(registeredKey.getChecksum(), onDeviceUri);
855   }
856 
857   @Test
testGetSharedFile()858   public void testGetSharedFile() throws Exception {
859     DataFile file = MddTestUtil.createDataFile("fileId", /* fileIndex= */ 0);
860     NewFileKey newFileKey =
861         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
862 
863     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
864 
865     SharedFile sharedFile = sfm.getSharedFile(newFileKey).get();
866     SharedFile expectedSharedFile = sharedFilesMetadata.read(newFileKey).get();
867 
868     assertThat(sharedFile).isNotNull();
869     assertThat(sharedFile).isEqualTo(expectedSharedFile);
870   }
871 
872   @Test
testGetSharedFile_nonExistentFile()873   public void testGetSharedFile_nonExistentFile() throws Exception {
874     DataFile file = MddTestUtil.createDataFile("fileId", 0);
875     NewFileKey newFileKey =
876         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
877 
878     ExecutionException ex =
879         Assert.assertThrows(ExecutionException.class, () -> sfm.getSharedFile(newFileKey).get());
880     assertThat(ex).hasCauseThat().isInstanceOf(SharedFileMissingException.class);
881   }
882 
883   @Test
testUpdateMaxExpirationDateSecs()884   public void testUpdateMaxExpirationDateSecs() throws Exception {
885     DataFile file = MddTestUtil.createDataFile("fileId", 0);
886     NewFileKey newFileKey =
887         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
888 
889     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
890     SharedFile sharedFileBeforeUpdate = sharedFilesMetadata.read(newFileKey).get();
891     SharedFile expectedSharedFileAfterUpdate =
892         SharedFile.newBuilder(sharedFileBeforeUpdate)
893             .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS)
894             .build();
895 
896     assertThat(sharedFileBeforeUpdate).isNotNull();
897     assertThat(sharedFileBeforeUpdate).isNotEqualTo(expectedSharedFileAfterUpdate);
898 
899     // updateMaxExpirationDateSecs updates maxExpirationDateSecs
900     assertThat(sfm.updateMaxExpirationDateSecs(newFileKey, FILE_GROUP_EXPIRATION_DATE_SECS).get())
901         .isTrue();
902     SharedFile sharedFileAfterUpdate = sharedFilesMetadata.read(newFileKey).get();
903     assertThat(sharedFileAfterUpdate).isNotNull();
904     assertThat(sharedFileAfterUpdate).isEqualTo(expectedSharedFileAfterUpdate);
905 
906     // updateMaxExpirationDateSecs doesn't update maxExpirationDateSecs
907     assertThat(
908             sfm.updateMaxExpirationDateSecs(newFileKey, FILE_GROUP_EXPIRATION_DATE_SECS - 1).get())
909         .isTrue();
910     SharedFile sharedFileAfterSecondUpdate = sharedFilesMetadata.read(newFileKey).get();
911     assertThat(sharedFileAfterSecondUpdate).isNotNull();
912     assertThat(sharedFileAfterSecondUpdate).isEqualTo(expectedSharedFileAfterUpdate);
913   }
914 
915   @Test
testUpdateMaxExpirationDateSecs_nonExistentFile()916   public void testUpdateMaxExpirationDateSecs_nonExistentFile() throws Exception {
917     DataFile file = MddTestUtil.createDataFile("fileId", 0);
918     NewFileKey newFileKey =
919         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
920 
921     ExecutionException ex =
922         Assert.assertThrows(
923             ExecutionException.class,
924             () ->
925                 sfm.updateMaxExpirationDateSecs(newFileKey, FILE_GROUP_EXPIRATION_DATE_SECS).get());
926     assertThat(ex).hasCauseThat().isInstanceOf(SharedFileMissingException.class);
927   }
928 
929   @Test
testSetAndroidSharedDownloadedFileEntry()930   public void testSetAndroidSharedDownloadedFileEntry() throws Exception {
931     DataFile file = MddTestUtil.createSharedDataFile("fileId", 0);
932     SharedFile expectedSharedFileAfterUpdate =
933         SharedFile.newBuilder()
934             .setFileStatus(FileStatus.DOWNLOAD_COMPLETE)
935             .setFileName("android_shared_" + file.getAndroidSharingChecksum())
936             .setAndroidShared(true)
937             .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS)
938             .setAndroidSharingChecksum(file.getAndroidSharingChecksum())
939             .build();
940     NewFileKey newFileKey =
941         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
942     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
943 
944     SharedFile sharedFile = sharedFilesMetadata.read(newFileKey).get();
945     assertThat(sharedFile).isNotNull();
946     assertThat(sharedFile).isNotEqualTo(expectedSharedFileAfterUpdate);
947 
948     assertThat(
949             sfm.setAndroidSharedDownloadedFileEntry(
950                     newFileKey, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS)
951                 .get())
952         .isTrue();
953     sharedFile = sharedFilesMetadata.read(newFileKey).get();
954     assertThat(sharedFile).isNotNull();
955     assertThat(sharedFile).isEqualTo(expectedSharedFileAfterUpdate);
956   }
957 
958   @Test
testOnDeviceUri()959   public void testOnDeviceUri() throws Exception {
960     DataFile file = MddTestUtil.createSharedDataFile("fileId", 0);
961     NewFileKey newFileKey =
962         SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS);
963 
964     assertThat(sfm.reserveFileEntry(newFileKey).get()).isTrue();
965 
966     File onDeviceFile = simulateDownload(file, getLastFileName(), AllowedReaders.ALL_GOOGLE_APPS);
967     assertThat(sfm.getOnDeviceUri(newFileKey).get())
968         .isEqualTo(AndroidUri.builder(context).fromFile(onDeviceFile).build());
969 
970     assertThat(
971             sfm.setAndroidSharedDownloadedFileEntry(
972                     newFileKey, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS)
973                 .get())
974         .isTrue();
975     assertThat(sfm.getOnDeviceUri(newFileKey).get())
976         .isEqualTo(
977             BlobUri.builder(context).setBlobParameters(file.getAndroidSharingChecksum()).build());
978   }
979 
simulateDownload(DataFile dataFile, String fileName, AllowedReaders allowedReaders)980   private File simulateDownload(DataFile dataFile, String fileName, AllowedReaders allowedReaders)
981       throws IOException {
982     File onDeviceFile;
983     if (allowedReaders == AllowedReaders.ALL_GOOGLE_APPS) {
984       onDeviceFile = new File(publicDirectory, fileName);
985     } else {
986       onDeviceFile = new File(privateDirectory, fileName);
987     }
988     FileOutputStream writer = new FileOutputStream(onDeviceFile);
989     byte[] bytes = new byte[dataFile.getByteSize()];
990     writer.write(bytes);
991     writer.close();
992 
993     return onDeviceFile;
994   }
995 
changeFileStatusAs(NewFileKey newFileKey, FileStatus fileStatus)996   private void changeFileStatusAs(NewFileKey newFileKey, FileStatus fileStatus)
997       throws InterruptedException, ExecutionException {
998     synchronized (SharedFilesMetadata.class) {
999       SharedFile sharedFile = sharedFilesMetadata.read(newFileKey).get();
1000       sharedFile = sharedFile.toBuilder().setFileStatus(fileStatus).build();
1001       assertThat(sharedFilesMetadata.write(newFileKey, sharedFile).get()).isTrue();
1002     }
1003   }
1004 
getLastFileName()1005   private String getLastFileName() {
1006     SharedPreferences sfmMetadata =
1007         SharedPreferencesUtil.getSharedPreferences(
1008             context, MDD_SHARED_FILE_MANAGER_METADATA, Optional.absent());
1009     long lastName = sfmMetadata.getLong(SharedFileManager.PREFS_KEY_NEXT_FILE_NAME, 1) - 1;
1010     return SharedFileManager.FILE_NAME_PREFIX + lastName;
1011   }
1012 }
1013