Loading services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +217 −24 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.accessibility; import android.accessibilityservice.AccessibilityService; import android.app.PendingIntent; import android.app.RemoteAction; import android.app.StatusBarManager; import android.content.Context; import android.hardware.input.InputManager; Loading @@ -25,81 +27,272 @@ import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; import android.util.ArrayMap; import android.util.Slog; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ScreenshotHelper; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Supplier; /** * Handle the back-end of AccessibilityService#performGlobalAction * Handle the back-end of system AccessibilityAction. * * This class should support three use cases with combined usage of new API and legacy API: * * Use case 1: SystemUI doesn't use the new system action registration API. Accessibility * service doesn't use the new system action API to obtain action list. Accessibility * service uses legacy global action id to perform predefined system actions. * Use case 2: SystemUI uses the new system action registration API to register available system * actions. Accessibility service doesn't use the new system action API to obtain action * list. Accessibility service uses legacy global action id to trigger the system * actions registered by SystemUI. * Use case 3: SystemUI doesn't use the new system action registration API.Accessibility service * obtains the available system actions using new AccessibilityService API and trigger * the predefined system actions. */ public class SystemActionPerformer { private static final String TAG = "SystemActionPerformer"; interface SystemActionsChangedListener { void onSystemActionsChanged(); } private final SystemActionsChangedListener mListener; private final Object mSystemActionLock = new Object(); // Resource id based ActionId -> RemoteAction @GuardedBy("mSystemActionLock") private final Map<Integer, RemoteAction> mRegisteredSystemActions = new ArrayMap<>(); // Legacy system actions. private final AccessibilityAction mLegacyHomeAction; private final AccessibilityAction mLegacyBackAction; private final AccessibilityAction mLegacyRecentsAction; private final AccessibilityAction mLegacyNotificationsAction; private final AccessibilityAction mLegacyQuickSettingsAction; private final AccessibilityAction mLegacyPowerDialogAction; private final AccessibilityAction mLegacyToggleSplitScreenAction; private final AccessibilityAction mLegacyLockScreenAction; private final AccessibilityAction mLegacyTakeScreenshotAction; private final WindowManagerInternal mWindowManagerService; private final Context mContext; private Supplier<ScreenshotHelper> mScreenshotHelperSupplier; public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) { mContext = context; mWindowManagerService = windowManagerInternal; mScreenshotHelperSupplier = null; public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal) { this(context, windowManagerInternal, null, null); } // Used to mock ScreenshotHelper @VisibleForTesting public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal, public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal, Supplier<ScreenshotHelper> screenshotHelperSupplier) { this(context, windowManagerInternal); this(context, windowManagerInternal, screenshotHelperSupplier, null); } public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal, Supplier<ScreenshotHelper> screenshotHelperSupplier, SystemActionsChangedListener listener) { mContext = context; mWindowManagerService = windowManagerInternal; mListener = listener; mScreenshotHelperSupplier = screenshotHelperSupplier; mLegacyHomeAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_HOME, mContext.getResources().getString( R.string.accessibility_system_action_home_label)); mLegacyBackAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_BACK, mContext.getResources().getString( R.string.accessibility_system_action_back_label)); mLegacyRecentsAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_RECENTS, mContext.getResources().getString( R.string.accessibility_system_action_recents_label)); mLegacyNotificationsAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, mContext.getResources().getString( R.string.accessibility_system_action_notifications_label)); mLegacyQuickSettingsAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, mContext.getResources().getString( R.string.accessibility_system_action_quick_settings_label)); mLegacyPowerDialogAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_POWER_DIALOG, mContext.getResources().getString( R.string.accessibility_system_action_power_dialog_label)); mLegacyToggleSplitScreenAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN, mContext.getResources().getString( R.string.accessibility_system_action_toggle_split_screen_label)); mLegacyLockScreenAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN, mContext.getResources().getString( R.string.accessibility_system_action_lock_screen_label)); mLegacyTakeScreenshotAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT, mContext.getResources().getString( R.string.accessibility_system_action_screenshot_label)); } /** * This method is called to register a system action. If a system action is already registered * with the given id, the existing system action will be overwritten. */ void registerSystemAction(int id, RemoteAction action) { synchronized (mSystemActionLock) { mRegisteredSystemActions.put(id, action); } if (mListener != null) { mListener.onSystemActionsChanged(); } } /** * This method is called to unregister a system action previously registered through * registerSystemAction. */ void unregisterSystemAction(int id) { synchronized (mSystemActionLock) { mRegisteredSystemActions.remove(id); } if (mListener != null) { mListener.onSystemActionsChanged(); } } /** * Performe the system action matching the given action id. * This method returns the list of available system actions. */ public boolean performSystemAction(int action) { List<AccessibilityAction> getSystemActions() { List<AccessibilityAction> systemActions = new ArrayList<>(); synchronized (mSystemActionLock) { for (Map.Entry<Integer, RemoteAction> entry : mRegisteredSystemActions.entrySet()) { AccessibilityAction systemAction = new AccessibilityAction( entry.getKey(), entry.getValue().getTitle()); systemActions.add(systemAction); } // add AccessibilitySystemAction entry for legacy system actions if not overwritten addLegacySystemActions(systemActions); } return systemActions; } private void addLegacySystemActions(List<AccessibilityAction> systemActions) { if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_BACK)) { systemActions.add(mLegacyBackAction); } if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_HOME)) { systemActions.add(mLegacyHomeAction); } if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_RECENTS)) { systemActions.add(mLegacyRecentsAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS)) { systemActions.add(mLegacyNotificationsAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS)) { systemActions.add(mLegacyQuickSettingsAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_POWER_DIALOG)) { systemActions.add(mLegacyPowerDialogAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)) { systemActions.add(mLegacyToggleSplitScreenAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)) { systemActions.add(mLegacyLockScreenAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT)) { systemActions.add(mLegacyTakeScreenshotAction); } } /** * Trigger the registered action by the matching action id. */ public boolean performSystemAction(int actionId) { final long identity = Binder.clearCallingIdentity(); try { switch (action) { synchronized (mSystemActionLock) { // If a system action is registered with the given actionId, call the corresponding // RemoteAction. RemoteAction registeredAction = mRegisteredSystemActions.get(actionId); if (registeredAction != null) { try { registeredAction.getActionIntent().send(); return true; } catch (PendingIntent.CanceledException ex) { Slog.e(TAG, "canceled PendingIntent for global action " + registeredAction.getTitle(), ex); } return false; } } // No RemoteAction registered with the given actionId, try the default legacy system // actions. switch (actionId) { case AccessibilityService.GLOBAL_ACTION_BACK: { sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK); } return true; } case AccessibilityService.GLOBAL_ACTION_HOME: { sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME); } return true; case AccessibilityService.GLOBAL_ACTION_RECENTS: { return openRecents(); } case AccessibilityService.GLOBAL_ACTION_RECENTS: return openRecents(); case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: { expandNotifications(); } return true; } case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: { expandQuickSettings(); } return true; } case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: { showGlobalActions(); } return true; case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: { return toggleSplitScreen(); } case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: { case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: return toggleSplitScreen(); case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: return lockScreen(); } case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: { case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: return takeScreenshot(); } } default: return false; } } finally { Binder.restoreCallingIdentity(identity); } Loading services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java +271 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +217 −24 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.accessibility; import android.accessibilityservice.AccessibilityService; import android.app.PendingIntent; import android.app.RemoteAction; import android.app.StatusBarManager; import android.content.Context; import android.hardware.input.InputManager; Loading @@ -25,81 +27,272 @@ import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; import android.util.ArrayMap; import android.util.Slog; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ScreenshotHelper; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Supplier; /** * Handle the back-end of AccessibilityService#performGlobalAction * Handle the back-end of system AccessibilityAction. * * This class should support three use cases with combined usage of new API and legacy API: * * Use case 1: SystemUI doesn't use the new system action registration API. Accessibility * service doesn't use the new system action API to obtain action list. Accessibility * service uses legacy global action id to perform predefined system actions. * Use case 2: SystemUI uses the new system action registration API to register available system * actions. Accessibility service doesn't use the new system action API to obtain action * list. Accessibility service uses legacy global action id to trigger the system * actions registered by SystemUI. * Use case 3: SystemUI doesn't use the new system action registration API.Accessibility service * obtains the available system actions using new AccessibilityService API and trigger * the predefined system actions. */ public class SystemActionPerformer { private static final String TAG = "SystemActionPerformer"; interface SystemActionsChangedListener { void onSystemActionsChanged(); } private final SystemActionsChangedListener mListener; private final Object mSystemActionLock = new Object(); // Resource id based ActionId -> RemoteAction @GuardedBy("mSystemActionLock") private final Map<Integer, RemoteAction> mRegisteredSystemActions = new ArrayMap<>(); // Legacy system actions. private final AccessibilityAction mLegacyHomeAction; private final AccessibilityAction mLegacyBackAction; private final AccessibilityAction mLegacyRecentsAction; private final AccessibilityAction mLegacyNotificationsAction; private final AccessibilityAction mLegacyQuickSettingsAction; private final AccessibilityAction mLegacyPowerDialogAction; private final AccessibilityAction mLegacyToggleSplitScreenAction; private final AccessibilityAction mLegacyLockScreenAction; private final AccessibilityAction mLegacyTakeScreenshotAction; private final WindowManagerInternal mWindowManagerService; private final Context mContext; private Supplier<ScreenshotHelper> mScreenshotHelperSupplier; public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) { mContext = context; mWindowManagerService = windowManagerInternal; mScreenshotHelperSupplier = null; public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal) { this(context, windowManagerInternal, null, null); } // Used to mock ScreenshotHelper @VisibleForTesting public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal, public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal, Supplier<ScreenshotHelper> screenshotHelperSupplier) { this(context, windowManagerInternal); this(context, windowManagerInternal, screenshotHelperSupplier, null); } public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal, Supplier<ScreenshotHelper> screenshotHelperSupplier, SystemActionsChangedListener listener) { mContext = context; mWindowManagerService = windowManagerInternal; mListener = listener; mScreenshotHelperSupplier = screenshotHelperSupplier; mLegacyHomeAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_HOME, mContext.getResources().getString( R.string.accessibility_system_action_home_label)); mLegacyBackAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_BACK, mContext.getResources().getString( R.string.accessibility_system_action_back_label)); mLegacyRecentsAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_RECENTS, mContext.getResources().getString( R.string.accessibility_system_action_recents_label)); mLegacyNotificationsAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, mContext.getResources().getString( R.string.accessibility_system_action_notifications_label)); mLegacyQuickSettingsAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, mContext.getResources().getString( R.string.accessibility_system_action_quick_settings_label)); mLegacyPowerDialogAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_POWER_DIALOG, mContext.getResources().getString( R.string.accessibility_system_action_power_dialog_label)); mLegacyToggleSplitScreenAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN, mContext.getResources().getString( R.string.accessibility_system_action_toggle_split_screen_label)); mLegacyLockScreenAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN, mContext.getResources().getString( R.string.accessibility_system_action_lock_screen_label)); mLegacyTakeScreenshotAction = new AccessibilityAction( AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT, mContext.getResources().getString( R.string.accessibility_system_action_screenshot_label)); } /** * This method is called to register a system action. If a system action is already registered * with the given id, the existing system action will be overwritten. */ void registerSystemAction(int id, RemoteAction action) { synchronized (mSystemActionLock) { mRegisteredSystemActions.put(id, action); } if (mListener != null) { mListener.onSystemActionsChanged(); } } /** * This method is called to unregister a system action previously registered through * registerSystemAction. */ void unregisterSystemAction(int id) { synchronized (mSystemActionLock) { mRegisteredSystemActions.remove(id); } if (mListener != null) { mListener.onSystemActionsChanged(); } } /** * Performe the system action matching the given action id. * This method returns the list of available system actions. */ public boolean performSystemAction(int action) { List<AccessibilityAction> getSystemActions() { List<AccessibilityAction> systemActions = new ArrayList<>(); synchronized (mSystemActionLock) { for (Map.Entry<Integer, RemoteAction> entry : mRegisteredSystemActions.entrySet()) { AccessibilityAction systemAction = new AccessibilityAction( entry.getKey(), entry.getValue().getTitle()); systemActions.add(systemAction); } // add AccessibilitySystemAction entry for legacy system actions if not overwritten addLegacySystemActions(systemActions); } return systemActions; } private void addLegacySystemActions(List<AccessibilityAction> systemActions) { if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_BACK)) { systemActions.add(mLegacyBackAction); } if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_HOME)) { systemActions.add(mLegacyHomeAction); } if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_RECENTS)) { systemActions.add(mLegacyRecentsAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS)) { systemActions.add(mLegacyNotificationsAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS)) { systemActions.add(mLegacyQuickSettingsAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_POWER_DIALOG)) { systemActions.add(mLegacyPowerDialogAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)) { systemActions.add(mLegacyToggleSplitScreenAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)) { systemActions.add(mLegacyLockScreenAction); } if (!mRegisteredSystemActions.containsKey( AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT)) { systemActions.add(mLegacyTakeScreenshotAction); } } /** * Trigger the registered action by the matching action id. */ public boolean performSystemAction(int actionId) { final long identity = Binder.clearCallingIdentity(); try { switch (action) { synchronized (mSystemActionLock) { // If a system action is registered with the given actionId, call the corresponding // RemoteAction. RemoteAction registeredAction = mRegisteredSystemActions.get(actionId); if (registeredAction != null) { try { registeredAction.getActionIntent().send(); return true; } catch (PendingIntent.CanceledException ex) { Slog.e(TAG, "canceled PendingIntent for global action " + registeredAction.getTitle(), ex); } return false; } } // No RemoteAction registered with the given actionId, try the default legacy system // actions. switch (actionId) { case AccessibilityService.GLOBAL_ACTION_BACK: { sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK); } return true; } case AccessibilityService.GLOBAL_ACTION_HOME: { sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME); } return true; case AccessibilityService.GLOBAL_ACTION_RECENTS: { return openRecents(); } case AccessibilityService.GLOBAL_ACTION_RECENTS: return openRecents(); case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: { expandNotifications(); } return true; } case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: { expandQuickSettings(); } return true; } case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: { showGlobalActions(); } return true; case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: { return toggleSplitScreen(); } case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: { case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: return toggleSplitScreen(); case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: return lockScreen(); } case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: { case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: return takeScreenshot(); } } default: return false; } } finally { Binder.restoreCallingIdentity(identity); } Loading
services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java +271 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes