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.systemui.accessibility;
18 
19 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
20 
21 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
22 
23 import android.accessibilityservice.AccessibilityService;
24 import android.app.PendingIntent;
25 import android.app.RemoteAction;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.res.Configuration;
31 import android.graphics.drawable.Icon;
32 import android.hardware.input.InputManager;
33 import android.os.Handler;
34 import android.os.Looper;
35 import android.os.PowerManager;
36 import android.os.RemoteException;
37 import android.os.SystemClock;
38 import android.util.Log;
39 import android.view.IWindowManager;
40 import android.view.InputDevice;
41 import android.view.KeyCharacterMap;
42 import android.view.KeyEvent;
43 import android.view.WindowManagerGlobal;
44 import android.view.accessibility.AccessibilityManager;
45 
46 import com.android.internal.R;
47 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
48 import com.android.internal.accessibility.util.AccessibilityUtils;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.internal.util.ScreenshotHelper;
51 import com.android.systemui.CoreStartable;
52 import com.android.systemui.dagger.SysUISingleton;
53 import com.android.systemui.recents.Recents;
54 import com.android.systemui.settings.DisplayTracker;
55 import com.android.systemui.settings.UserTracker;
56 import com.android.systemui.shade.ShadeController;
57 import com.android.systemui.shade.ShadeViewController;
58 import com.android.systemui.statusbar.CommandQueue;
59 import com.android.systemui.statusbar.NotificationShadeWindowController;
60 import com.android.systemui.statusbar.phone.CentralSurfaces;
61 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
62 import com.android.systemui.util.Assert;
63 
64 import dagger.Lazy;
65 
66 import java.util.Locale;
67 import java.util.Optional;
68 
69 import javax.inject.Inject;
70 
71 /**
72  * Class to register system actions with accessibility framework.
73  */
74 @SysUISingleton
75 public class SystemActions implements CoreStartable {
76     private static final String TAG = "SystemActions";
77 
78     /**
79      * Action ID to go back.
80      */
81     private static final int SYSTEM_ACTION_ID_BACK = AccessibilityService.GLOBAL_ACTION_BACK; // = 1
82 
83     /**
84      * Action ID to go home.
85      */
86     private static final int SYSTEM_ACTION_ID_HOME = AccessibilityService.GLOBAL_ACTION_HOME; // = 2
87 
88     /**
89      * Action ID to toggle showing the overview of recent apps. Will fail on platforms that don't
90      * show recent apps.
91      */
92     private static final int SYSTEM_ACTION_ID_RECENTS =
93             AccessibilityService.GLOBAL_ACTION_RECENTS; // = 3
94 
95     /**
96      * Action ID to open the notifications.
97      */
98     private static final int SYSTEM_ACTION_ID_NOTIFICATIONS =
99             AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS; // = 4
100 
101     /**
102      * Action ID to open the quick settings.
103      */
104     private static final int SYSTEM_ACTION_ID_QUICK_SETTINGS =
105             AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS; // = 5
106 
107     /**
108      * Action ID to open the power long-press dialog.
109      */
110     private static final int SYSTEM_ACTION_ID_POWER_DIALOG =
111             AccessibilityService.GLOBAL_ACTION_POWER_DIALOG; // = 6
112 
113     /**
114      * Action ID to lock the screen
115      */
116     private static final int SYSTEM_ACTION_ID_LOCK_SCREEN =
117             AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN; // = 8
118 
119     /**
120      * Action ID to take a screenshot
121      */
122     private static final int SYSTEM_ACTION_ID_TAKE_SCREENSHOT =
123             AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9
124 
125     /**
126      * Action ID to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer/hang up calls and
127      * play/stop media
128      */
129     private static final int SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK =
130             AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK; // = 10
131 
132     /**
133      * Action ID to trigger the accessibility button
134      */
135     public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON =
136             AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_BUTTON; // 11
137 
138     /**
139      * Action ID to show accessibility button's menu of services
140      */
141     public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER =
142             AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER; // 12
143 
144     public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT =
145             AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT; // 13
146 
147     public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
148             AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15
149 
150     /**
151      * Action ID to trigger the dpad up button
152      */
153     private static final int SYSTEM_ACTION_ID_DPAD_UP =
154             AccessibilityService.GLOBAL_ACTION_DPAD_UP; // 16
155 
156     /**
157      * Action ID to trigger the dpad down button
158      */
159     private static final int SYSTEM_ACTION_ID_DPAD_DOWN =
160             AccessibilityService.GLOBAL_ACTION_DPAD_DOWN; // 17
161 
162     /**
163      * Action ID to trigger the dpad left button
164      */
165     private static final int SYSTEM_ACTION_ID_DPAD_LEFT =
166             AccessibilityService.GLOBAL_ACTION_DPAD_LEFT; // 18
167 
168     /**
169      * Action ID to trigger the dpad right button
170      */
171     private static final int SYSTEM_ACTION_ID_DPAD_RIGHT =
172             AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT; // 19
173 
174     /**
175      * Action ID to trigger dpad center keyevent
176      */
177     private static final int SYSTEM_ACTION_ID_DPAD_CENTER =
178             AccessibilityService.GLOBAL_ACTION_DPAD_CENTER; // 20
179 
180     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
181 
182     private final SystemActionsBroadcastReceiver mReceiver;
183     private final Context mContext;
184     private final UserTracker mUserTracker;
185     private final Optional<Recents> mRecentsOptional;
186     private final DisplayTracker mDisplayTracker;
187     private Locale mLocale;
188     private final AccessibilityManager mA11yManager;
189     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
190     private final NotificationShadeWindowController mNotificationShadeController;
191     private final ShadeController mShadeController;
192     private final Lazy<ShadeViewController> mShadeViewController;
193     private final StatusBarWindowCallback mNotificationShadeCallback;
194     private boolean mDismissNotificationShadeActionRegistered;
195 
196     @Inject
SystemActions(Context context, UserTracker userTracker, NotificationShadeWindowController notificationShadeController, ShadeController shadeController, Lazy<ShadeViewController> shadeViewController, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, Optional<Recents> recentsOptional, DisplayTracker displayTracker)197     public SystemActions(Context context,
198             UserTracker userTracker,
199             NotificationShadeWindowController notificationShadeController,
200             ShadeController shadeController,
201             Lazy<ShadeViewController> shadeViewController,
202             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
203             Optional<Recents> recentsOptional,
204             DisplayTracker displayTracker) {
205         mContext = context;
206         mUserTracker = userTracker;
207         mShadeController = shadeController;
208         mShadeViewController = shadeViewController;
209         mRecentsOptional = recentsOptional;
210         mDisplayTracker = displayTracker;
211         mReceiver = new SystemActionsBroadcastReceiver();
212         mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
213         mA11yManager = (AccessibilityManager) mContext.getSystemService(
214                 Context.ACCESSIBILITY_SERVICE);
215         mNotificationShadeController = notificationShadeController;
216         // Saving in instance variable since to prevent GC since
217         // NotificationShadeWindowController.registerCallback() only keeps weak references.
218         mNotificationShadeCallback =
219                 (keyguardShowing, keyguardOccluded, keyguardGoingAway, bouncerShowing, mDozing,
220                         panelExpanded, isDreaming) ->
221                         registerOrUnregisterDismissNotificationShadeAction();
222         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
223     }
224 
225     @Override
start()226     public void start() {
227         mNotificationShadeController.registerCallback(mNotificationShadeCallback);
228         mContext.registerReceiverForAllUsers(
229                 mReceiver,
230                 mReceiver.createIntentFilter(),
231                 PERMISSION_SELF,
232                 null,
233                 Context.RECEIVER_EXPORTED);
234         registerActions();
235     }
236 
237     @Override
onConfigurationChanged(Configuration newConfig)238     public void onConfigurationChanged(Configuration newConfig) {
239         final Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
240         if (!locale.equals(mLocale)) {
241             mLocale = locale;
242             registerActions();
243         }
244     }
245 
registerActions()246     private void registerActions() {
247         RemoteAction actionBack = createRemoteAction(
248                 R.string.accessibility_system_action_back_label,
249                 SystemActionsBroadcastReceiver.INTENT_ACTION_BACK);
250 
251         RemoteAction actionHome = createRemoteAction(
252                 R.string.accessibility_system_action_home_label,
253                 SystemActionsBroadcastReceiver.INTENT_ACTION_HOME);
254 
255         RemoteAction actionRecents = createRemoteAction(
256                 R.string.accessibility_system_action_recents_label,
257                 SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS);
258 
259         RemoteAction actionNotifications = createRemoteAction(
260                 R.string.accessibility_system_action_notifications_label,
261                 SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS);
262 
263         RemoteAction actionQuickSettings = createRemoteAction(
264                 R.string.accessibility_system_action_quick_settings_label,
265                 SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS);
266 
267         RemoteAction actionPowerDialog = createRemoteAction(
268                 R.string.accessibility_system_action_power_dialog_label,
269                 SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG);
270 
271         RemoteAction actionLockScreen = createRemoteAction(
272                 R.string.accessibility_system_action_lock_screen_label,
273                 SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN);
274 
275         RemoteAction actionTakeScreenshot = createRemoteAction(
276                 R.string.accessibility_system_action_screenshot_label,
277                 SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT);
278 
279         RemoteAction actionHeadsetHook = createRemoteAction(
280                 R.string.accessibility_system_action_headset_hook_label,
281                 SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK);
282 
283         RemoteAction actionAccessibilityShortcut = createRemoteAction(
284                 R.string.accessibility_system_action_hardware_a11y_shortcut_label,
285                 SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
286 
287         RemoteAction actionDpadUp = createRemoteAction(
288                 R.string.accessibility_system_action_dpad_up_label,
289                 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP);
290 
291         RemoteAction actionDpadDown = createRemoteAction(
292                 R.string.accessibility_system_action_dpad_down_label,
293                 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN);
294 
295         RemoteAction actionDpadLeft = createRemoteAction(
296                 R.string.accessibility_system_action_dpad_left_label,
297                 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT);
298 
299         RemoteAction actionDpadRight = createRemoteAction(
300                 R.string.accessibility_system_action_dpad_right_label,
301                 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT);
302 
303         RemoteAction actionDpadCenter = createRemoteAction(
304                 R.string.accessibility_system_action_dpad_center_label,
305                 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER);
306 
307         mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
308         mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
309         mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
310         if (mCentralSurfacesOptionalLazy.get().isPresent()) {
311             // These two actions require the CentralSurfaces instance.
312             mA11yManager.registerSystemAction(actionNotifications, SYSTEM_ACTION_ID_NOTIFICATIONS);
313             mA11yManager.registerSystemAction(actionQuickSettings, SYSTEM_ACTION_ID_QUICK_SETTINGS);
314         }
315         mA11yManager.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG);
316         mA11yManager.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN);
317         mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
318         mA11yManager.registerSystemAction(actionHeadsetHook, SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK);
319         mA11yManager.registerSystemAction(
320                 actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT);
321         mA11yManager.registerSystemAction(actionDpadUp, SYSTEM_ACTION_ID_DPAD_UP);
322         mA11yManager.registerSystemAction(actionDpadDown, SYSTEM_ACTION_ID_DPAD_DOWN);
323         mA11yManager.registerSystemAction(actionDpadLeft, SYSTEM_ACTION_ID_DPAD_LEFT);
324         mA11yManager.registerSystemAction(actionDpadRight, SYSTEM_ACTION_ID_DPAD_RIGHT);
325         mA11yManager.registerSystemAction(actionDpadCenter, SYSTEM_ACTION_ID_DPAD_CENTER);
326         registerOrUnregisterDismissNotificationShadeAction();
327     }
328 
registerOrUnregisterDismissNotificationShadeAction()329     private void registerOrUnregisterDismissNotificationShadeAction() {
330         Assert.isMainThread();
331 
332         // Saving state in instance variable since this callback is called quite often to avoid
333         // binder calls
334         final Optional<CentralSurfaces> centralSurfacesOptional =
335                 mCentralSurfacesOptionalLazy.get();
336         if (centralSurfacesOptional.isPresent()
337                 && mShadeViewController.get().isPanelExpanded()
338                 && !centralSurfacesOptional.get().isKeyguardShowing()) {
339             if (!mDismissNotificationShadeActionRegistered) {
340                 mA11yManager.registerSystemAction(
341                         createRemoteAction(
342                                 R.string.accessibility_system_action_dismiss_notification_shade,
343                                 SystemActionsBroadcastReceiver
344                                         .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE),
345                         SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
346                 mDismissNotificationShadeActionRegistered = true;
347             }
348         } else {
349             if (mDismissNotificationShadeActionRegistered) {
350                 mA11yManager.unregisterSystemAction(
351                         SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
352                 mDismissNotificationShadeActionRegistered = false;
353             }
354         }
355     }
356 
357     /**
358      * Register a system action.
359      *
360      * @param actionId the action ID to register.
361      */
register(int actionId)362     public void register(int actionId) {
363         int labelId;
364         String intent;
365         switch (actionId) {
366             case SYSTEM_ACTION_ID_BACK:
367                 labelId = R.string.accessibility_system_action_back_label;
368                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_BACK;
369                 break;
370             case SYSTEM_ACTION_ID_HOME:
371                 labelId = R.string.accessibility_system_action_home_label;
372                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HOME;
373                 break;
374             case SYSTEM_ACTION_ID_RECENTS:
375                 labelId = R.string.accessibility_system_action_recents_label;
376                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS;
377                 break;
378             case SYSTEM_ACTION_ID_NOTIFICATIONS:
379                 labelId = R.string.accessibility_system_action_notifications_label;
380                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS;
381                 break;
382             case SYSTEM_ACTION_ID_QUICK_SETTINGS:
383                 labelId = R.string.accessibility_system_action_quick_settings_label;
384                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS;
385                 break;
386             case SYSTEM_ACTION_ID_POWER_DIALOG:
387                 labelId = R.string.accessibility_system_action_power_dialog_label;
388                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG;
389                 break;
390             case SYSTEM_ACTION_ID_LOCK_SCREEN:
391                 labelId = R.string.accessibility_system_action_lock_screen_label;
392                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN;
393                 break;
394             case SYSTEM_ACTION_ID_TAKE_SCREENSHOT:
395                 labelId = R.string.accessibility_system_action_screenshot_label;
396                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT;
397                 break;
398             case SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK:
399                 labelId = R.string.accessibility_system_action_headset_hook_label;
400                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK;
401                 break;
402             case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON:
403                 labelId = R.string.accessibility_system_action_on_screen_a11y_shortcut_label;
404                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON;
405                 break;
406             case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER:
407                 labelId =
408                         R.string.accessibility_system_action_on_screen_a11y_shortcut_chooser_label;
409                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER;
410                 break;
411             case SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT:
412                 labelId = R.string.accessibility_system_action_hardware_a11y_shortcut_label;
413                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT;
414                 break;
415             case SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE:
416                 labelId = R.string.accessibility_system_action_dismiss_notification_shade;
417                 intent = SystemActionsBroadcastReceiver
418                         .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE;
419                 break;
420             case SYSTEM_ACTION_ID_DPAD_UP:
421                 labelId = R.string.accessibility_system_action_dpad_up_label;
422                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP;
423                 break;
424             case SYSTEM_ACTION_ID_DPAD_DOWN:
425                 labelId = R.string.accessibility_system_action_dpad_down_label;
426                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN;
427                 break;
428             case SYSTEM_ACTION_ID_DPAD_LEFT:
429                 labelId = R.string.accessibility_system_action_dpad_left_label;
430                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT;
431                 break;
432             case SYSTEM_ACTION_ID_DPAD_RIGHT:
433                 labelId = R.string.accessibility_system_action_dpad_right_label;
434                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT;
435                 break;
436             case SYSTEM_ACTION_ID_DPAD_CENTER:
437                 labelId = R.string.accessibility_system_action_dpad_center_label;
438                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER;
439                 break;
440             default:
441                 return;
442         }
443         mA11yManager.registerSystemAction(createRemoteAction(labelId, intent), actionId);
444     }
445 
createRemoteAction(int labelId, String intent)446     private RemoteAction createRemoteAction(int labelId, String intent) {
447         // TODO(b/148087487): update the icon used below to a valid one
448         return new RemoteAction(
449                 Icon.createWithResource(mContext, R.drawable.ic_info),
450                 mContext.getString(labelId),
451                 mContext.getString(labelId),
452                 mReceiver.createPendingIntent(mContext, intent));
453     }
454 
455     /**
456      * Unregister a system action.
457      *
458      * @param actionId the action ID to unregister.
459      */
unregister(int actionId)460     public void unregister(int actionId) {
461         mA11yManager.unregisterSystemAction(actionId);
462     }
463 
handleBack()464     private void handleBack() {
465         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
466     }
467 
handleHome()468     private void handleHome() {
469         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
470     }
471 
sendDownAndUpKeyEvents(int keyCode)472     private void sendDownAndUpKeyEvents(int keyCode) {
473         final long downTime = SystemClock.uptimeMillis();
474         sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
475         sendKeyEventIdentityCleared(
476                 keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
477     }
478 
sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time)479     private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
480         KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
481                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
482                 InputDevice.SOURCE_KEYBOARD, null);
483         mContext.getSystemService(InputManager.class)
484                 .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
485         event.recycle();
486     }
487 
handleRecents()488     private void handleRecents() {
489         mRecentsOptional.ifPresent(Recents::toggleRecentApps);
490     }
491 
handleNotifications()492     private void handleNotifications() {
493         mShadeController.animateExpandShade();
494     }
495 
handleQuickSettings()496     private void handleQuickSettings() {
497         mShadeController.animateExpandQs();
498     }
499 
handlePowerDialog()500     private void handlePowerDialog() {
501         IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
502 
503         try {
504             windowManager.showGlobalActions();
505         } catch (RemoteException e) {
506             Log.e(TAG, "failed to display power dialog.");
507         }
508     }
509 
handleLockScreen()510     private void handleLockScreen() {
511         IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
512 
513         mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
514                 PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
515         try {
516             windowManager.lockNow(null);
517         } catch (RemoteException e) {
518             Log.e(TAG, "failed to lock screen.");
519         }
520     }
521 
handleTakeScreenshot()522     private void handleTakeScreenshot() {
523         ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext);
524         screenshotHelper.takeScreenshot(
525                 SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null);
526     }
527 
528     @VisibleForTesting
handleHeadsetHook()529     void handleHeadsetHook() {
530         if (!AccessibilityUtils.interceptHeadsetHookForActiveCall(mContext)) {
531             sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
532         }
533     }
534 
handleAccessibilityButton()535     private void handleAccessibilityButton() {
536         AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
537                 mDisplayTracker.getDefaultDisplayId());
538     }
539 
handleAccessibilityButtonChooser()540     private void handleAccessibilityButtonChooser() {
541         final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
542         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
543         final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
544         intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
545         mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
546     }
547 
handleAccessibilityShortcut()548     private void handleAccessibilityShortcut() {
549         mA11yManager.performAccessibilityShortcut();
550     }
551 
handleAccessibilityDismissNotificationShade()552     private void handleAccessibilityDismissNotificationShade() {
553         mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
554     }
555 
handleDpadUp()556     private void handleDpadUp() {
557         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP);
558     }
559 
handleDpadDown()560     private void handleDpadDown() {
561         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN);
562     }
563 
handleDpadLeft()564     private void handleDpadLeft() {
565         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
566     }
567 
handleDpadRight()568     private void handleDpadRight() {
569         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
570     }
571 
handleDpadCenter()572     private void handleDpadCenter() {
573         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER);
574     }
575 
576     private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
577         private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
578         private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
579         private static final String INTENT_ACTION_RECENTS = "SYSTEM_ACTION_RECENTS";
580         private static final String INTENT_ACTION_NOTIFICATIONS = "SYSTEM_ACTION_NOTIFICATIONS";
581         private static final String INTENT_ACTION_QUICK_SETTINGS = "SYSTEM_ACTION_QUICK_SETTINGS";
582         private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG";
583         private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN";
584         private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT";
585         private static final String INTENT_ACTION_HEADSET_HOOK = "SYSTEM_ACTION_HEADSET_HOOK";
586         private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON =
587                 "SYSTEM_ACTION_ACCESSIBILITY_BUTTON";
588         private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER =
589                 "SYSTEM_ACTION_ACCESSIBILITY_BUTTON_MENU";
590         private static final String INTENT_ACTION_ACCESSIBILITY_SHORTCUT =
591                 "SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT";
592         private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
593                 "SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE";
594         private static final String INTENT_ACTION_DPAD_UP = "SYSTEM_ACTION_DPAD_UP";
595         private static final String INTENT_ACTION_DPAD_DOWN = "SYSTEM_ACTION_DPAD_DOWN";
596         private static final String INTENT_ACTION_DPAD_LEFT = "SYSTEM_ACTION_DPAD_LEFT";
597         private static final String INTENT_ACTION_DPAD_RIGHT = "SYSTEM_ACTION_DPAD_RIGHT";
598         private static final String INTENT_ACTION_DPAD_CENTER = "SYSTEM_ACTION_DPAD_CENTER";
599 
createPendingIntent(Context context, String intentAction)600         private PendingIntent createPendingIntent(Context context, String intentAction) {
601             switch (intentAction) {
602                 case INTENT_ACTION_BACK:
603                 case INTENT_ACTION_HOME:
604                 case INTENT_ACTION_RECENTS:
605                 case INTENT_ACTION_NOTIFICATIONS:
606                 case INTENT_ACTION_QUICK_SETTINGS:
607                 case INTENT_ACTION_POWER_DIALOG:
608                 case INTENT_ACTION_LOCK_SCREEN:
609                 case INTENT_ACTION_TAKE_SCREENSHOT:
610                 case INTENT_ACTION_HEADSET_HOOK:
611                 case INTENT_ACTION_ACCESSIBILITY_BUTTON:
612                 case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER:
613                 case INTENT_ACTION_ACCESSIBILITY_SHORTCUT:
614                 case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE:
615                 case INTENT_ACTION_DPAD_UP:
616                 case INTENT_ACTION_DPAD_DOWN:
617                 case INTENT_ACTION_DPAD_LEFT:
618                 case INTENT_ACTION_DPAD_RIGHT:
619                 case INTENT_ACTION_DPAD_CENTER: {
620                     Intent intent = new Intent(intentAction);
621                     intent.setPackage(context.getPackageName());
622                     intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
623                     return PendingIntent.getBroadcast(context, 0, intent,
624                             PendingIntent.FLAG_IMMUTABLE);
625                 }
626                 default:
627                     break;
628             }
629             return null;
630         }
631 
createIntentFilter()632         private IntentFilter createIntentFilter() {
633             IntentFilter intentFilter = new IntentFilter();
634             intentFilter.addAction(INTENT_ACTION_BACK);
635             intentFilter.addAction(INTENT_ACTION_HOME);
636             intentFilter.addAction(INTENT_ACTION_RECENTS);
637             intentFilter.addAction(INTENT_ACTION_NOTIFICATIONS);
638             intentFilter.addAction(INTENT_ACTION_QUICK_SETTINGS);
639             intentFilter.addAction(INTENT_ACTION_POWER_DIALOG);
640             intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN);
641             intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT);
642             intentFilter.addAction(INTENT_ACTION_HEADSET_HOOK);
643             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON);
644             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER);
645             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
646             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
647             intentFilter.addAction(INTENT_ACTION_DPAD_UP);
648             intentFilter.addAction(INTENT_ACTION_DPAD_DOWN);
649             intentFilter.addAction(INTENT_ACTION_DPAD_LEFT);
650             intentFilter.addAction(INTENT_ACTION_DPAD_RIGHT);
651             intentFilter.addAction(INTENT_ACTION_DPAD_CENTER);
652             return intentFilter;
653         }
654 
655         @Override
onReceive(Context context, Intent intent)656         public void onReceive(Context context, Intent intent) {
657             String intentAction = intent.getAction();
658             switch (intentAction) {
659                 case INTENT_ACTION_BACK: {
660                     handleBack();
661                     break;
662                 }
663                 case INTENT_ACTION_HOME: {
664                     handleHome();
665                     break;
666                 }
667                 case INTENT_ACTION_RECENTS: {
668                     handleRecents();
669                     break;
670                 }
671                 case INTENT_ACTION_NOTIFICATIONS: {
672                     handleNotifications();
673                     break;
674                 }
675                 case INTENT_ACTION_QUICK_SETTINGS: {
676                     handleQuickSettings();
677                     break;
678                 }
679                 case INTENT_ACTION_POWER_DIALOG: {
680                     handlePowerDialog();
681                     break;
682                 }
683                 case INTENT_ACTION_LOCK_SCREEN: {
684                     handleLockScreen();
685                     break;
686                 }
687                 case INTENT_ACTION_TAKE_SCREENSHOT: {
688                     handleTakeScreenshot();
689                     break;
690                 }
691                 case INTENT_ACTION_HEADSET_HOOK: {
692                     handleHeadsetHook();
693                     break;
694                 }
695                 case INTENT_ACTION_ACCESSIBILITY_BUTTON: {
696                     handleAccessibilityButton();
697                     break;
698                 }
699                 case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER: {
700                     handleAccessibilityButtonChooser();
701                     break;
702                 }
703                 case INTENT_ACTION_ACCESSIBILITY_SHORTCUT: {
704                     handleAccessibilityShortcut();
705                     break;
706                 }
707                 case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
708                     handleAccessibilityDismissNotificationShade();
709                     break;
710                 }
711                 case INTENT_ACTION_DPAD_UP: {
712                     handleDpadUp();
713                     break;
714                 }
715                 case INTENT_ACTION_DPAD_DOWN: {
716                     handleDpadDown();
717                     break;
718                 }
719                 case INTENT_ACTION_DPAD_LEFT: {
720                     handleDpadLeft();
721                     break;
722                 }
723                 case INTENT_ACTION_DPAD_RIGHT: {
724                     handleDpadRight();
725                     break;
726                 }
727                 case INTENT_ACTION_DPAD_CENTER: {
728                     handleDpadCenter();
729                     break;
730                 }
731                 default:
732                     break;
733             }
734         }
735     }
736 }
737