diff options
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); + } + } + } } |