summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hongming Jin <hongmingjin@google.com> 2019-11-13 15:15:48 -0800
committer Hongming Jin <hongmingjin@google.com> 2019-12-04 11:35:41 -0800
commit2d49456c9a79c37a842cdabeda77468d8e01d016 (patch)
tree80c1872cee3945d5b2ccb6a9a5a81dc27cd1a215
parent01975c598b7d4375549b4457f8b6e1ecd2c66cbc (diff)
SystemActionPerformer implementation for new API.
Implmentation details of Accessibility framework SystemActionPerformer. Will be accessed by AccessibilityManagerService to provide API implementation support for system action registration and system action access. Bug: 136286274 Test: atest SystemActionPerformerTest Change-Id: Ic0adbb6fc21d262fb2949f616ca28940fd6f1f53
-rw-r--r--services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java241
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java286
2 files changed, 488 insertions, 39 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 19ac0d3c1024..17549268503e 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -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;
@@ -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;
}
- return true;
case AccessibilityService.GLOBAL_ACTION_HOME: {
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
+ return true;
}
- return true;
- case AccessibilityService.GLOBAL_ACTION_RECENTS: {
+ case AccessibilityService.GLOBAL_ACTION_RECENTS:
return openRecents();
- }
case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
expandNotifications();
+ return true;
}
- return true;
case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
expandQuickSettings();
+ return true;
}
- return true;
case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
showGlobalActions();
+ return true;
}
- return true;
- case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
+ case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN:
return toggleSplitScreen();
- }
- case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
+ 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;
}
- return false;
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 37f5b87ac115..335217719cc9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -16,18 +16,39 @@
package com.android.server.accessibility;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
import android.os.Handler;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.InstrumentationRegistry;
import com.android.internal.util.ScreenshotHelper;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
@@ -35,55 +56,290 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for SystemActionPerformer
*/
public class SystemActionPerformerTest {
- SystemActionPerformer mSystemActionPerformer;
+ private static final int LATCH_TIMEOUT_MS = 500;
+ private static final int LEGACY_SYSTEM_ACTION_COUNT = 9;
+ private static final int NEW_ACTION_ID = 20;
+ private static final String LABEL_1 = "label1";
+ private static final String LABEL_2 = "label2";
+ private static final String INTENT_ACTION1 = "TESTACTION1";
+ private static final String INTENT_ACTION2 = "TESTACTION2";
+ private static final String DESCRIPTION1 = "description1";
+ private static final String DESCRIPTION2 = "description2";
+ private static final PendingIntent TEST_PENDING_INTENT_1 = PendingIntent.getBroadcast(
+ InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION1), 0);
+ private static final RemoteAction NEW_TEST_ACTION_1 = new RemoteAction(
+ Icon.createWithContentUri("content://test"),
+ LABEL_1,
+ DESCRIPTION1,
+ TEST_PENDING_INTENT_1);
+ private static final PendingIntent TEST_PENDING_INTENT_2 = PendingIntent.getBroadcast(
+ InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION2), 0);
+ private static final RemoteAction NEW_TEST_ACTION_2 = new RemoteAction(
+ Icon.createWithContentUri("content://test"),
+ LABEL_2,
+ DESCRIPTION2,
+ TEST_PENDING_INTENT_2);
+
+ private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
+ new AccessibilityAction(NEW_ACTION_ID, LABEL_1);
+ private static final AccessibilityAction LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION =
+ new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, LABEL_1);
+ private static final AccessibilityAction LEGACY_HOME_ACCESSIBILITY_ACTION =
+ new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_HOME, LABEL_2);
- @Mock Context mMockContext;
- @Mock WindowManagerInternal mMockWindowManagerInternal;
- @Mock StatusBarManager mMockStatusBarManager;
- @Mock ScreenshotHelper mMockScreenshotHelper;
+ private SystemActionPerformer mSystemActionPerformer;
+
+ @Mock private Context mMockContext;
+ @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal;
+ @Mock private WindowManagerInternal mMockWindowManagerInternal;
+ @Mock private StatusBarManager mMockStatusBarManager;
+ @Mock private ScreenshotHelper mMockScreenshotHelper;
+ @Mock private SystemActionPerformer.SystemActionsChangedListener mMockListener;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+ }
+
+ private void setupWithMockContext() {
+ doReturn(mMockStatusBarManager).when(
+ mMockContext).getSystemService(android.app.Service.STATUS_BAR_SERVICE);
+ doReturn(InstrumentationRegistry.getContext().getResources()).when(
+ mMockContext).getResources();
+ mSystemActionPerformer = new SystemActionPerformer(
+ mMockContext,
+ mMockWindowManagerInternal,
+ () -> mMockScreenshotHelper,
+ mMockListener);
+ }
+
+ private void setupWithRealContext() {
+ mSystemActionPerformer = new SystemActionPerformer(
+ InstrumentationRegistry.getContext(),
+ mMockWindowManagerInternal,
+ () -> mMockScreenshotHelper,
+ mMockListener);
+ }
+
+ // We need below two help functions because AccessbilityAction.equals function only compares
+ // action ids. To verify the test result here, we are also looking at action labels.
+ private void assertHasLegacyAccessibilityAction(
+ List<AccessibilityAction> actions, AccessibilityAction action) {
+ boolean foundAction = false;
+ for (AccessibilityAction a : actions) {
+ if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) {
+ foundAction = true;
+ break;
+ }
+ }
+ assertTrue(foundAction);
+ }
+
+ private void assertHasNoLegacyAccessibilityAction(
+ List<AccessibilityAction> actions, AccessibilityAction action) {
+ boolean foundAction = false;
+ for (AccessibilityAction a : actions) {
+ if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) {
+ foundAction = true;
+ break;
+ }
+ }
+ assertFalse(foundAction);
+ }
+
+ @Test
+ public void testRegisterSystemAction_addedIntoAvailableSystemActions() {
+ setupWithRealContext();
+ // Before any new system action is registered, getSystemActions returns all legacy actions
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ // Register a new system action
+ mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size());
+ assertThat(actions, hasItem(NEW_ACCESSIBILITY_ACTION));
+ }
+
+ @Test
+ public void testRegisterSystemAction_overrideLegacyAction() {
+ setupWithRealContext();
+ // Before any new system action is registered, getSystemActions returns all legacy actions
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ // Overriding a legacy system action using legacy notification action id
+ mSystemActionPerformer.registerSystemAction(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+ }
+
+ @Test
+ public void testUnregisterSystemAction_removeFromAvailableSystemActions() {
+ setupWithRealContext();
+ // Before any new system action is registered, getSystemActions returns all legacy actions
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ // Register a new system action
+ mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size());
+
+ mSystemActionPerformer.unregisterSystemAction(NEW_ACTION_ID);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertThat(actions, is(not(hasItem(NEW_ACCESSIBILITY_ACTION))));
+ }
- when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE))
- .thenReturn(mMockStatusBarManager);
+ @Test
+ public void testUnregisterSystemAction_removeOverrideForLegacyAction() {
+ setupWithRealContext();
+
+ // Overriding a legacy system action
+ mSystemActionPerformer.registerSystemAction(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1);
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+
+ // Remove the overriding action using legacy action id
+ mSystemActionPerformer.unregisterSystemAction(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertHasNoLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+ }
+
+ @Test
+ public void testPerformSystemActionNewAction() throws CanceledException {
+ setupWithRealContext();
- mSystemActionPerformer =
- new SystemActionPerformer(mMockContext, mMockWindowManagerInternal,
- () -> mMockScreenshotHelper);
+ final CountDownLatch latch = new CountDownLatch(1);
+ mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+ TestBroadcastReceiver br = new TestBroadcastReceiver(latch);
+ br.register(InstrumentationRegistry.getTargetContext());
+ mSystemActionPerformer.performSystemAction(NEW_ACTION_ID);
+ try {
+ latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("RemoteAction should be triggered.");
+ } finally {
+ br.unregister(InstrumentationRegistry.getTargetContext());
+ }
}
@Test
- public void testNotifications_expandsNotificationPanel() {
+ public void testPerformSystemActionOverrideLegacyActionUsingLegacyActionId()
+ throws CanceledException {
+ setupWithRealContext();
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ mSystemActionPerformer.registerSystemAction(
+ AccessibilityService.GLOBAL_ACTION_RECENTS, NEW_TEST_ACTION_1);
+ TestBroadcastReceiver br = new TestBroadcastReceiver(latch);
+ br.register(InstrumentationRegistry.getTargetContext());
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ try {
+ latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("RemoteAction should be triggered.");
+ } finally {
+ br.unregister(InstrumentationRegistry.getTargetContext());
+ }
+ verify(mMockStatusBarManagerInternal, never()).toggleRecentApps();
+ // Now revert to legacy action
+ mSystemActionPerformer.unregisterSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ verify(mMockStatusBarManagerInternal).toggleRecentApps();
+ }
+
+ @Test
+ public void testNotifications_expandsNotificationPanel_legacy() {
+ setupWithMockContext();
mSystemActionPerformer
.performSystemAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
verify(mMockStatusBarManager).expandNotificationsPanel();
}
@Test
- public void testQuickSettings_requestsQuickSettingsPanel() {
+ public void testQuickSettings_requestsQuickSettingsPanel_legacy() {
+ setupWithMockContext();
mSystemActionPerformer
.performSystemAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
verify(mMockStatusBarManager).expandSettingsPanel();
}
@Test
- public void testPowerDialog_requestsFromWindowManager() {
+ public void testRecentApps_legacy() {
+ setupWithRealContext();
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ verify(mMockStatusBarManagerInternal).toggleRecentApps();
+ }
+
+ @Test
+ public void testPowerDialog_requestsFromWindowManager_legacy() {
+ setupWithMockContext();
mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
verify(mMockWindowManagerInternal).showGlobalActions();
}
@Test
- public void testScreenshot_requestsFromScreenshotHelper() {
+ public void testToggleSplitScreen_legacy() {
+ setupWithRealContext();
+ mSystemActionPerformer.performSystemAction(
+ AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
+ verify(mMockStatusBarManagerInternal).toggleSplitScreen();
+ }
+
+ @Test
+ public void testScreenshot_requestsFromScreenshotHelper_legacy() {
+ setupWithMockContext();
mSystemActionPerformer.performSystemAction(
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
verify(mMockScreenshotHelper).takeScreenshot(
eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
anyBoolean(), any(Handler.class), any());
}
+
+ // PendingIntent is a final class and cannot be mocked. So we are using this
+ // Broadcast receiver to verify the registered remote action is called correctly.
+ private static final class TestBroadcastReceiver extends BroadcastReceiver {
+ private CountDownLatch mLatch;
+ private boolean mRegistered;
+ private final IntentFilter mFilter;
+
+ TestBroadcastReceiver(CountDownLatch latch) {
+ mLatch = latch;
+ mRegistered = false;
+ mFilter = new IntentFilter(INTENT_ACTION1);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mLatch.countDown();
+ }
+
+ void register(Context context) {
+ if (!mRegistered) {
+ context.registerReceiver(this, mFilter);
+ mRegistered = true;
+ }
+ }
+
+ void unregister(Context context) {
+ if (mRegistered) {
+ context.unregisterReceiver(this);
+ }
+ }
+ }
}