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 android.app.role.cts; 18 19 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; 20 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 21 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.junit.Assert.fail; 25 import static org.junit.Assume.assumeTrue; 26 27 import android.app.Activity; 28 import android.app.AppOpsManager; 29 import android.app.Instrumentation; 30 import android.app.role.OnRoleHoldersChangedListener; 31 import android.app.role.RoleManager; 32 import android.content.ComponentName; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.pm.PackageManager; 36 import android.content.pm.PermissionInfo; 37 import android.os.Process; 38 import android.os.UserHandle; 39 import android.provider.Telephony; 40 import android.support.test.uiautomator.By; 41 import android.support.test.uiautomator.UiDevice; 42 import android.support.test.uiautomator.UiObject2; 43 import android.support.test.uiautomator.Until; 44 import android.telecom.TelecomManager; 45 import android.util.Log; 46 import android.util.Pair; 47 48 import androidx.annotation.NonNull; 49 import androidx.annotation.Nullable; 50 import androidx.test.InstrumentationRegistry; 51 import androidx.test.filters.FlakyTest; 52 import androidx.test.rule.ActivityTestRule; 53 import androidx.test.runner.AndroidJUnit4; 54 55 import com.android.compatibility.common.util.AppOpsUtils; 56 import com.android.compatibility.common.util.TestUtils; 57 import com.android.compatibility.common.util.ThrowingRunnable; 58 59 import org.junit.After; 60 import org.junit.Before; 61 import org.junit.Rule; 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 65 import java.io.ByteArrayOutputStream; 66 import java.io.IOException; 67 import java.nio.charset.StandardCharsets; 68 import java.util.Collections; 69 import java.util.List; 70 import java.util.Objects; 71 import java.util.concurrent.CompletableFuture; 72 import java.util.concurrent.TimeUnit; 73 import java.util.concurrent.TimeoutException; 74 import java.util.function.Consumer; 75 76 /** 77 * Tests {@link RoleManager}. 78 */ 79 @RunWith(AndroidJUnit4.class) 80 public class RoleManagerTest { 81 82 private static final String LOG_TAG = "RoleManagerTest"; 83 84 private static final long TIMEOUT_MILLIS = 15 * 1000; 85 86 private static final long UNEXPECTED_TIMEOUT_MILLIS = 1000; 87 88 private static final String ROLE_NAME = RoleManager.ROLE_BROWSER; 89 90 private static final String APP_APK_PATH = "/data/local/tmp/cts/role/CtsRoleTestApp.apk"; 91 private static final String APP_PACKAGE_NAME = "android.app.role.cts.app"; 92 private static final String APP_IS_ROLE_HELD_ACTIVITY_NAME = APP_PACKAGE_NAME 93 + ".IsRoleHeldActivity"; 94 private static final String APP_IS_ROLE_HELD_EXTRA_IS_ROLE_HELD = APP_PACKAGE_NAME 95 + ".extra.IS_ROLE_HELD"; 96 private static final String APP_REQUEST_ROLE_ACTIVITY_NAME = APP_PACKAGE_NAME 97 + ".RequestRoleActivity"; 98 private static final String APP_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME = APP_PACKAGE_NAME 99 + ".ChangeDefaultDialerActivity"; 100 private static final String APP_CHANGE_DEFAULT_SMS_ACTIVITY_NAME = APP_PACKAGE_NAME 101 + ".ChangeDefaultSmsActivity"; 102 103 private static final String APP_28_APK_PATH = "/data/local/tmp/cts/role/CtsRoleTestApp28.apk"; 104 private static final String APP_28_PACKAGE_NAME = "android.app.role.cts.app28"; 105 private static final String APP_28_LABEL = "CtsRoleTestApp28"; 106 private static final String APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME = APP_28_PACKAGE_NAME 107 + ".ChangeDefaultDialerActivity"; 108 private static final String APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME = APP_28_PACKAGE_NAME 109 + ".ChangeDefaultSmsActivity"; 110 111 private static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER = 112 "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER"; 113 114 private static final Instrumentation sInstrumentation = 115 InstrumentationRegistry.getInstrumentation(); 116 private static final Context sContext = InstrumentationRegistry.getTargetContext(); 117 private static final PackageManager sPackageManager = sContext.getPackageManager(); 118 private static final RoleManager sRoleManager = sContext.getSystemService(RoleManager.class); 119 private static final UiDevice sUiDevice = UiDevice.getInstance(sInstrumentation); 120 121 @Rule 122 public ActivityTestRule<WaitForResultActivity> mActivityRule = 123 new ActivityTestRule<>(WaitForResultActivity.class); 124 125 private String mRoleHolder; 126 127 @Before saveRoleHolder()128 public void saveRoleHolder() throws Exception { 129 List<String> roleHolders = getRoleHolders(ROLE_NAME); 130 mRoleHolder = !roleHolders.isEmpty() ? roleHolders.get(0) : null; 131 132 if (Objects.equals(mRoleHolder, APP_PACKAGE_NAME)) { 133 removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 134 mRoleHolder = null; 135 } 136 } 137 138 @After restoreRoleHolder()139 public void restoreRoleHolder() throws Exception { 140 removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 141 142 if (mRoleHolder != null) { 143 addRoleHolder(ROLE_NAME, mRoleHolder); 144 } 145 146 assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false); 147 } 148 149 @Before installApp()150 public void installApp() throws Exception { 151 installPackage(APP_APK_PATH); 152 installPackage(APP_28_APK_PATH); 153 } 154 155 @After uninstallApp()156 public void uninstallApp() throws Exception { 157 uninstallPackage(APP_PACKAGE_NAME); 158 uninstallPackage(APP_28_PACKAGE_NAME); 159 } 160 161 @Before wakeUpScreen()162 public void wakeUpScreen() throws IOException { 163 runShellCommand(sInstrumentation, "input keyevent KEYCODE_WAKEUP"); 164 } 165 166 @Test requestRoleIntentHasPermissionControllerPackage()167 public void requestRoleIntentHasPermissionControllerPackage() throws Exception { 168 Intent intent = sRoleManager.createRequestRoleIntent(ROLE_NAME); 169 170 assertThat(intent.getPackage()).isEqualTo( 171 sPackageManager.getPermissionControllerPackageName()); 172 } 173 174 @Test requestRoleIntentHasExtraRoleName()175 public void requestRoleIntentHasExtraRoleName() throws Exception { 176 Intent intent = sRoleManager.createRequestRoleIntent(ROLE_NAME); 177 178 assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(ROLE_NAME); 179 } 180 181 @FlakyTest 182 @Test requestRoleAndDenyThenIsNotRoleHolder()183 public void requestRoleAndDenyThenIsNotRoleHolder() throws Exception { 184 requestRole(ROLE_NAME); 185 respondToRoleRequest(false); 186 187 assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false); 188 } 189 190 @FlakyTest 191 @Test requestRoleAndAllowThenIsRoleHolder()192 public void requestRoleAndAllowThenIsRoleHolder() throws Exception { 193 requestRole(ROLE_NAME); 194 respondToRoleRequest(true); 195 196 assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true); 197 } 198 199 @FlakyTest 200 @Test requestRoleFirstTimeNoDontAskAgain()201 public void requestRoleFirstTimeNoDontAskAgain() throws Exception { 202 requestRole(ROLE_NAME); 203 UiObject2 dontAskAgainCheck = findDontAskAgainCheck(false); 204 205 assertThat(dontAskAgainCheck).isNull(); 206 207 respondToRoleRequest(false); 208 } 209 210 @FlakyTest 211 @Test requestRoleAndDenyThenHasDontAskAgain()212 public void requestRoleAndDenyThenHasDontAskAgain() throws Exception { 213 requestRole(ROLE_NAME); 214 respondToRoleRequest(false); 215 216 requestRole(ROLE_NAME); 217 UiObject2 dontAskAgainCheck = findDontAskAgainCheck(); 218 219 assertThat(dontAskAgainCheck).isNotNull(); 220 221 respondToRoleRequest(false); 222 } 223 224 @FlakyTest 225 @Test requestRoleAndDenyWithDontAskAgainReturnsCanceled()226 public void requestRoleAndDenyWithDontAskAgainReturnsCanceled() throws Exception { 227 requestRole(ROLE_NAME); 228 respondToRoleRequest(false); 229 230 requestRole(ROLE_NAME); 231 findDontAskAgainCheck().click(); 232 Pair<Integer, Intent> result = clickButtonAndWaitForResult(true); 233 234 assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED); 235 } 236 237 @FlakyTest 238 @Test requestRoleAndDenyWithDontAskAgainThenDeniedAutomatically()239 public void requestRoleAndDenyWithDontAskAgainThenDeniedAutomatically() throws Exception { 240 requestRole(ROLE_NAME); 241 respondToRoleRequest(false); 242 243 requestRole(ROLE_NAME); 244 findDontAskAgainCheck().click(); 245 clickButtonAndWaitForResult(true); 246 247 requestRole(ROLE_NAME); 248 Pair<Integer, Intent> result = waitForResult(); 249 250 assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED); 251 } 252 253 @FlakyTest 254 @Test requestRoleAndDenyWithDontAskAgainAndClearDataThenShowsUiWithoutDontAskAgain()255 public void requestRoleAndDenyWithDontAskAgainAndClearDataThenShowsUiWithoutDontAskAgain() 256 throws Exception { 257 requestRole(ROLE_NAME); 258 respondToRoleRequest(false); 259 260 requestRole(ROLE_NAME); 261 findDontAskAgainCheck().click(); 262 clickButtonAndWaitForResult(true); 263 264 clearPackageData(APP_PACKAGE_NAME); 265 // Wait for the don't ask again to be forgotten. 266 Thread.sleep(5000); 267 268 requestRole(ROLE_NAME); 269 UiObject2 dontAskAgainCheck = findDontAskAgainCheck(false); 270 271 assertThat(dontAskAgainCheck).isNull(); 272 273 respondToRoleRequest(false); 274 } 275 276 @FlakyTest 277 @Test requestRoleAndDenyWithDontAskAgainAndReinstallThenShowsUiWithoutDontAskAgain()278 public void requestRoleAndDenyWithDontAskAgainAndReinstallThenShowsUiWithoutDontAskAgain() 279 throws Exception { 280 requestRole(ROLE_NAME); 281 respondToRoleRequest(false); 282 283 requestRole(ROLE_NAME); 284 findDontAskAgainCheck().click(); 285 clickButtonAndWaitForResult(true); 286 287 uninstallPackage(APP_PACKAGE_NAME); 288 // Wait for the don't ask again to be forgotten. 289 Thread.sleep(5000); 290 installPackage(APP_APK_PATH); 291 292 requestRole(ROLE_NAME); 293 UiObject2 dontAskAgainCheck = findDontAskAgainCheck(false); 294 295 assertThat(dontAskAgainCheck).isNull(); 296 297 respondToRoleRequest(false); 298 } 299 300 @Test requestAssistantRoleThenDeniedAutomatically()301 public void requestAssistantRoleThenDeniedAutomatically() throws InterruptedException { 302 requestRole(RoleManager.ROLE_ASSISTANT); 303 Pair<Integer, Intent> result = waitForResult(); 304 305 assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED); 306 } 307 requestRole(@onNull String roleName)308 private void requestRole(@NonNull String roleName) { 309 Intent intent = new Intent() 310 .setComponent(new ComponentName(APP_PACKAGE_NAME, APP_REQUEST_ROLE_ACTIVITY_NAME)) 311 .putExtra(Intent.EXTRA_ROLE_NAME, roleName); 312 mActivityRule.getActivity().startActivityToWaitForResult(intent); 313 } 314 respondToRoleRequest(boolean allow)315 private void respondToRoleRequest(boolean allow) throws InterruptedException, IOException { 316 if (allow) { 317 UiObject2 item = sUiDevice.wait(Until.findObject(By.text(APP_PACKAGE_NAME)), 318 TIMEOUT_MILLIS); 319 if (item == null) { 320 dumpWindowHierarchy(); 321 fail("Cannot find item to click"); 322 } 323 item.click(); 324 } 325 Pair<Integer, Intent> result = clickButtonAndWaitForResult(allow); 326 int expectedResult = allow ? Activity.RESULT_OK : Activity.RESULT_CANCELED; 327 328 assertThat(result.first).isEqualTo(expectedResult); 329 } 330 331 @Nullable findDontAskAgainCheck(boolean expected)332 private UiObject2 findDontAskAgainCheck(boolean expected) { 333 return sUiDevice.wait(Until.findObject(By.text("Don\u2019t ask again")), expected 334 ? TIMEOUT_MILLIS : UNEXPECTED_TIMEOUT_MILLIS); 335 } 336 337 @Nullable findDontAskAgainCheck()338 private UiObject2 findDontAskAgainCheck() { 339 return findDontAskAgainCheck(true); 340 } 341 342 @NonNull clickButtonAndWaitForResult(boolean positive)343 private Pair<Integer, Intent> clickButtonAndWaitForResult(boolean positive) throws IOException, 344 InterruptedException { 345 String buttonId = positive ? "android:id/button1" : "android:id/button2"; 346 UiObject2 button = sUiDevice.wait(Until.findObject(By.res(buttonId)), TIMEOUT_MILLIS); 347 if (button == null) { 348 dumpWindowHierarchy(); 349 fail("Cannot find button to click"); 350 } 351 button.click(); 352 return waitForResult(); 353 } 354 dumpWindowHierarchy()355 private void dumpWindowHierarchy() throws InterruptedException, IOException { 356 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 357 sUiDevice.dumpWindowHierarchy(outputStream); 358 String windowHierarchy = outputStream.toString(StandardCharsets.UTF_8.name()); 359 360 Log.w(LOG_TAG, "Window hierarchy:"); 361 for (String line : windowHierarchy.split("\n")) { 362 Thread.sleep(10); 363 Log.w(LOG_TAG, line); 364 } 365 } 366 367 @NonNull waitForResult()368 private Pair<Integer, Intent> waitForResult() throws InterruptedException { 369 return mActivityRule.getActivity().waitForActivityResult(TIMEOUT_MILLIS); 370 } 371 clearPackageData(@onNull String packageName)372 private void clearPackageData(@NonNull String packageName) { 373 runShellCommand("pm clear --user " + Process.myUserHandle().getIdentifier() + " " 374 + packageName); 375 } 376 installPackage(@onNull String apkPath)377 private void installPackage(@NonNull String apkPath) { 378 runShellCommand("pm install -r --user " + Process.myUserHandle().getIdentifier() + " " 379 + apkPath); 380 } 381 uninstallPackage(@onNull String packageName)382 private void uninstallPackage(@NonNull String packageName) { 383 runShellCommand("pm uninstall --user " + Process.myUserHandle().getIdentifier() + " " 384 + packageName); 385 } 386 387 @Test targetCurrentSdkAndChangeDefaultDialerThenIsCanceled()388 public void targetCurrentSdkAndChangeDefaultDialerThenIsCanceled() throws Exception { 389 assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER)); 390 WaitForResultActivity activity = mActivityRule.getActivity(); 391 activity.startActivityToWaitForResult(new Intent() 392 .setComponent(new ComponentName(APP_PACKAGE_NAME, 393 APP_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME)) 394 .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)); 395 Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS); 396 assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED); 397 } 398 399 @Test targetCurrentSdkAndChangeDefaultSmsThenIsCanceled()400 public void targetCurrentSdkAndChangeDefaultSmsThenIsCanceled() throws Exception { 401 assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)); 402 WaitForResultActivity activity = mActivityRule.getActivity(); 403 activity.startActivityToWaitForResult(new Intent() 404 .setComponent(new ComponentName(APP_PACKAGE_NAME, 405 APP_CHANGE_DEFAULT_SMS_ACTIVITY_NAME)) 406 .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)); 407 Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS); 408 assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED); 409 } 410 411 @FlakyTest 412 @Test targetSdk28AndChangeDefaultDialerAndAllowThenIsDefaultDialer()413 public void targetSdk28AndChangeDefaultDialerAndAllowThenIsDefaultDialer() throws Exception { 414 assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER)); 415 sContext.startActivity(new Intent() 416 .setComponent(new ComponentName(APP_28_PACKAGE_NAME, 417 APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME)) 418 .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME) 419 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 420 allowRoleRequestForApp28(); 421 TelecomManager telecomManager = sContext.getSystemService(TelecomManager.class); 422 TestUtils.waitUntil("App is not set as default dialer app", () -> Objects.equals( 423 telecomManager.getDefaultDialerPackage(), APP_28_PACKAGE_NAME)); 424 } 425 426 @FlakyTest 427 @Test targetSdk28AndChangeDefaultSmsAndAllowThenIsDefaultSms()428 public void targetSdk28AndChangeDefaultSmsAndAllowThenIsDefaultSms() throws Exception { 429 assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)); 430 sContext.startActivity(new Intent() 431 .setComponent(new ComponentName(APP_28_PACKAGE_NAME, 432 APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME)) 433 .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME) 434 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 435 allowRoleRequestForApp28(); 436 TestUtils.waitUntil("App is not set as default sms app", () -> Objects.equals( 437 Telephony.Sms.getDefaultSmsPackage(sContext), APP_28_PACKAGE_NAME)); 438 } 439 allowRoleRequestForApp28()440 private void allowRoleRequestForApp28() throws InterruptedException, IOException { 441 UiObject2 item = sUiDevice.wait(Until.findObject(By.text(APP_28_LABEL)), TIMEOUT_MILLIS); 442 if (item == null) { 443 dumpWindowHierarchy(); 444 fail("Cannot find item to click"); 445 } 446 item.click(); 447 UiObject2 button = sUiDevice.wait(Until.findObject(By.res("android:id/button1")), 448 TIMEOUT_MILLIS); 449 if (button == null) { 450 dumpWindowHierarchy(); 451 fail("Cannot find button to click"); 452 } 453 button.click(); 454 } 455 456 @Test roleIsAvailable()457 public void roleIsAvailable() { 458 assertThat(sRoleManager.isRoleAvailable(ROLE_NAME)).isTrue(); 459 } 460 461 @Test dontAddRoleHolderThenRoleIsNotHeld()462 public void dontAddRoleHolderThenRoleIsNotHeld() throws Exception { 463 assertRoleIsHeld(ROLE_NAME, false); 464 } 465 466 @Test addRoleHolderThenRoleIsHeld()467 public void addRoleHolderThenRoleIsHeld() throws Exception { 468 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 469 470 assertRoleIsHeld(ROLE_NAME, true); 471 } 472 473 @Test addAndRemoveRoleHolderThenRoleIsNotHeld()474 public void addAndRemoveRoleHolderThenRoleIsNotHeld() throws Exception { 475 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 476 removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 477 478 assertRoleIsHeld(ROLE_NAME, false); 479 } 480 assertRoleIsHeld(@onNull String roleName, boolean isHeld)481 private void assertRoleIsHeld(@NonNull String roleName, boolean isHeld) 482 throws InterruptedException { 483 Intent intent = new Intent() 484 .setComponent(new ComponentName(APP_PACKAGE_NAME, APP_IS_ROLE_HELD_ACTIVITY_NAME)) 485 .putExtra(Intent.EXTRA_ROLE_NAME, roleName); 486 WaitForResultActivity activity = mActivityRule.getActivity(); 487 activity.startActivityToWaitForResult(intent); 488 Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS); 489 490 assertThat(result.first).isEqualTo(Activity.RESULT_OK); 491 assertThat(result.second).isNotNull(); 492 assertThat(result.second.hasExtra(APP_IS_ROLE_HELD_EXTRA_IS_ROLE_HELD)).isTrue(); 493 assertThat(result.second.getBooleanExtra(APP_IS_ROLE_HELD_EXTRA_IS_ROLE_HELD, false)) 494 .isEqualTo(isHeld); 495 } 496 497 @Test dontAddRoleHolderThenIsNotRoleHolder()498 public void dontAddRoleHolderThenIsNotRoleHolder() throws Exception { 499 assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false); 500 } 501 502 @Test addRoleHolderThenIsRoleHolder()503 public void addRoleHolderThenIsRoleHolder() throws Exception { 504 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 505 506 assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true); 507 } 508 509 @Test addAndRemoveRoleHolderThenIsNotRoleHolder()510 public void addAndRemoveRoleHolderThenIsNotRoleHolder() throws Exception { 511 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 512 removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 513 514 assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false); 515 } 516 517 @Test addAndClearRoleHoldersThenIsNotRoleHolder()518 public void addAndClearRoleHoldersThenIsNotRoleHolder() throws Exception { 519 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 520 clearRoleHolders(ROLE_NAME); 521 522 assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false); 523 } 524 525 @Test addOnRoleHoldersChangedListenerAndAddRoleHolderThenIsNotified()526 public void addOnRoleHoldersChangedListenerAndAddRoleHolderThenIsNotified() throws Exception { 527 assertOnRoleHoldersChangedListenerIsNotified(() -> addRoleHolder(ROLE_NAME, 528 APP_PACKAGE_NAME)); 529 } 530 531 @Test addOnRoleHoldersChangedListenerAndRemoveRoleHolderThenIsNotified()532 public void addOnRoleHoldersChangedListenerAndRemoveRoleHolderThenIsNotified() 533 throws Exception { 534 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 535 536 assertOnRoleHoldersChangedListenerIsNotified(() -> removeRoleHolder(ROLE_NAME, 537 APP_PACKAGE_NAME)); 538 } 539 540 @Test addOnRoleHoldersChangedListenerAndClearRoleHoldersThenIsNotified()541 public void addOnRoleHoldersChangedListenerAndClearRoleHoldersThenIsNotified() 542 throws Exception { 543 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 544 545 assertOnRoleHoldersChangedListenerIsNotified(() -> clearRoleHolders(ROLE_NAME)); 546 } 547 assertOnRoleHoldersChangedListenerIsNotified(@onNull ThrowingRunnable runnable)548 private void assertOnRoleHoldersChangedListenerIsNotified(@NonNull ThrowingRunnable runnable) 549 throws Exception { 550 ListenerFuture future = new ListenerFuture(); 551 UserHandle user = Process.myUserHandle(); 552 runWithShellPermissionIdentity(() -> sRoleManager.addOnRoleHoldersChangedListenerAsUser( 553 sContext.getMainExecutor(), future, user)); 554 Pair<String, UserHandle> result; 555 try { 556 runnable.run(); 557 result = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 558 } finally { 559 runWithShellPermissionIdentity(() -> 560 sRoleManager.removeOnRoleHoldersChangedListenerAsUser(future, user)); 561 } 562 563 assertThat(result.first).isEqualTo(ROLE_NAME); 564 assertThat(result.second).isEqualTo(user); 565 } 566 567 @Test addAndRemoveOnRoleHoldersChangedListenerAndAddRoleHolderThenIsNotNotified()568 public void addAndRemoveOnRoleHoldersChangedListenerAndAddRoleHolderThenIsNotNotified() 569 throws Exception { 570 ListenerFuture future = new ListenerFuture(); 571 UserHandle user = Process.myUserHandle(); 572 runWithShellPermissionIdentity(() -> { 573 sRoleManager.addOnRoleHoldersChangedListenerAsUser(sContext.getMainExecutor(), future, 574 user); 575 sRoleManager.removeOnRoleHoldersChangedListenerAsUser(future, user); 576 }); 577 addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME); 578 579 try { 580 future.get(UNEXPECTED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 581 } catch (TimeoutException e) { 582 // Expected 583 return; 584 } 585 throw new AssertionError("OnRoleHoldersChangedListener was notified after removal"); 586 } 587 588 @Test setRoleNamesFromControllerShouldRequireManageRolesFromControllerPermission()589 public void setRoleNamesFromControllerShouldRequireManageRolesFromControllerPermission() { 590 assertRequiresManageRolesFromControllerPermission( 591 () -> sRoleManager.setRoleNamesFromController(Collections.emptyList()), 592 "setRoleNamesFromController"); 593 } 594 595 @Test addRoleHolderFromControllerShouldRequireManageRolesFromControllerPermission()596 public void addRoleHolderFromControllerShouldRequireManageRolesFromControllerPermission() { 597 assertRequiresManageRolesFromControllerPermission( 598 () -> sRoleManager.addRoleHolderFromController(ROLE_NAME, APP_PACKAGE_NAME), 599 "addRoleHolderFromController"); 600 } 601 602 @Test removeRoleHolderFromControllerShouldRequireManageRolesFromControllerPermission()603 public void removeRoleHolderFromControllerShouldRequireManageRolesFromControllerPermission() { 604 assertRequiresManageRolesFromControllerPermission( 605 () -> sRoleManager.removeRoleHolderFromController(ROLE_NAME, APP_PACKAGE_NAME), 606 "removeRoleHolderFromController"); 607 } 608 609 @Test getHeldRolesFromControllerShouldRequireManageRolesFromControllerPermission()610 public void getHeldRolesFromControllerShouldRequireManageRolesFromControllerPermission() { 611 assertRequiresManageRolesFromControllerPermission( 612 () -> sRoleManager.getHeldRolesFromController(APP_PACKAGE_NAME), 613 "getHeldRolesFromController"); 614 } 615 assertRequiresManageRolesFromControllerPermission(@onNull Runnable runnable, @NonNull String methodName)616 private void assertRequiresManageRolesFromControllerPermission(@NonNull Runnable runnable, 617 @NonNull String methodName) { 618 try { 619 runnable.run(); 620 } catch (SecurityException e) { 621 if (e.getMessage().contains(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)) { 622 // Expected 623 return; 624 } 625 throw e; 626 } 627 fail("RoleManager." + methodName + "() should require " 628 + PERMISSION_MANAGE_ROLES_FROM_CONTROLLER); 629 } 630 631 @Test manageRoleFromsFromControllerPermissionShouldBeDeclaredByPermissionController()632 public void manageRoleFromsFromControllerPermissionShouldBeDeclaredByPermissionController() 633 throws PackageManager.NameNotFoundException { 634 PermissionInfo permissionInfo = sPackageManager.getPermissionInfo( 635 PERMISSION_MANAGE_ROLES_FROM_CONTROLLER, 0); 636 637 assertThat(permissionInfo.packageName).isEqualTo( 638 sPackageManager.getPermissionControllerPackageName()); 639 assertThat(permissionInfo.getProtection()).isEqualTo(PermissionInfo.PROTECTION_SIGNATURE); 640 assertThat(permissionInfo.getProtectionFlags()).isEqualTo(0); 641 } 642 643 @Test removeSmsRoleHolderThenDialerRoleAppOpIsNotDenied()644 public void removeSmsRoleHolderThenDialerRoleAppOpIsNotDenied() throws Exception { 645 if (!(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER) 646 && sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS))) { 647 return; 648 } 649 650 addRoleHolder(RoleManager.ROLE_DIALER, APP_PACKAGE_NAME); 651 addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME); 652 removeRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME); 653 654 assertThat(AppOpsUtils.getOpMode(APP_PACKAGE_NAME, AppOpsManager.OPSTR_SEND_SMS)) 655 .isEqualTo(AppOpsManager.MODE_ALLOWED); 656 } 657 658 @Test smsRoleHasHolder()659 public void smsRoleHasHolder() throws Exception { 660 if (!sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)) { 661 return; 662 } 663 664 assertThat(getRoleHolders(RoleManager.ROLE_SMS)).isNotEmpty(); 665 } 666 getRoleHolders(@onNull String roleName)667 private List<String> getRoleHolders(@NonNull String roleName) throws Exception { 668 return callWithShellPermissionIdentity(() -> sRoleManager.getRoleHolders(roleName)); 669 } 670 assertIsRoleHolder(@onNull String roleName, @NonNull String packageName, boolean shouldBeRoleHolder)671 private void assertIsRoleHolder(@NonNull String roleName, @NonNull String packageName, 672 boolean shouldBeRoleHolder) throws Exception { 673 List<String> packageNames = getRoleHolders(roleName); 674 675 if (shouldBeRoleHolder) { 676 assertThat(packageNames).contains(packageName); 677 } else { 678 assertThat(packageNames).doesNotContain(packageName); 679 } 680 } 681 addRoleHolder(@onNull String roleName, @NonNull String packageName)682 private void addRoleHolder(@NonNull String roleName, @NonNull String packageName) 683 throws Exception { 684 CallbackFuture future = new CallbackFuture(); 685 runWithShellPermissionIdentity(() -> sRoleManager.addRoleHolderAsUser(roleName, 686 packageName, 0, Process.myUserHandle(), sContext.getMainExecutor(), future)); 687 future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 688 } 689 removeRoleHolder(@onNull String roleName, @NonNull String packageName)690 private void removeRoleHolder(@NonNull String roleName, @NonNull String packageName) 691 throws Exception { 692 CallbackFuture future = new CallbackFuture(); 693 runWithShellPermissionIdentity(() -> sRoleManager.removeRoleHolderAsUser(roleName, 694 packageName, 0, Process.myUserHandle(), sContext.getMainExecutor(), future)); 695 future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 696 } 697 clearRoleHolders(@onNull String roleName)698 private void clearRoleHolders(@NonNull String roleName) throws Exception { 699 CallbackFuture future = new CallbackFuture(); 700 runWithShellPermissionIdentity(() -> sRoleManager.clearRoleHoldersAsUser(roleName, 0, 701 Process.myUserHandle(), sContext.getMainExecutor(), future)); 702 future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 703 } 704 705 private static class ListenerFuture extends CompletableFuture<Pair<String, UserHandle>> 706 implements OnRoleHoldersChangedListener { 707 708 @Override onRoleHoldersChanged(@onNull String roleName, @NonNull UserHandle user)709 public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { 710 complete(new Pair<>(roleName, user)); 711 } 712 } 713 714 private static class CallbackFuture extends CompletableFuture<Void> 715 implements Consumer<Boolean> { 716 717 @Override accept(Boolean successful)718 public void accept(Boolean successful) { 719 if (successful) { 720 complete(null); 721 } else { 722 completeExceptionally(new RuntimeException()); 723 } 724 } 725 } 726 } 727