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