• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.systemui.accessibility.accessibilitymenu.tests;
18 
19 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN;
20 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS;
21 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_POWER_DIALOG;
22 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS;
23 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_RECENTS;
24 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT;
25 
26 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION;
27 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION_EXTRA;
28 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;
29 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_OPEN_BLOCKED;
30 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;
31 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME;
32 
33 import static com.google.common.truth.Truth.assertThat;
34 
35 import android.accessibilityservice.AccessibilityServiceInfo;
36 import android.app.Instrumentation;
37 import android.app.KeyguardManager;
38 import android.app.UiAutomation;
39 import android.content.BroadcastReceiver;
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.IntentFilter;
44 import android.hardware.display.BrightnessInfo;
45 import android.hardware.display.DisplayManager;
46 import android.media.AudioManager;
47 import android.os.PowerManager;
48 import android.os.UserManager;
49 import android.platform.uiautomatorhelpers.WaitUtils;
50 import android.provider.Settings;
51 import android.util.Log;
52 import android.view.Display;
53 import android.view.KeyEvent;
54 import android.view.accessibility.AccessibilityManager;
55 import android.view.accessibility.AccessibilityNodeInfo;
56 
57 import androidx.test.ext.junit.runners.AndroidJUnit4;
58 import androidx.test.platform.app.InstrumentationRegistry;
59 import androidx.test.uiautomator.Configurator;
60 import androidx.test.uiautomator.UiDevice;
61 
62 import com.android.compatibility.common.util.TestUtils;
63 import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId;
64 
65 import org.junit.After;
66 import org.junit.AfterClass;
67 import org.junit.Assume;
68 import org.junit.Before;
69 import org.junit.BeforeClass;
70 import org.junit.Test;
71 import org.junit.runner.RunWith;
72 
73 import java.io.IOException;
74 import java.util.List;
75 import java.util.concurrent.atomic.AtomicBoolean;
76 import java.util.concurrent.atomic.AtomicInteger;
77 
78 @RunWith(AndroidJUnit4.class)
79 public class AccessibilityMenuServiceTest {
80     private static final String TAG = "A11yMenuServiceTest";
81     private static final int CLICK_ID = AccessibilityNodeInfo.ACTION_CLICK;
82 
83     private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
84     private static final int TIMEOUT_UI_CHANGE_S = 5;
85     private static final int NO_GLOBAL_ACTION = -1;
86     private static final Intent INTENT_OPEN_MENU =
87             new Intent(INTENT_TOGGLE_MENU).setPackage(PACKAGE_NAME);
88     private static final String SERVICE_NAME = PACKAGE_NAME + "/.AccessibilityMenuService";
89 
90     private static Instrumentation sInstrumentation;
91     private static UiAutomation sUiAutomation;
92     private static UiDevice sUiDevice;
93     private static String sLockSettings;
94     private static final AtomicInteger sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION);
95     private static final AtomicBoolean sOpenBlocked = new AtomicBoolean(false);
96 
97     private static AccessibilityManager sAccessibilityManager;
98     private static PowerManager sPowerManager;
99     private static KeyguardManager sKeyguardManager;
100     private static DisplayManager sDisplayManager;
101 
102     @BeforeClass
classSetup()103     public static void classSetup() throws Throwable {
104         Configurator.getInstance()
105                 .setUiAutomationFlags(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
106         sInstrumentation = InstrumentationRegistry.getInstrumentation();
107         sUiAutomation = sInstrumentation.getUiAutomation(
108                 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
109         sUiAutomation.adoptShellPermissionIdentity(
110                 UiAutomation.ALL_PERMISSIONS.toArray(new String[0]));
111         sUiDevice = UiDevice.getInstance(sInstrumentation);
112         sLockSettings = sUiDevice.executeShellCommand("locksettings get-disabled");
113         Log.i(TAG, "locksettings get-disabled returns " + sLockSettings);
114         // Some test in the test class requires the device to be in lock screen
115         // ensure we have locksettings enabled before running the tests
116         sUiDevice.executeShellCommand("locksettings set-disabled false");
117 
118         final Context context = sInstrumentation.getTargetContext();
119         sAccessibilityManager = context.getSystemService(AccessibilityManager.class);
120         sPowerManager = context.getSystemService(PowerManager.class);
121         sKeyguardManager = context.getSystemService(KeyguardManager.class);
122         sDisplayManager = context.getSystemService(DisplayManager.class);
123         unlockSignal();
124 
125         enableA11yMenuService(context);
126 
127         context.registerReceiver(new BroadcastReceiver() {
128             @Override
129             public void onReceive(Context context, Intent intent) {
130                 Log.i(TAG, "Received global action intent.");
131                 sLastGlobalAction.set(
132                         intent.getIntExtra(INTENT_GLOBAL_ACTION_EXTRA, NO_GLOBAL_ACTION));
133             }},
134                 new IntentFilter(INTENT_GLOBAL_ACTION),
135                 null, null, Context.RECEIVER_EXPORTED);
136 
137         context.registerReceiver(new BroadcastReceiver() {
138             @Override
139             public void onReceive(Context context, Intent intent) {
140                 Log.i(TAG, "Received notification that menu cannot be opened.");
141                 sOpenBlocked.set(true);
142             }},
143                 new IntentFilter(INTENT_OPEN_BLOCKED),
144                 null, null, Context.RECEIVER_EXPORTED);
145     }
146 
147     @AfterClass
classTeardown()148     public static void classTeardown() throws IOException {
149         Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(),
150                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
151         sUiDevice.executeShellCommand("locksettings set-disabled " + sLockSettings);
152     }
153 
154     @Before
setup()155     public void setup() throws Throwable {
156         sOpenBlocked.set(false);
157     }
158 
159     @After
tearDown()160     public void tearDown() throws Throwable {
161         closeMenu();
162         sLastGlobalAction.set(NO_GLOBAL_ACTION);
163         // Leave the device in clean state when the test finished
164         unlockSignal();
165         // dismisses screenshot popup if present.
166         sUiDevice.pressBack();
167         sUiDevice.pressHome();
168     }
169 
enableA11yMenuService(Context context)170     private static void enableA11yMenuService(Context context) throws Throwable {
171         // Disable all a11yServices if any are active.
172         if (!sAccessibilityManager.getEnabledAccessibilityServiceList(
173                 AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
174             Settings.Secure.putString(context.getContentResolver(),
175                     Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
176             TestUtils.waitUntil("Failed to disable all services",
177                     TIMEOUT_SERVICE_STATUS_CHANGE_S,
178                     () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
179                             AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty());
180         }
181 
182         // Enable a11yMenu service.
183         Settings.Secure.putString(context.getContentResolver(),
184                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, SERVICE_NAME);
185 
186         TestUtils.waitUntil("Failed to enable service",
187                 TIMEOUT_SERVICE_STATUS_CHANGE_S,
188                 () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
189                         AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
190                                 info -> info.getId().contains(SERVICE_NAME)).count() == 1);
191     }
192 
isMenuVisible()193     private static boolean isMenuVisible() {
194         sUiDevice.waitForIdle();
195         AccessibilityNodeInfo root = sUiAutomation.getRootInActiveWindow();
196         return root != null && root.getPackageName().toString().equals(PACKAGE_NAME);
197     }
198 
wakeUpScreen()199     private static void wakeUpScreen() {
200         sUiDevice.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
201         WaitUtils.waitForValueToSettle("Screen On", AccessibilityMenuServiceTest::isScreenOn);
202         WaitUtils.ensureThat("Screen is on", AccessibilityMenuServiceTest::isScreenOn);
203     }
204 
closeScreen()205     private static void closeScreen() {
206         // go/adb-cheats#lock-screen
207         sUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP);
208         WaitUtils.waitForValueToSettle("Screen Off", AccessibilityMenuServiceTest::isScreenOff);
209         WaitUtils.ensureThat("Screen is off", AccessibilityMenuServiceTest::isScreenOff);
210         WaitUtils.ensureThat(
211                 "Screen is locked", () -> sKeyguardManager.isKeyguardLocked());
212     }
213 
openMenu()214     private static void openMenu() throws Throwable {
215         unlockSignal();
216         if (!isMenuVisible()) {
217             sInstrumentation.getTargetContext().sendBroadcast(INTENT_OPEN_MENU);
218             WaitUtils.ensureThat("Accessibility Menu is visible",
219                     AccessibilityMenuServiceTest::isMenuVisible);
220         }
221     }
222 
closeMenu()223     private static void closeMenu() throws Throwable {
224         if (!isMenuVisible()) {
225             return;
226         }
227         Intent intent = new Intent(INTENT_HIDE_MENU);
228         intent.setPackage(PACKAGE_NAME);
229         sInstrumentation.getContext().sendBroadcast(intent);
230         TestUtils.waitUntil("Timed out before menu could close.",
231                 TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());
232     }
233 
234     /**
235      * Provides list of all present shortcut buttons.
236      * @return List of shortcut buttons.
237      */
getGridButtonList()238     private List<AccessibilityNodeInfo> getGridButtonList() {
239         return sUiAutomation.getRootInActiveWindow()
240                         .findAccessibilityNodeInfosByViewId(PACKAGE_NAME + ":id/shortcutIconBtn");
241     }
242 
243     /**
244      * Returns the first button whose uniqueID matches the provided text.
245      * @param buttons List of buttons.
246      * @param text Text to match button's uniqueID to.
247      * @return Button whose uniqueID matches text, {@code null} otherwise.
248      */
findGridButtonInfo( List<AccessibilityNodeInfo> buttons, String text)249     private AccessibilityNodeInfo findGridButtonInfo(
250             List<AccessibilityNodeInfo> buttons, String text) {
251         for (AccessibilityNodeInfo button: buttons) {
252             if (button.getUniqueId().equals(text)) {
253                 return button;
254             }
255         }
256         return null;
257     }
258 
259     @Test
testAdjustBrightness()260     public void testAdjustBrightness() throws Throwable {
261         openMenu();
262         Context context = sInstrumentation.getTargetContext();
263         DisplayManager displayManager = context.getSystemService(
264                 DisplayManager.class);
265         float resetBrightness = displayManager.getBrightness(context.getDisplayId());
266 
267         List<AccessibilityNodeInfo> buttons = getGridButtonList();
268         AccessibilityNodeInfo brightnessUpButton = findGridButtonInfo(buttons,
269                 String.valueOf(ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()));
270         AccessibilityNodeInfo brightnessDownButton = findGridButtonInfo(buttons,
271                 String.valueOf(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()));
272 
273         BrightnessInfo brightnessInfo = displayManager.getDisplay(
274                 context.getDisplayId()).getBrightnessInfo();
275 
276         try {
277             TestUtils.waitUntil("Could not change to minimum brightness",
278                     TIMEOUT_UI_CHANGE_S,
279                     () -> {
280                         displayManager.setBrightness(
281                                 context.getDisplayId(), brightnessInfo.brightnessMinimum);
282                         return displayManager.getBrightness(context.getDisplayId())
283                                 == brightnessInfo.brightnessMinimum;
284                     });
285             brightnessUpButton.performAction(CLICK_ID);
286             TestUtils.waitUntil("Did not detect an increase in brightness.",
287                     TIMEOUT_UI_CHANGE_S,
288                     () -> displayManager.getBrightness(context.getDisplayId())
289                             > brightnessInfo.brightnessMinimum);
290 
291             TestUtils.waitUntil("Could not change to maximum brightness",
292                     TIMEOUT_UI_CHANGE_S,
293                     () -> {
294                         displayManager.setBrightness(
295                                 context.getDisplayId(), brightnessInfo.brightnessMaximum);
296                         return displayManager.getBrightness(context.getDisplayId())
297                                 == brightnessInfo.brightnessMaximum;
298                     });
299             brightnessDownButton.performAction(CLICK_ID);
300             TestUtils.waitUntil("Did not detect a decrease in brightness.",
301                     TIMEOUT_UI_CHANGE_S,
302                     () -> displayManager.getBrightness(context.getDisplayId())
303                             < brightnessInfo.brightnessMaximum);
304         } finally {
305             displayManager.setBrightness(context.getDisplayId(), resetBrightness);
306         }
307     }
308 
309     @Test
testAdjustVolume()310     public void testAdjustVolume() throws Throwable {
311         openMenu();
312         Context context = sInstrumentation.getTargetContext();
313         AudioManager audioManager = context.getSystemService(AudioManager.class);
314         int resetVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
315 
316         List<AccessibilityNodeInfo> buttons = getGridButtonList();
317         AccessibilityNodeInfo volumeUpButton = findGridButtonInfo(buttons,
318                 String.valueOf(ShortcutId.ID_VOLUME_UP_VALUE.ordinal()));
319         AccessibilityNodeInfo volumeDownButton = findGridButtonInfo(buttons,
320                 String.valueOf(ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()));
321 
322         try {
323             int min = audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC);
324             audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, min,
325                     AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
326             TestUtils.waitUntil("Could not change audio stream to minimum volume.",
327                     TIMEOUT_UI_CHANGE_S,
328                     () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) == min);
329             volumeUpButton.performAction(CLICK_ID);
330             TestUtils.waitUntil("Did not detect an increase in volume.",
331                     TIMEOUT_UI_CHANGE_S,
332                     () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) > min);
333 
334             int max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
335             audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, max,
336                     AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
337             TestUtils.waitUntil("Could not change audio stream to maximum volume.",
338                     TIMEOUT_UI_CHANGE_S,
339                     () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) == max);
340             volumeDownButton.performAction(CLICK_ID);
341             TestUtils.waitUntil("Did not detect a decrease in volume.",
342                     TIMEOUT_UI_CHANGE_S,
343                     () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) < max);
344         } finally {
345             audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
346                     resetVolume, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
347         }
348     }
349 
350     @Test
testAssistantButton_opensVoiceAssistant()351     public void testAssistantButton_opensVoiceAssistant() throws Throwable {
352         openMenu();
353         AccessibilityNodeInfo assistantButton = findGridButtonInfo(getGridButtonList(),
354                 String.valueOf(ShortcutId.ID_ASSISTANT_VALUE.ordinal()));
355         Intent expectedIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
356         ComponentName componentName = expectedIntent.resolveActivity(
357                 sInstrumentation.getContext().getPackageManager());
358         Assume.assumeNotNull(componentName);
359         String expectedPackage = componentName.getPackageName();
360 
361         sUiAutomation.executeAndWaitForEvent(
362                 () -> assistantButton.performAction(CLICK_ID),
363                 (event) ->
364                         event.getPackageName() != null
365                                 && expectedPackage.contains(event.getPackageName()),
366                 TIMEOUT_UI_CHANGE_S * 1000
367         );
368     }
369 
370     @Test
testAccessibilitySettingsButton_opensAccessibilitySettings()371     public void testAccessibilitySettingsButton_opensAccessibilitySettings() throws Throwable {
372         openMenu();
373         AccessibilityNodeInfo settingsButton = findGridButtonInfo(getGridButtonList(),
374                 String.valueOf(ShortcutId.ID_A11YSETTING_VALUE.ordinal()));
375         Intent expectedIntent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
376         String expectedPackage = expectedIntent.resolveActivity(
377                 sInstrumentation.getContext().getPackageManager()).getPackageName();
378 
379         sUiAutomation.executeAndWaitForEvent(
380                 () -> settingsButton.performAction(CLICK_ID),
381                 (event) ->
382                         event.getPackageName() != null
383                                 && expectedPackage.contains(event.getPackageName()),
384                 TIMEOUT_UI_CHANGE_S * 1000
385         );
386     }
387 
388     @Test
testPowerButton_performsGlobalAction()389     public void testPowerButton_performsGlobalAction() throws Throwable {
390         openMenu();
391         AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(),
392                 String.valueOf(ShortcutId.ID_POWER_VALUE.ordinal()));
393 
394         button.performAction(CLICK_ID);
395         TestUtils.waitUntil("Did not detect Power action being performed.",
396                 TIMEOUT_UI_CHANGE_S,
397                 () -> sLastGlobalAction.compareAndSet(
398                         GLOBAL_ACTION_POWER_DIALOG, NO_GLOBAL_ACTION));
399     }
400 
401     @Test
testRecentButton_performsGlobalAction()402     public void testRecentButton_performsGlobalAction() throws Throwable {
403         openMenu();
404         AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(),
405                 String.valueOf(ShortcutId.ID_RECENT_VALUE.ordinal()));
406 
407         button.performAction(CLICK_ID);
408         TestUtils.waitUntil("Did not detect Recents action being performed.",
409                 TIMEOUT_UI_CHANGE_S,
410                 () -> sLastGlobalAction.compareAndSet(
411                         GLOBAL_ACTION_RECENTS, NO_GLOBAL_ACTION));
412     }
413 
414     @Test
testLockButton_performsGlobalAction()415     public void testLockButton_performsGlobalAction() throws Throwable {
416         openMenu();
417         AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(),
418                 String.valueOf(ShortcutId.ID_LOCKSCREEN_VALUE.ordinal()));
419 
420         button.performAction(CLICK_ID);
421         TestUtils.waitUntil("Did not detect Lock action being performed.",
422                 TIMEOUT_UI_CHANGE_S,
423                 () -> sLastGlobalAction.compareAndSet(
424                         GLOBAL_ACTION_LOCK_SCREEN, NO_GLOBAL_ACTION));
425     }
426 
427     @Test
testQuickSettingsButton_performsGlobalAction()428     public void testQuickSettingsButton_performsGlobalAction() throws Throwable {
429         openMenu();
430         AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(),
431                 String.valueOf(ShortcutId.ID_QUICKSETTING_VALUE.ordinal()));
432 
433         button.performAction(CLICK_ID);
434         TestUtils.waitUntil("Did not detect Quick Settings action being performed.",
435                 TIMEOUT_UI_CHANGE_S,
436                 () -> sLastGlobalAction.compareAndSet(
437                         GLOBAL_ACTION_QUICK_SETTINGS, NO_GLOBAL_ACTION));
438     }
439 
440     @Test
testNotificationsButton_performsGlobalAction()441     public void testNotificationsButton_performsGlobalAction() throws Throwable {
442         openMenu();
443         AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(),
444                 String.valueOf(ShortcutId.ID_NOTIFICATION_VALUE.ordinal()));
445 
446         button.performAction(CLICK_ID);
447         TestUtils.waitUntil("Did not detect Notifications action being performed.",
448                 TIMEOUT_UI_CHANGE_S,
449                 () -> sLastGlobalAction.compareAndSet(
450                         GLOBAL_ACTION_NOTIFICATIONS, NO_GLOBAL_ACTION));
451     }
452 
453     @Test
testScreenshotButton_performsGlobalAction()454     public void testScreenshotButton_performsGlobalAction() throws Throwable {
455         openMenu();
456         AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(),
457                 String.valueOf(ShortcutId.ID_SCREENSHOT_VALUE.ordinal()));
458 
459         button.performAction(CLICK_ID);
460         TestUtils.waitUntil("Did not detect Screenshot action being performed.",
461                 TIMEOUT_UI_CHANGE_S,
462                 () -> sLastGlobalAction.compareAndSet(
463                         GLOBAL_ACTION_TAKE_SCREENSHOT, NO_GLOBAL_ACTION));
464     }
465 
466     @Test
testOnScreenLock_closesMenu()467     public void testOnScreenLock_closesMenu() throws Throwable {
468         openMenu();
469         closeScreen();
470         wakeUpScreen();
471         assertThat(sKeyguardManager.isKeyguardLocked()).isTrue();
472 
473         TestUtils.waitUntil("Menu did not close.",
474                 TIMEOUT_UI_CHANGE_S,
475                 () -> !isMenuVisible()
476         );
477     }
478 
479     @Test
testOnScreenLock_cannotOpenMenu()480     public void testOnScreenLock_cannotOpenMenu() throws Throwable {
481         closeScreen();
482         wakeUpScreen();
483         assertThat(sKeyguardManager.isKeyguardLocked()).isTrue();
484 
485         sInstrumentation.getContext().sendBroadcast(INTENT_OPEN_MENU);
486         sUiDevice.waitForIdle();
487 
488         TestUtils.waitUntil("Did not receive signal that menu cannot open",
489                 TIMEOUT_UI_CHANGE_S,
490                 sOpenBlocked::get);
491     }
492 
493     @Test
testRestrictedActions_BrightnessNotAvailable()494     public void testRestrictedActions_BrightnessNotAvailable() throws Throwable {
495         try {
496             setUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, true);
497             openMenu();
498 
499             List<AccessibilityNodeInfo> buttons = getGridButtonList();
500             AccessibilityNodeInfo brightnessUpButton = findGridButtonInfo(buttons,
501                     String.valueOf(ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()));
502             AccessibilityNodeInfo brightnessDownButton = findGridButtonInfo(buttons,
503                     String.valueOf(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()));
504 
505             assertThat(brightnessUpButton).isNull();
506             assertThat(brightnessDownButton).isNull();
507         } finally {
508             setUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, false);
509         }
510     }
511 
512     @Test
testRestrictedActions_VolumeNotAvailable()513     public void testRestrictedActions_VolumeNotAvailable() throws Throwable {
514         try {
515             setUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, true);
516             openMenu();
517 
518             List<AccessibilityNodeInfo> buttons = getGridButtonList();
519             AccessibilityNodeInfo volumeUpButton = findGridButtonInfo(buttons,
520                     String.valueOf(ShortcutId.ID_VOLUME_UP_VALUE.ordinal()));
521             AccessibilityNodeInfo volumeDownButton = findGridButtonInfo(buttons,
522                     String.valueOf(ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()));
523 
524             assertThat(volumeUpButton).isNull();
525             assertThat(volumeDownButton).isNull();
526         } finally {
527             setUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, false);
528         }
529     }
530 
setUserRestriction(String restriction, boolean isRestricted)531     private void setUserRestriction(String restriction, boolean isRestricted) throws Throwable {
532         final Context context = sInstrumentation.getTargetContext();
533         final UserManager userManager = context.getSystemService(UserManager.class);
534         userManager.setUserRestriction(restriction, isRestricted);
535     }
536 
unlockSignal()537     private static void unlockSignal() throws IOException {
538         // go/adb-cheats#unlock-screen
539         wakeUpScreen();
540         if (sKeyguardManager.isKeyguardLocked()) {
541             sUiDevice.pressMenu();
542         }
543         WaitUtils.ensureThat(
544                 "Device unlocked & isInteractive",
545                 () -> isScreenOn() && !sKeyguardManager.isKeyguardLocked());
546     }
547 
isScreenOn()548     private static boolean isScreenOn() {
549         int display = Display.DEFAULT_DISPLAY;
550         return sPowerManager.isInteractive(display)
551                 && sDisplayManager.getDisplay(display).getState() == Display.STATE_ON;
552     }
553 
isScreenOff()554     private static boolean isScreenOff() {
555         int display = Display.DEFAULT_DISPLAY;
556         return !sPowerManager.isInteractive(display)
557                 && sDisplayManager.getDisplay(display).getState() == Display.STATE_OFF;
558     }
559 }
560