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