1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.media; 18 19 import static com.android.providers.media.MediaGrants.FILE_ID_COLUMN; 20 import static com.android.providers.media.MediaGrants.PACKAGE_USER_ID_COLUMN; 21 import static com.android.providers.media.photopicker.data.ItemsProvider.getItemsUri; 22 import static com.android.providers.media.util.FileCreationUtils.buildValidPickerUri; 23 import static com.android.providers.media.util.FileCreationUtils.insertFileInResolver; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertNotNull; 27 import static org.junit.Assert.assertThrows; 28 import static org.junit.Assert.assertTrue; 29 30 import android.Manifest; 31 import android.content.ContentResolver; 32 import android.content.ContentUris; 33 import android.content.ContentValues; 34 import android.content.Context; 35 import android.database.Cursor; 36 import android.net.Uri; 37 import android.os.Process; 38 import android.os.UserHandle; 39 import android.provider.MediaStore; 40 41 import androidx.test.InstrumentationRegistry; 42 import androidx.test.runner.AndroidJUnit4; 43 44 import com.android.providers.media.photopicker.PickerSyncController; 45 import com.android.providers.media.photopicker.data.model.UserId; 46 47 import org.junit.Before; 48 import org.junit.BeforeClass; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 52 import java.util.ArrayList; 53 import java.util.List; 54 55 @RunWith(AndroidJUnit4.class) 56 public class MediaGrantsTest { 57 private Context mIsolatedContext; 58 private Context mContext; 59 private ContentResolver mIsolatedResolver; 60 private DatabaseHelper mExternalDatabase; 61 private MediaGrants mGrants; 62 63 private static final String TEST_OWNER_PACKAGE_NAME = "com.android.test.package"; 64 private static final String TEST_OWNER_PACKAGE_NAME2 = "com.android.test.package2"; 65 private static final int TEST_USER_ID = UserHandle.myUserId(); 66 67 private static final String PNG_MIME_TYPE = "image/png"; 68 69 @BeforeClass setUpClass()70 public static void setUpClass() { 71 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation() 72 .getUiAutomation() 73 .adoptShellPermissionIdentity( 74 Manifest.permission.LOG_COMPAT_CHANGE, 75 Manifest.permission.READ_COMPAT_CHANGE_CONFIG, 76 Manifest.permission.READ_DEVICE_CONFIG, 77 Manifest.permission.INTERACT_ACROSS_USERS, 78 Manifest.permission.WRITE_MEDIA_STORAGE, 79 Manifest.permission.MANAGE_EXTERNAL_STORAGE); 80 } 81 82 @Before 83 /** Clean up and old files / force a clean slate before each test case. */ setUp()84 public void setUp() { 85 if (mIsolatedResolver != null) { 86 // This is necessary, we wait for all unfinished tasks to finish before we create a 87 // new IsolatedContext. 88 MediaStore.waitForIdle(mIsolatedResolver); 89 } 90 91 mContext = InstrumentationRegistry.getTargetContext(); 92 mIsolatedContext = new IsolatedContext(mContext, "modern", /*asFuseThread*/ false); 93 mIsolatedResolver = mIsolatedContext.getContentResolver(); 94 mExternalDatabase = ((IsolatedContext) mIsolatedContext).getExternalDatabase(); 95 mGrants = new MediaGrants(mExternalDatabase); 96 } 97 98 @Test testAddMediaGrants()99 public void testAddMediaGrants() throws Exception { 100 101 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 102 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 103 List<Uri> uris = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 104 105 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris, TEST_USER_ID); 106 107 assertGrantExistsForPackage(fileId1, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 108 assertGrantExistsForPackage(fileId2, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 109 } 110 111 @Test testGetMediaGrantsForPackages()112 public void testGetMediaGrantsForPackages() throws Exception { 113 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 114 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 115 Long fileId3 = insertFileInResolver(mIsolatedResolver, "test_file3"); 116 List<Uri> uris1 = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 117 List<Uri> uris2 = List.of(buildValidPickerUri(fileId3)); 118 119 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris1, TEST_USER_ID); 120 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME2, uris2, TEST_USER_ID); 121 122 String[] mimeTypes = {PNG_MIME_TYPE}; 123 String[] volumes = {MediaStore.VOLUME_EXTERNAL_PRIMARY}; 124 125 List<Uri> fileUris = convertToListOfUri(mGrants.getMediaGrantsForPackages( 126 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes, volumes)); 127 128 List<Long> expectedFileIdsList = List.of(fileId1, fileId2); 129 130 assertEquals(fileUris.size(), expectedFileIdsList.size()); 131 for (Uri uri : fileUris) { 132 assertTrue(expectedFileIdsList.contains(Long.valueOf(ContentUris.parseId(uri)))); 133 } 134 135 List<Uri> fileUrisForTestPackage2 = convertToListOfUri(mGrants.getMediaGrantsForPackages( 136 new String[]{TEST_OWNER_PACKAGE_NAME2}, TEST_USER_ID, mimeTypes, volumes)); 137 138 List<Long> expectedFileIdsList2 = List.of(fileId3); 139 140 assertEquals(fileUrisForTestPackage2.size(), expectedFileIdsList2.size()); 141 for (Uri uri : fileUrisForTestPackage2) { 142 assertTrue(expectedFileIdsList2.contains(Long.valueOf(ContentUris.parseId(uri)))); 143 } 144 145 List<Uri> fileUrisForTestPackage3 = convertToListOfUri(mGrants.getMediaGrantsForPackages( 146 new String[]{"non.existent.package"}, TEST_USER_ID, mimeTypes, volumes)); 147 148 // assert no items are returned for an invalid package. 149 assertEquals(/* expected= */fileUrisForTestPackage3.size(), /* actual= */0); 150 } 151 152 @Test test_GetMediaGrantsForPackages_excludesIsTrashed()153 public void test_GetMediaGrantsForPackages_excludesIsTrashed() throws Exception { 154 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 155 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 156 List<Uri> uris1 = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 157 158 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris1, TEST_USER_ID); 159 160 String[] mimeTypes = {PNG_MIME_TYPE}; 161 String[] volumes = {MediaStore.VOLUME_EXTERNAL_PRIMARY}; 162 // Mark one of the files as trashed. 163 updateFileValues(fileId1, MediaStore.Files.FileColumns.IS_TRASHED, "1"); 164 165 List<Uri> fileUris = convertToListOfUri(mGrants.getMediaGrantsForPackages( 166 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes, volumes)); 167 168 // Now the 1st file with fileId1 should not be part of the returned grants. 169 List<Long> expectedFileIdsList = List.of(fileId2); 170 171 assertEquals(fileUris.size(), expectedFileIdsList.size()); 172 for (Uri uri : fileUris) { 173 assertTrue(expectedFileIdsList.contains(Long.valueOf(ContentUris.parseId(uri)))); 174 } 175 } 176 177 @Test test_GetMediaGrantsForPackages_excludesIsPending()178 public void test_GetMediaGrantsForPackages_excludesIsPending() throws Exception { 179 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 180 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 181 List<Uri> uris1 = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 182 183 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris1, TEST_USER_ID); 184 185 String[] mimeTypes = {PNG_MIME_TYPE}; 186 String[] volumes = {MediaStore.VOLUME_EXTERNAL_PRIMARY}; 187 // Mark one of the files as pending. 188 updateFileValues(fileId1, MediaStore.Files.FileColumns.IS_PENDING, "1"); 189 190 List<Uri> fileUris = convertToListOfUri(mGrants.getMediaGrantsForPackages( 191 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes, volumes)); 192 193 // Now the 1st file with fileId1 should not be part of the returned grants. 194 List<Long> expectedFileIdsList = List.of(fileId2); 195 196 assertEquals(fileUris.size(), expectedFileIdsList.size()); 197 for (Uri uri : fileUris) { 198 assertTrue(expectedFileIdsList.contains(Long.valueOf(ContentUris.parseId(uri)))); 199 } 200 } 201 202 @Test test_GetMediaGrantsForPackages_testMimeTypeFilter()203 public void test_GetMediaGrantsForPackages_testMimeTypeFilter() throws Exception { 204 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 205 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 206 List<Uri> uris1 = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 207 208 Long fileId3 = insertFileInResolver(mIsolatedResolver, "test_file3", "mp4"); 209 List<Uri> uris2 = List.of(buildValidPickerUri(fileId3)); 210 211 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris1, TEST_USER_ID); 212 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris2, TEST_USER_ID); 213 214 String[] volumes = {MediaStore.VOLUME_EXTERNAL_PRIMARY}; 215 216 // Test image only, should return 2 items. 217 String[] mimeTypes = {PNG_MIME_TYPE}; 218 219 List<Uri> fileUris = convertToListOfUri(mGrants.getMediaGrantsForPackages( 220 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes, volumes)); 221 222 List<Long> expectedFileIdsList = List.of(fileId1, fileId2); 223 assertEquals(fileUris.size(), expectedFileIdsList.size()); 224 for (Uri uri : fileUris) { 225 assertTrue(expectedFileIdsList.contains(Long.valueOf(ContentUris.parseId(uri)))); 226 } 227 228 // Test video only, should return 1 item. 229 String[] mimeTypes2 = {"video/mp4"}; 230 231 List<Uri> fileUris2 = convertToListOfUri(mGrants.getMediaGrantsForPackages( 232 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes2, volumes)); 233 List<Long> expectedFileIdsList2 = List.of(fileId3); 234 assertEquals(fileUris2.size(), expectedFileIdsList2.size()); 235 for (Uri uri : fileUris2) { 236 assertTrue(expectedFileIdsList2.contains(Long.valueOf(ContentUris.parseId(uri)))); 237 } 238 239 240 // Test jpeg mimeType, since no items with this mimeType is granted, empty list should be 241 // returned. 242 String[] mimeTypes3 = {"image/jpeg"}; 243 List<Uri> fileUris3 = convertToListOfUri(mGrants.getMediaGrantsForPackages( 244 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes3, volumes)); 245 assertTrue(fileUris3.isEmpty()); 246 } 247 248 @Test test_GetMediaGrantsForPackages_volume()249 public void test_GetMediaGrantsForPackages_volume() throws Exception { 250 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 251 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 252 List<Uri> uris1 = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 253 254 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris1, TEST_USER_ID); 255 256 String[] volumes = {"test_volume"}; 257 String[] mimeTypes = {PNG_MIME_TYPE}; 258 259 List<Uri> fileUris = convertToListOfUri(mGrants.getMediaGrantsForPackages( 260 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes, volumes)); 261 262 assertTrue(fileUris.isEmpty()); 263 } 264 265 @Test testRemoveMediaGrantsForPackages()266 public void testRemoveMediaGrantsForPackages() throws Exception { 267 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 268 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 269 Long fileId3 = insertFileInResolver(mIsolatedResolver, "test_file3"); 270 List<Uri> uris1 = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 271 List<Uri> uris2 = List.of(buildValidPickerUri(fileId3)); 272 273 // Add grants for 2 different packages. 274 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris1, TEST_USER_ID); 275 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME2, uris2, TEST_USER_ID); 276 277 String[] mimeTypes = {PNG_MIME_TYPE}; 278 String[] volumes = {MediaStore.VOLUME_EXTERNAL_PRIMARY}; 279 280 // Verify the grants for the first package were inserted. 281 List<Uri> fileUris = convertToListOfUri(mGrants.getMediaGrantsForPackages( 282 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, 283 mimeTypes, volumes)); 284 List<Long> expectedFileIdsList = List.of(fileId1, fileId2); 285 assertEquals(fileUris.size(), expectedFileIdsList.size()); 286 for (Uri uri : fileUris) { 287 assertTrue(expectedFileIdsList.contains(Long.valueOf(ContentUris.parseId(uri)))); 288 } 289 290 // Remove one of the 2 grants for TEST_OWNER_PACKAGE_NAME and verify the other grants is 291 // still present. 292 mGrants.removeMediaGrantsForPackage(new String[]{TEST_OWNER_PACKAGE_NAME}, 293 List.of(buildValidPickerUri(fileId1)), TEST_USER_ID); 294 List<Uri> fileUris3 = convertToListOfUri(mGrants.getMediaGrantsForPackages( 295 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes, volumes)); 296 assertEquals(1, fileUris3.size()); 297 assertEquals(fileId2, Long.valueOf(ContentUris.parseId(fileUris3.get(0)))); 298 299 300 // Verify grants of other packages are unaffected. 301 List<Uri> fileUrisForTestPackage2 = convertToListOfUri(mGrants.getMediaGrantsForPackages( 302 new String[]{TEST_OWNER_PACKAGE_NAME2}, TEST_USER_ID, mimeTypes, volumes)); 303 List<Long> expectedFileIdsList2 = List.of(fileId3); 304 assertEquals(fileUrisForTestPackage2.size(), expectedFileIdsList2.size()); 305 for (Uri uri : fileUrisForTestPackage2) { 306 assertTrue(expectedFileIdsList2.contains(Long.valueOf(ContentUris.parseId(uri)))); 307 } 308 } 309 310 @Test testRemoveMediaGrantsForPackagesLargerDataSet()311 public void testRemoveMediaGrantsForPackagesLargerDataSet() throws Exception { 312 List<Uri> inputFiles = new ArrayList<>(); 313 for (int itr = 1; itr < 110; itr++) { 314 inputFiles.add(buildValidPickerUri( 315 insertFileInResolver(mIsolatedResolver, "test_file" + itr))); 316 } 317 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, inputFiles, TEST_USER_ID); 318 319 String[] mimeTypes = {PNG_MIME_TYPE}; 320 String[] volumes = {MediaStore.VOLUME_EXTERNAL_PRIMARY}; 321 322 // The query used inside remove grants is batched by 50 ids, hence having a test like this 323 // would help ensure the batching worked perfectly. 324 mGrants.removeMediaGrantsForPackage(new String[]{TEST_OWNER_PACKAGE_NAME}, 325 inputFiles.subList(0, 101), TEST_USER_ID); 326 List<Uri> fileUris3 = convertToListOfUri(mGrants.getMediaGrantsForPackages( 327 new String[]{TEST_OWNER_PACKAGE_NAME}, TEST_USER_ID, mimeTypes, volumes)); 328 assertEquals(8, fileUris3.size()); 329 } 330 @Test testAddDuplicateMediaGrants()331 public void testAddDuplicateMediaGrants() throws Exception { 332 333 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 334 List<Uri> uris = List.of(buildValidPickerUri(fileId1)); 335 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris, TEST_USER_ID); 336 assertGrantExistsForPackage(fileId1, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 337 338 // Add the same grant again to ensure no database insert failure. 339 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris, TEST_USER_ID); 340 assertGrantExistsForPackage(fileId1, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 341 } 342 343 @Test testAddMediaGrantsRequiresPickerUri()344 public void testAddMediaGrantsRequiresPickerUri() throws Exception { 345 346 Uri invalidUri = 347 Uri.EMPTY 348 .buildUpon() 349 .scheme("content") 350 .encodedAuthority("some_authority") 351 .appendPath("path") 352 .appendPath("20180713") 353 .build(); 354 355 assertThrows( 356 IllegalArgumentException.class, 357 () -> { 358 mGrants.addMediaGrantsForPackage( 359 TEST_OWNER_PACKAGE_NAME, List.of(invalidUri), TEST_USER_ID); 360 }); 361 } 362 363 @Test removeAllMediaGrantsForPackage()364 public void removeAllMediaGrantsForPackage() throws Exception { 365 366 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 367 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 368 List<Uri> uris = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 369 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris, TEST_USER_ID); 370 371 assertGrantExistsForPackage(fileId1, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 372 assertGrantExistsForPackage(fileId2, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 373 374 int removed = 375 mGrants.removeAllMediaGrantsForPackages( 376 new String[] {TEST_OWNER_PACKAGE_NAME}, "test", TEST_USER_ID); 377 assertEquals(2, removed); 378 379 try (Cursor c = 380 mExternalDatabase.runWithTransaction( 381 (db) -> 382 db.query( 383 MediaGrants.MEDIA_GRANTS_TABLE, 384 new String[] { 385 MediaGrants.FILE_ID_COLUMN, 386 MediaGrants.OWNER_PACKAGE_NAME_COLUMN 387 }, 388 String.format( 389 "%s = '%s'", 390 MediaGrants.OWNER_PACKAGE_NAME_COLUMN, 391 TEST_OWNER_PACKAGE_NAME), 392 null, 393 null, 394 null, 395 null))) { 396 assertEquals(0, c.getCount()); 397 } 398 } 399 400 @Test removeAllMediaGrantsForMultiplePackages()401 public void removeAllMediaGrantsForMultiplePackages() throws Exception { 402 403 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 404 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 405 List<Uri> uris = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 406 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris, TEST_USER_ID); 407 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME2, uris, TEST_USER_ID); 408 409 assertGrantExistsForPackage(fileId1, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 410 assertGrantExistsForPackage(fileId2, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 411 assertGrantExistsForPackage(fileId1, TEST_OWNER_PACKAGE_NAME2, TEST_USER_ID); 412 assertGrantExistsForPackage(fileId2, TEST_OWNER_PACKAGE_NAME2, TEST_USER_ID); 413 414 int removed = 415 mGrants.removeAllMediaGrantsForPackages( 416 new String[] {TEST_OWNER_PACKAGE_NAME, TEST_OWNER_PACKAGE_NAME2}, 417 "test", 418 TEST_USER_ID); 419 assertEquals(4, removed); 420 421 try (Cursor c = 422 mExternalDatabase.runWithTransaction( 423 (db) -> 424 db.query( 425 MediaGrants.MEDIA_GRANTS_TABLE, 426 new String[] { 427 MediaGrants.FILE_ID_COLUMN, 428 MediaGrants.OWNER_PACKAGE_NAME_COLUMN 429 }, 430 String.format( 431 "%s = '%s'", 432 MediaGrants.OWNER_PACKAGE_NAME_COLUMN, 433 TEST_OWNER_PACKAGE_NAME), 434 null, 435 null, 436 null, 437 null))) { 438 assertEquals(0, c.getCount()); 439 } 440 441 try (Cursor c = 442 mExternalDatabase.runWithTransaction( 443 (db) -> 444 db.query( 445 MediaGrants.MEDIA_GRANTS_TABLE, 446 new String[] { 447 MediaGrants.FILE_ID_COLUMN, 448 MediaGrants.OWNER_PACKAGE_NAME_COLUMN 449 }, 450 String.format( 451 "%s = '%s'", 452 MediaGrants.OWNER_PACKAGE_NAME_COLUMN, 453 TEST_OWNER_PACKAGE_NAME2), 454 null, 455 null, 456 null, 457 null))) { 458 assertEquals(0, c.getCount()); 459 } 460 } 461 462 @Test removeAllMediaGrantsForPackageRequiresNonEmpty()463 public void removeAllMediaGrantsForPackageRequiresNonEmpty() throws Exception { 464 assertThrows( 465 IllegalArgumentException.class, 466 () -> { 467 mGrants.removeAllMediaGrantsForPackages(new String[]{}, "test", TEST_USER_ID); 468 }); 469 } 470 471 @Test removeAllMediaGrants()472 public void removeAllMediaGrants() throws Exception { 473 474 final String secondPackageName = "com.android.test.another.package"; 475 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 476 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 477 List<Uri> uris = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 478 mGrants.addMediaGrantsForPackage(TEST_OWNER_PACKAGE_NAME, uris, TEST_USER_ID); 479 mGrants.addMediaGrantsForPackage(secondPackageName, uris, TEST_USER_ID); 480 481 assertGrantExistsForPackage(fileId1, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 482 assertGrantExistsForPackage(fileId2, TEST_OWNER_PACKAGE_NAME, TEST_USER_ID); 483 assertGrantExistsForPackage(fileId1, secondPackageName, TEST_USER_ID); 484 assertGrantExistsForPackage(fileId2, secondPackageName, TEST_USER_ID); 485 486 int removed = mGrants.removeAllMediaGrants(); 487 assertEquals(4, removed); 488 489 try (Cursor c = 490 mExternalDatabase.runWithTransaction( 491 (db) -> 492 db.query( 493 MediaGrants.MEDIA_GRANTS_TABLE, 494 new String[] { 495 MediaGrants.FILE_ID_COLUMN, 496 MediaGrants.OWNER_PACKAGE_NAME_COLUMN 497 }, 498 null, 499 null, 500 null, 501 null, 502 null))) { 503 assertEquals(0, c.getCount()); 504 } 505 } 506 507 @Test addMediaGrantsIsPrivileged()508 public void addMediaGrantsIsPrivileged() throws Exception { 509 assertThrows( 510 SecurityException.class, 511 () -> { 512 MediaStore.grantMediaReadForPackage(mContext, 1234, List.of()); 513 }); 514 } 515 516 @Test mediaProviderUidCanAddMediaGrants()517 public void mediaProviderUidCanAddMediaGrants() throws Exception { 518 519 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 520 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 521 List<Uri> uris = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 522 // Use mIsolatedContext here to ensure we pass the security check. 523 MediaStore.grantMediaReadForPackage(mIsolatedContext, Process.myUid(), uris); 524 525 assertGrantExistsForPackage(fileId1, mContext.getPackageName(), TEST_USER_ID); 526 assertGrantExistsForPackage(fileId2, mContext.getPackageName(), TEST_USER_ID); 527 } 528 529 @Test test_generationGrantedExistsAndIsIncreasing_success()530 public void test_generationGrantedExistsAndIsIncreasing_success() throws Exception { 531 Long fileId1 = insertFileInResolver(mIsolatedResolver, "test_file1"); 532 Long fileId2 = insertFileInResolver(mIsolatedResolver, "test_file2"); 533 Long fileId3 = insertFileInResolver(mIsolatedResolver, "test_file3"); 534 535 List<Uri> uris = List.of(buildValidPickerUri(fileId1), buildValidPickerUri(fileId2)); 536 MediaStore.grantMediaReadForPackage(mIsolatedContext, Process.myUid(), uris); 537 // adding grants separately for fileId3 so that it has a different generation from fileId1 538 // and fileId2. 539 List<Uri> uris2 = List.of(buildValidPickerUri(fileId3)); 540 MediaStore.grantMediaReadForPackage(mIsolatedContext, Process.myUid(), uris2); 541 542 assertGrantExistsForPackage( 543 fileId1, 544 mContext.getPackageName(), 545 TEST_USER_ID); 546 assertGrantExistsForPackage( 547 fileId2, 548 mContext.getPackageName(), 549 TEST_USER_ID); 550 assertGrantExistsForPackage( 551 fileId3, 552 mContext.getPackageName(), 553 TEST_USER_ID); 554 555 long gen1 = getGenerationForMediaGrant(fileId1, 556 mContext.getPackageName(), 557 TEST_USER_ID); 558 long gen2 = getGenerationForMediaGrant(fileId2, 559 mContext.getPackageName(), 560 TEST_USER_ID); 561 long gen3 = getGenerationForMediaGrant(fileId3, 562 mContext.getPackageName(), 563 TEST_USER_ID); 564 // verify generation for items granted in the same session are equal. 565 assertEquals(gen2, gen1); 566 // verify generation are increasing. 567 assertTrue(gen1 < gen3); 568 } 569 570 /** 571 * Assert a media grant exists in the given database. 572 * 573 * @param fileId the corresponding files._id column value. 574 * @param packageName i.e. com.android.test.package 575 * @param userId the user id of the package. 576 */ 577 private void assertGrantExistsForPackage(Long fileId, String packageName, 578 int userId) { 579 try (Cursor c = getMediaGrantRow(fileId, packageName, userId)) { 580 assertNotNull(c); 581 assertEquals(1, c.getCount()); 582 Long fileIdValue; 583 String ownerValue; 584 assertTrue(c.moveToFirst()); 585 fileIdValue = c.getLong(c.getColumnIndex(MediaGrants.FILE_ID_COLUMN)); 586 ownerValue = c.getString(c.getColumnIndex(MediaGrants.OWNER_PACKAGE_NAME_COLUMN)); 587 long generationGranted = c.getLong( 588 c.getColumnIndex(MediaGrants.GENERATION_GRANTED)); 589 assertEquals(fileIdValue, fileId); 590 assertEquals(packageName, ownerValue); 591 assertTrue(generationGranted > 0); 592 } 593 } 594 getGenerationForMediaGrant(Long fileId, String packageName, int userId)595 private long getGenerationForMediaGrant(Long fileId, String packageName, 596 int userId) { 597 598 long generationGranted = -1; 599 try (Cursor c = getMediaGrantRow(fileId, packageName, userId)) { 600 assertNotNull(c); 601 assertEquals(1, c.getCount()); 602 assertTrue(c.moveToFirst()); 603 generationGranted = c.getLong( 604 c.getColumnIndex(MediaGrants.GENERATION_GRANTED)); 605 assertTrue(generationGranted >= 0); 606 607 } 608 return generationGranted; 609 } 610 getMediaGrantRow(Long fileId, String packageName, int userId)611 private Cursor getMediaGrantRow(Long fileId, String packageName, 612 int userId) { 613 return mExternalDatabase.runWithTransaction( 614 (db) -> 615 db.query( 616 MediaGrants.MEDIA_GRANTS_TABLE, 617 new String[]{ 618 MediaGrants.FILE_ID_COLUMN, 619 MediaGrants.OWNER_PACKAGE_NAME_COLUMN, 620 MediaGrants.PACKAGE_USER_ID_COLUMN, 621 MediaGrants.GENERATION_GRANTED 622 }, 623 String.format( 624 "%s = '%s' AND %s = %s AND %s = %s", 625 MediaGrants.OWNER_PACKAGE_NAME_COLUMN, 626 packageName, 627 MediaGrants.FILE_ID_COLUMN, 628 Long.toString(fileId), 629 MediaGrants.PACKAGE_USER_ID_COLUMN, 630 Integer.toString(userId)), 631 null, 632 null, 633 null, 634 null)); 635 } 636 convertToListOfUri(Cursor c)637 private List<Uri> convertToListOfUri(Cursor c) { 638 List<Uri> filesUriList = new ArrayList<>(0); 639 while (c.moveToNext()) { 640 final Integer file_id = c.getInt(c.getColumnIndexOrThrow(FILE_ID_COLUMN)); 641 final Integer userId = c.getInt( 642 c.getColumnIndexOrThrow(PACKAGE_USER_ID_COLUMN)); 643 // transforming ids to Item uris to use as a key in selection based features. 644 filesUriList.add(getItemsUri(String.valueOf(file_id), 645 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY, 646 UserId.of(UserHandle.of(userId)))); 647 } 648 return filesUriList; 649 } 650 651 /** 652 * Modify column value for the fileId passed in the parameters with the modifiedValue. 653 */ updateFileValues(Long fileId, String columnToBeModified, String modifiedValue)654 private void updateFileValues(Long fileId, String columnToBeModified, String modifiedValue) { 655 int numberOfUpdatedRows = mExternalDatabase.runWithTransaction( 656 (db) -> { 657 ContentValues updatedRowValue = new ContentValues(); 658 updatedRowValue.put(columnToBeModified, modifiedValue); 659 return db.update(MediaStore.Files.TABLE, 660 updatedRowValue, 661 String.format( 662 "%s = '%s'", 663 MediaStore.Files.FileColumns._ID, 664 Long.toString(fileId)), 665 null); 666 }); 667 assertEquals(/* expected */ 1, numberOfUpdatedRows); 668 } 669 } 670