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