1 /* 2 * Copyright (C) 2020 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 android.Manifest.permission.ACCESS_MEDIA_LOCATION; 20 import static android.Manifest.permission.MANAGE_APP_OPS_MODES; 21 import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; 22 import static android.Manifest.permission.MANAGE_MEDIA; 23 import static android.Manifest.permission.READ_EXTERNAL_STORAGE; 24 import static android.Manifest.permission.READ_MEDIA_AUDIO; 25 import static android.Manifest.permission.READ_MEDIA_IMAGES; 26 import static android.Manifest.permission.READ_MEDIA_VIDEO; 27 import static android.Manifest.permission.UPDATE_APP_OPS_STATS; 28 29 import static androidx.test.InstrumentationRegistry.getContext; 30 31 import static com.android.providers.media.PermissionActivity.VERB_FAVORITE; 32 import static com.android.providers.media.PermissionActivity.VERB_TRASH; 33 import static com.android.providers.media.PermissionActivity.VERB_UNFAVORITE; 34 import static com.android.providers.media.PermissionActivity.VERB_WRITE; 35 import static com.android.providers.media.PermissionActivity.shouldShowActionDialog; 36 import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMediaLocation; 37 import static com.android.providers.media.util.PermissionUtils.checkPermissionManageMedia; 38 import static com.android.providers.media.util.PermissionUtils.checkPermissionManager; 39 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadAudio; 40 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadImages; 41 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadStorage; 42 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadVideo; 43 import static com.android.providers.media.util.TestUtils.adoptShellPermission; 44 import static com.android.providers.media.util.TestUtils.dropShellPermission; 45 46 import static com.google.common.truth.Truth.assertThat; 47 48 import android.app.AppOpsManager; 49 import android.app.Instrumentation; 50 import android.content.ClipData; 51 import android.content.ContentValues; 52 import android.content.Context; 53 import android.content.Intent; 54 import android.net.Uri; 55 import android.os.Build; 56 import android.os.Environment; 57 import android.provider.MediaStore; 58 import android.text.TextUtils; 59 60 import androidx.annotation.NonNull; 61 import androidx.test.InstrumentationRegistry; 62 import androidx.test.filters.SdkSuppress; 63 import androidx.test.rule.ActivityTestRule; 64 import androidx.test.runner.AndroidJUnit4; 65 66 import com.android.providers.media.scan.MediaScannerTest; 67 68 import org.junit.Before; 69 import org.junit.Test; 70 import org.junit.runner.RunWith; 71 72 import java.io.File; 73 import java.util.HashSet; 74 import java.util.concurrent.TimeoutException; 75 76 /** 77 * We already have solid coverage of this logic in {@code CtsMediaProviderTestCases}, 78 * but the coverage system currently doesn't measure that, so we add the bare 79 * minimum local testing here to convince the tooling that it's covered. 80 */ 81 @RunWith(AndroidJUnit4.class) 82 public class PermissionActivityTest { 83 private static final String TEST_APP_PACKAGE_NAME = 84 "com.android.providers.media.testapp.permission"; 85 private static final String TEST_APP_33_PACKAGE_NAME = 86 "com.android.providers.media.testapp.permissionmedia"; 87 88 private static final String OP_ACCESS_MEDIA_LOCATION = 89 AppOpsManager.permissionToOp(ACCESS_MEDIA_LOCATION); 90 private static final String OP_MANAGE_MEDIA = 91 AppOpsManager.permissionToOp(MANAGE_MEDIA); 92 private static final String OP_MANAGE_EXTERNAL_STORAGE = 93 AppOpsManager.permissionToOp(MANAGE_EXTERNAL_STORAGE); 94 private static final String OP_READ_EXTERNAL_STORAGE = 95 AppOpsManager.permissionToOp(READ_EXTERNAL_STORAGE); 96 private static final String OP_READ_MEDIA_IMAGES = 97 AppOpsManager.permissionToOp(READ_MEDIA_IMAGES); 98 private static final String OP_READ_MEDIA_AUDIO = 99 AppOpsManager.permissionToOp(READ_MEDIA_AUDIO); 100 private static final String OP_READ_MEDIA_VIDEO = 101 AppOpsManager.permissionToOp(READ_MEDIA_VIDEO); 102 103 // The list is used to restore the permissions after the test is finished. 104 // The default value for these app ops is {@link AppOpsManager#MODE_DEFAULT} 105 private static final String[] DEFAULT_OP_PERMISSION_LIST = new String[] { 106 OP_MANAGE_EXTERNAL_STORAGE, 107 OP_MANAGE_MEDIA 108 }; 109 110 // The list is used to restore the permissions after the test is finished. 111 // The default value for these app ops is {@link AppOpsManager#MODE_ALLOWED} 112 private static final String[] ALLOWED_OP_PERMISSION_LIST = new String[] { 113 OP_ACCESS_MEDIA_LOCATION, 114 OP_READ_EXTERNAL_STORAGE 115 }; 116 117 private static final long TIMEOUT_MILLIS = 3000; 118 private static final long SLEEP_MILLIS = 30; 119 120 private static final int TEST_APP_PID = -1; 121 private int mTestAppUid = -1; 122 private int mTestAppUid33 = -1; 123 124 @Before setUp()125 public void setUp() throws Exception { 126 mTestAppUid = getContext().getPackageManager().getPackageUid(TEST_APP_PACKAGE_NAME, 0); 127 mTestAppUid33 = getContext().getPackageManager().getPackageUid(TEST_APP_33_PACKAGE_NAME, 0); 128 } 129 130 @Test testSimple()131 public void testSimple() throws Exception { 132 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 133 final Intent intent = new Intent(inst.getContext(), GetResultActivity.class); 134 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 135 136 final GetResultActivity activity = (GetResultActivity) inst.startActivitySync(intent); 137 activity.startActivityForResult(createIntent(), 42); 138 } 139 140 @Test testLaunchWithNoCallerInfoNoCrash()141 public void testLaunchWithNoCallerInfoNoCrash() throws Exception { 142 ActivityTestRule<PermissionActivity> activityTestRule = new ActivityTestRule<>( 143 PermissionActivity.class, /* initialTouchMode */ true, /* launchActivity */ false); 144 activityTestRule.launchActivity(new Intent()); 145 } 146 147 @Test testShouldShowActionDialog_favorite_false()148 public void testShouldShowActionDialog_favorite_false() throws Exception { 149 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 150 TEST_APP_PACKAGE_NAME, null, VERB_FAVORITE)).isFalse(); 151 } 152 153 @Test testShouldShowActionDialog_unfavorite_false()154 public void testShouldShowActionDialog_unfavorite_false() throws Exception { 155 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 156 TEST_APP_PACKAGE_NAME, null, VERB_UNFAVORITE)).isFalse(); 157 } 158 159 @Test 160 @SdkSuppress(minSdkVersion = 31, codeName = "S") testShouldShowActionDialog_noRESAndMES_true()161 public void testShouldShowActionDialog_noRESAndMES_true() throws Exception { 162 final String[] enableAppOpsList = {OP_MANAGE_MEDIA}; 163 final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE}; 164 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 165 166 try { 167 setupPermissions( 168 mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME); 169 170 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 171 TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isTrue(); 172 } finally { 173 restoreDefaultAppOpPermissions(mTestAppUid); 174 dropShellPermission(); 175 } 176 } 177 178 @Test 179 @SdkSuppress(minSdkVersion = 33, codeName = "T") testShouldShowActionDialog_noRMAAndMES_true_33()180 public void testShouldShowActionDialog_noRMAAndMES_true_33() throws Exception { 181 final String[] enableAppOpsList = 182 {OP_MANAGE_MEDIA, OP_READ_MEDIA_IMAGES, OP_READ_MEDIA_VIDEO}; 183 final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_AUDIO}; 184 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 185 186 try { 187 setupPermissions( 188 mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME); 189 190 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33, 191 TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH, 192 /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true, 193 /* shouldCheckReadImages */ false, /* shouldCheckReadVideo */ false, 194 /* mShouldCheckReadAudioOrReadVideo */ false, 195 /* isTargetSdkAtLeastT */ true)).isTrue(); 196 } finally { 197 restoreDefaultAppOpPermissions(mTestAppUid33); 198 dropShellPermission(); 199 } 200 } 201 202 @Test 203 @SdkSuppress(minSdkVersion = 33, codeName = "T") testShouldShowActionDialog_noRMIAndMES_true_33()204 public void testShouldShowActionDialog_noRMIAndMES_true_33() throws Exception { 205 final String[] enableAppOpsList = 206 {OP_MANAGE_MEDIA, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO}; 207 final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_IMAGES}; 208 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 209 210 try { 211 setupPermissions( 212 mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME); 213 214 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33, 215 TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH, 216 /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ false, 217 /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ false, 218 /* mShouldCheckReadAudioOrReadVideo */ false, 219 /* isTargetSdkAtLeastT */ true)).isTrue(); 220 } finally { 221 restoreDefaultAppOpPermissions(mTestAppUid33); 222 dropShellPermission(); 223 } 224 } 225 226 @Test 227 @SdkSuppress(minSdkVersion = 33, codeName = "T") testShouldShowActionDialog_noRMVAndMES_true_33()228 public void testShouldShowActionDialog_noRMVAndMES_true_33() throws Exception { 229 final String[] enableAppOpsList = 230 {OP_MANAGE_MEDIA, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_IMAGES}; 231 final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_VIDEO}; 232 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 233 234 try { 235 setupPermissions( 236 mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME); 237 238 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33, 239 TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH, 240 /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ false, 241 /* shouldCheckReadImages */ false, /* shouldCheckReadVideo */ true, 242 /* mShouldCheckReadAudioOrReadVideo */ false, 243 /* isTargetSdkAtLeastT */ true)).isTrue(); 244 } finally { 245 restoreDefaultAppOpPermissions(mTestAppUid33); 246 dropShellPermission(); 247 } 248 } 249 250 @Test 251 @SdkSuppress(minSdkVersion = 33, codeName = "T") testShouldShowActionDialogForSubtitle_noRMARMVAndMES_true_33()252 public void testShouldShowActionDialogForSubtitle_noRMARMVAndMES_true_33() throws Exception { 253 final String[] enableAppOpsList = 254 {OP_MANAGE_MEDIA, OP_READ_MEDIA_IMAGES}; 255 final String[] disableAppOpsList = 256 {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO}; 257 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 258 259 try { 260 setupPermissions( 261 mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME); 262 263 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33, 264 TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH, 265 /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ false, 266 /* shouldCheckReadImages */ false, /* shouldCheckReadVideo */ false, 267 /* mShouldCheckReadAudioOrReadVideo */ true, 268 /* isTargetSdkAtLeastT */ true)).isTrue(); 269 } finally { 270 restoreDefaultAppOpPermissions(mTestAppUid33); 271 dropShellPermission(); 272 } 273 } 274 275 @Test 276 @SdkSuppress(minSdkVersion = 31, codeName = "S") testShouldShowActionDialog_noMANAGE_MEDIA_true()277 public void testShouldShowActionDialog_noMANAGE_MEDIA_true() throws Exception { 278 final String[] enableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE}; 279 final String[] disableAppOpsList = {OP_MANAGE_MEDIA}; 280 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 281 282 try { 283 setupPermissions( 284 mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME); 285 286 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 287 TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isTrue(); 288 } finally { 289 restoreDefaultAppOpPermissions(mTestAppUid); 290 dropShellPermission(); 291 } 292 } 293 294 @Test 295 @SdkSuppress(minSdkVersion = 33, codeName = "T") testShouldShowActionDialog_noMANAGE_MEDIA_true_33()296 public void testShouldShowActionDialog_noMANAGE_MEDIA_true_33() throws Exception { 297 final String[] enableAppOpsList = { 298 OP_MANAGE_EXTERNAL_STORAGE, 299 OP_READ_MEDIA_AUDIO, 300 OP_READ_MEDIA_VIDEO, 301 OP_READ_MEDIA_IMAGES 302 }; 303 final String[] disableAppOpsList = {OP_MANAGE_MEDIA}; 304 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 305 306 try { 307 setupPermissions( 308 mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME); 309 310 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33, 311 TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH, 312 /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true, 313 /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ true, 314 /* mShouldCheckReadAudioOrReadVideo */ true, 315 /* isTargetSdkAtLeastT */ true)).isTrue(); 316 } finally { 317 restoreDefaultAppOpPermissions(mTestAppUid33); 318 dropShellPermission(); 319 } 320 } 321 322 @Test 323 @SdkSuppress(minSdkVersion = 31, codeName = "S") testShouldShowActionDialog_hasMMWithRES_false()324 public void testShouldShowActionDialog_hasMMWithRES_false() throws Exception { 325 final String[] enableAppOpsList = {OP_MANAGE_MEDIA, OP_READ_EXTERNAL_STORAGE}; 326 final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE}; 327 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 328 329 try { 330 setupPermissions( 331 mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME); 332 333 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 334 TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isFalse(); 335 } finally { 336 restoreDefaultAppOpPermissions(mTestAppUid); 337 dropShellPermission(); 338 } 339 } 340 341 @Test 342 @SdkSuppress(minSdkVersion = 33, codeName = "T") testShouldShowActionDialog_hasMMWithRM_false_33()343 public void testShouldShowActionDialog_hasMMWithRM_false_33() throws Exception { 344 final String[] enableAppOpsList = { 345 OP_MANAGE_MEDIA, OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO, OP_READ_MEDIA_IMAGES 346 }; 347 final String[] disableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE}; 348 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 349 350 try { 351 setupPermissions( 352 mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME); 353 354 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33, 355 TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH, 356 /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true, 357 /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ true, 358 /* mShouldCheckReadAudioOrReadVideo */ true, 359 /* isTargetSdkAtLeastT */ true)).isFalse(); 360 } finally { 361 restoreDefaultAppOpPermissions(mTestAppUid33); 362 dropShellPermission(); 363 } 364 } 365 366 @Test 367 @SdkSuppress(minSdkVersion = 31, codeName = "S") testShouldShowActionDialog_hasMMWithMES_false()368 public void testShouldShowActionDialog_hasMMWithMES_false() throws Exception { 369 final String[] enableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_MANAGE_MEDIA}; 370 final String[] disableAppOpsList = {OP_READ_EXTERNAL_STORAGE}; 371 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 372 373 try { 374 setupPermissions( 375 mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME); 376 377 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 378 TEST_APP_PACKAGE_NAME, null, VERB_TRASH)).isFalse(); 379 } finally { 380 restoreDefaultAppOpPermissions(mTestAppUid); 381 dropShellPermission(); 382 } 383 } 384 385 @Test 386 @SdkSuppress(minSdkVersion = 33, codeName = "T") testShouldShowActionDialog_hasMMWithMES_false_33()387 public void testShouldShowActionDialog_hasMMWithMES_false_33() throws Exception { 388 final String[] enableAppOpsList = {OP_MANAGE_EXTERNAL_STORAGE, OP_MANAGE_MEDIA}; 389 final String[] disableAppOpsList = { 390 OP_READ_MEDIA_AUDIO, OP_READ_MEDIA_VIDEO, OP_READ_MEDIA_IMAGES 391 }; 392 393 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 394 395 try { 396 setupPermissions( 397 mTestAppUid33, enableAppOpsList, disableAppOpsList, TEST_APP_33_PACKAGE_NAME); 398 399 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid33, 400 TEST_APP_33_PACKAGE_NAME, null, VERB_TRASH, 401 /* shouldCheckMediaPermissions */ true, /* shouldCheckReadAudio */ true, 402 /* shouldCheckReadImages */ true, /* shouldCheckReadVideo */ true, 403 /* mShouldCheckReadAudioOrReadVideo */ true, 404 /* isTargetSdkAtLeastT */ true)).isFalse(); 405 } finally { 406 restoreDefaultAppOpPermissions(mTestAppUid33); 407 dropShellPermission(); 408 } 409 } 410 411 @Test 412 @SdkSuppress(minSdkVersion = 31, codeName = "S") testShouldShowActionDialog_writeNoACCESS_MEDIA_LOCATION_true()413 public void testShouldShowActionDialog_writeNoACCESS_MEDIA_LOCATION_true() throws Exception { 414 final String[] enableAppOpsList = 415 {OP_MANAGE_EXTERNAL_STORAGE, OP_MANAGE_MEDIA, OP_READ_EXTERNAL_STORAGE}; 416 final String[] disableAppOpsList = {OP_ACCESS_MEDIA_LOCATION}; 417 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 418 419 try { 420 setupPermissions( 421 mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME); 422 423 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 424 TEST_APP_PACKAGE_NAME, null, VERB_WRITE)).isTrue(); 425 } finally { 426 restoreDefaultAppOpPermissions(mTestAppUid); 427 dropShellPermission(); 428 } 429 } 430 431 @Test 432 @SdkSuppress(minSdkVersion = 31, codeName = "S") testShouldShowActionDialog_writeHasACCESS_MEDIA_LOCATION_false()433 public void testShouldShowActionDialog_writeHasACCESS_MEDIA_LOCATION_false() throws Exception { 434 final String[] enableAppOpsList = { 435 OP_ACCESS_MEDIA_LOCATION, 436 OP_MANAGE_EXTERNAL_STORAGE, 437 OP_MANAGE_MEDIA, 438 OP_READ_EXTERNAL_STORAGE}; 439 final String[] disableAppOpsList = new String[]{}; 440 adoptShellPermission(UPDATE_APP_OPS_STATS, MANAGE_APP_OPS_MODES); 441 442 try { 443 setupPermissions( 444 mTestAppUid, enableAppOpsList, disableAppOpsList, TEST_APP_PACKAGE_NAME); 445 446 assertThat(shouldShowActionDialog(getContext(), TEST_APP_PID, mTestAppUid, 447 TEST_APP_PACKAGE_NAME, null, VERB_WRITE)).isFalse(); 448 } finally { 449 restoreDefaultAppOpPermissions(mTestAppUid); 450 dropShellPermission(); 451 } 452 } 453 setupPermissions(int uid, @NonNull String[] enableAppOpsList, @NonNull String[] disableAppOpsList, @NonNull String packageName)454 private static void setupPermissions(int uid, @NonNull String[] enableAppOpsList, 455 @NonNull String[] disableAppOpsList, @NonNull String packageName) throws Exception { 456 for (String op : enableAppOpsList) { 457 modifyAppOp(uid, op, AppOpsManager.MODE_ALLOWED); 458 } 459 460 for (String op : disableAppOpsList) { 461 modifyAppOp(uid, op, AppOpsManager.MODE_ERRORED); 462 } 463 464 pollForAppOpPermissions( 465 TEST_APP_PID, packageName, uid, enableAppOpsList, /* hasPermission= */ true); 466 pollForAppOpPermissions( 467 TEST_APP_PID, packageName, uid, disableAppOpsList, /* hasPermission= */ false); 468 } 469 restoreDefaultAppOpPermissions(int uid)470 private static void restoreDefaultAppOpPermissions(int uid) { 471 for (String op : DEFAULT_OP_PERMISSION_LIST) { 472 modifyAppOp(uid, op, AppOpsManager.MODE_DEFAULT); 473 } 474 475 for (String op : ALLOWED_OP_PERMISSION_LIST) { 476 modifyAppOp(uid, op, AppOpsManager.MODE_ALLOWED); 477 } 478 } 479 createIntent()480 private static Intent createIntent() throws Exception { 481 final Context context = InstrumentationRegistry.getContext(); 482 483 final File dir = Environment 484 .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); 485 final File file = MediaScannerTest.stage(R.raw.test_image, 486 new File(dir, "test" + System.nanoTime() + ".jpg")); 487 final Uri uri = MediaStore.scanFile(context.getContentResolver(), file); 488 489 final Intent intent = new Intent(MediaStore.CREATE_WRITE_REQUEST_CALL, null, 490 context, PermissionActivity.class); 491 intent.putExtra(MediaStore.EXTRA_CLIP_DATA, ClipData.newRawUri("", uri)); 492 intent.putExtra(MediaStore.EXTRA_CONTENT_VALUES, new ContentValues()); 493 return intent; 494 } 495 modifyAppOp(int uid, @NonNull String op, int mode)496 private static void modifyAppOp(int uid, @NonNull String op, int mode) { 497 getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode); 498 } 499 pollForAppOpPermissions(int pid, @NonNull String packageName, int uid, String[] opList, boolean hasPermission)500 private static void pollForAppOpPermissions(int pid, @NonNull String packageName, int uid, 501 String[] opList, boolean hasPermission) throws Exception { 502 long current = System.currentTimeMillis(); 503 final long timeout = current + TIMEOUT_MILLIS; 504 final HashSet<String> checkedOpSet = new HashSet<>(); 505 506 while (current < timeout && checkedOpSet.size() < opList.length) { 507 for (String op : opList) { 508 if (!checkedOpSet.contains(op) 509 && checkPermission(op, pid, uid, packageName, hasPermission)) { 510 checkedOpSet.add(op); 511 continue; 512 } 513 } 514 Thread.sleep(SLEEP_MILLIS); 515 current = System.currentTimeMillis(); 516 } 517 518 if (checkedOpSet.size() != opList.length) { 519 throw new TimeoutException("Check AppOp permissions with " + uid + " timeout"); 520 } 521 } 522 checkPermission(@onNull String op, int pid, int uid, @NonNull String packageName, boolean expected)523 private static boolean checkPermission(@NonNull String op, int pid, int uid, 524 @NonNull String packageName, boolean expected) throws Exception { 525 final Context context = getContext(); 526 527 if (TextUtils.equals(op, OP_READ_EXTERNAL_STORAGE)) { 528 return expected == checkPermissionReadStorage(context, pid, uid, packageName, 529 /* attributionTag= */ null); 530 } else if (TextUtils.equals(op, OP_READ_MEDIA_IMAGES)) { 531 return expected == checkPermissionReadImages( 532 context, pid, uid, packageName, /* attributionTag= */ null, /* isAtleastT */ true); 533 } else if (TextUtils.equals(op, OP_READ_MEDIA_AUDIO)) { 534 return expected == checkPermissionReadAudio( 535 context, pid, uid, packageName, /* attributionTag= */ null, /* isAtleastT */ true); 536 } else if (TextUtils.equals(op, OP_READ_MEDIA_VIDEO)) { 537 return expected == checkPermissionReadVideo( 538 context, pid, uid, packageName, /* attributionTag= */ null, /* isAtleastT */ true); 539 } else if (TextUtils.equals(op, OP_MANAGE_EXTERNAL_STORAGE)) { 540 return expected == checkPermissionManager(context, pid, uid, packageName, 541 /* attributionTag= */ null); 542 } else if (TextUtils.equals(op, OP_MANAGE_MEDIA)) { 543 return expected == checkPermissionManageMedia(context, pid, uid, packageName, 544 /* attributionTag= */ null); 545 } else if (TextUtils.equals(op, OP_ACCESS_MEDIA_LOCATION)) { 546 final int targetSdk = context.getPackageManager() 547 .getApplicationInfo(packageName, 0).targetSdkVersion; 548 549 return expected == checkPermissionAccessMediaLocation(context, pid, uid, 550 packageName, /* attributionTag= */ null, 551 targetSdk >= Build.VERSION_CODES.TIRAMISU); 552 } else { 553 throw new IllegalArgumentException("checkPermission is not supported for op: " + op); 554 } 555 } 556 } 557