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