System action registration API for SystemUI

Bug: 136286274
Test: atest AccessibilityManagerTest
      atest AccessibilityManagerServiceTest
Change-Id: I6bbdf3627bfc9b39551cc7809dda1cf43d1d6ea4
diff --git a/api/system-current.txt b/api/system-current.txt
index ca56a61..f58f4aa 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11495,6 +11495,8 @@
   public final class AccessibilityManager {
     method public int getAccessibilityWindowId(@Nullable android.os.IBinder);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int);
   }
 
 }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index ff31bcc..2e5a4b5 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -31,6 +31,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
+import android.app.RemoteAction;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -1209,6 +1210,61 @@
     }
 
     /**
+     * Register the provided {@link RemoteAction} with the given actionId
+     *
+     * @param action The remote action to be registered with the given actionId as system action.
+     * @param actionId The id uniquely identify the system action.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    public void registerSystemAction(@NonNull RemoteAction action, int actionId) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.registerSystemAction(action, actionId);
+
+            if (DEBUG) {
+                Log.i(LOG_TAG, "System action " + action.getTitle() + " is registered.");
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error registering system action " + action.getTitle() + " ", re);
+        }
+    }
+
+   /**
+     * Unregister a system action with the given actionId
+     *
+     * @param actionId The id uniquely identify the system action to be unregistered.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    public void unregisterSystemAction(int actionId) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.unregisterSystemAction(actionId);
+
+            if (DEBUG) {
+                Log.i(LOG_TAG, "System action with actionId " + actionId + " is unregistered.");
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error unregistering system action with actionId " + actionId + " ", re);
+        }
+    }
+
+    /**
      * Notifies that the accessibility button in the system's navigation area has been clicked
      *
      * @param displayId The logical display id.
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 36515b3..392db57 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.app.RemoteAction;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.accessibilityservice.IAccessibilityServiceClient;
@@ -82,4 +83,7 @@
     int getAccessibilityWindowId(IBinder windowToken);
 
     long getRecommendedTimeoutMillis();
+
+    oneway void registerSystemAction(in RemoteAction action, int actionId);
+    oneway void unregisterSystemAction(int actionId);
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
similarity index 78%
rename from services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
rename to core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index b9b6d55..e23c51e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.accessibility;
+package android.view.accessibility;
 
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertSame;
@@ -29,16 +29,17 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Instrumentation;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.UserHandle;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-import android.view.accessibility.IAccessibilityManagerClient;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.IntPair;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 
 import org.junit.After;
 import org.junit.Before;
@@ -57,6 +58,16 @@
 public class AccessibilityManagerTest {
     private static final boolean WITH_A11Y_ENABLED = true;
     private static final boolean WITH_A11Y_DISABLED = false;
+    private static final String LABEL = "label";
+    private static final String INTENT_ACTION = "TESTACTION";
+    private static final String DESCRIPTION = "description";
+    private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
+            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0);
+    private static final RemoteAction TEST_ACTION = new RemoteAction(
+            Icon.createWithContentUri("content://test"),
+            LABEL,
+            DESCRIPTION,
+            TEST_PENDING_INTENT);
 
     @Mock private IAccessibilityManager mMockService;
     private MessageCapturingHandler mHandler;
@@ -122,6 +133,29 @@
     }
 
     @Test
+    public void testRegisterSystemAction() throws Exception {
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        RemoteAction action = new RemoteAction(
+                Icon.createWithContentUri("content://test"),
+                LABEL,
+                DESCRIPTION,
+                TEST_PENDING_INTENT);
+        final int actionId = 0;
+        manager.registerSystemAction(TEST_ACTION, actionId);
+
+        verify(mMockService).registerSystemAction(TEST_ACTION, actionId);
+    }
+
+    @Test
+    public void testUnregisterSystemAction() throws Exception {
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        final int actionId = 0;
+        manager.unregisterSystemAction(actionId);
+
+        verify(mMockService).unregisterSystemAction(actionId);
+    }
+
+    @Test
     public void testIsEnabled() throws Exception {
         // Create manager with a11y enabled
         AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6a6e2b2..58d3489 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -35,6 +35,7 @@
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.appwidget.AppWidgetManagerInternal;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -148,6 +149,8 @@
     //       their capabilities are ready.
     private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000;
 
+    static final String FUNCTION_REGISTER_SYSTEM_ACTION = "registerSystemAction";
+    static final String FUNCTION_UNREGISTER_SYSTEM_ACTION = "unregisterSystemAction";
     private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
         "registerUiTestAutomationService";
 
@@ -253,6 +256,27 @@
         }
     }
 
+    @VisibleForTesting
+    AccessibilityManagerService(
+            Context context,
+            PackageManager packageManager,
+            AccessibilitySecurityPolicy securityPolicy,
+            SystemActionPerformer systemActionPerformer,
+            AccessibilityWindowManager a11yWindowManager,
+            AccessibilityDisplayListener a11yDisplayListener) {
+        mContext = context;
+        mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
+        mMainHandler = new MainHandler(mContext.getMainLooper());
+        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mPackageManager = packageManager;
+        mSecurityPolicy = securityPolicy;
+        mSystemActionPerformer = systemActionPerformer;
+        mA11yWindowManager = a11yWindowManager;
+        mA11yDisplayListener = a11yDisplayListener;
+        init();
+    }
+
     /**
      * Creates a new instance.
      *
@@ -260,21 +284,24 @@
      */
     public AccessibilityManagerService(Context context) {
         mContext = context;
-        mPackageManager = mContext.getPackageManager();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
-        mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
         mMainHandler = new MainHandler(mContext.getMainLooper());
+        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mPackageManager = mContext.getPackageManager();
+        mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
         mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService);
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                 mWindowManagerService, this, mSecurityPolicy, this);
         mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
-        mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
-        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+        init();
+    }
 
+    private void init() {
+        mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
         registerBroadcastReceivers();
         new AccessibilityContentObserver(mMainHandler).register(
-                context.getContentResolver());
+                mContext.getContentResolver());
     }
 
     @Override
@@ -623,6 +650,30 @@
         event.recycle();
     }
 
+    /**
+     * This is the implementation of AccessibilityManager system API.
+     * System UI calls into this method through AccessibilityManager system API to register a
+     * system action.
+     */
+    @Override
+    public void registerSystemAction(RemoteAction action, int actionId) {
+        mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+                FUNCTION_REGISTER_SYSTEM_ACTION);
+        mSystemActionPerformer.registerSystemAction(actionId, action);
+    }
+
+    /**
+     * This is the implementation of AccessibilityManager system API.
+     * System UI calls into this method through AccessibilityManager system API to unregister a
+     * system action.
+     */
+    @Override
+    public void unregisterSystemAction(int actionId) {
+        mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+                FUNCTION_UNREGISTER_SYSTEM_ACTION);
+        mSystemActionPerformer.unregisterSystemAction(actionId);
+    }
+
     @Override
     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
         synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 1754926..11dcfef 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -157,8 +157,13 @@
     /**
      * 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.
+     *
+     * This method is supposed to be package internal since this class is meant to be used by
+     * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public
+     * to be mocked.
      */
-    void registerSystemAction(int id, RemoteAction action) {
+    @VisibleForTesting
+    public void registerSystemAction(int id, RemoteAction action) {
         synchronized (mSystemActionLock) {
             mRegisteredSystemActions.put(id, action);
         }
@@ -170,8 +175,13 @@
     /**
      * This method is called to unregister a system action previously registered through
      * registerSystemAction.
+     *
+     * This method is supposed to be package internal since this class is meant to be used by
+     * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public
+     * to be mocked.
      */
-    void unregisterSystemAction(int id) {
+    @VisibleForTesting
+    public void unregisterSystemAction(int id) {
         synchronized (mSystemActionLock) {
             mRegisteredSystemActions.remove(id);
         }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d11d987..591c3a3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -85,6 +85,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
new file mode 100644
index 0000000..75239db
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * APCT tests for {@link AccessibilityManagerService}.
+ */
+public class AccessibilityManagerServiceTest extends AndroidTestCase {
+    private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
+    private static final int ACTION_ID = 20;
+    private static final String LABEL = "label";
+    private static final String INTENT_ACTION = "TESTACTION";
+    private static final String DESCRIPTION = "description";
+    private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
+            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0);
+    private static final RemoteAction TEST_ACTION = new RemoteAction(
+            Icon.createWithContentUri("content://test"),
+            LABEL,
+            DESCRIPTION,
+            TEST_PENDING_INTENT);
+    private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
+            new AccessibilityAction(ACTION_ID, LABEL);
+
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private WindowManagerInternal mMockWindowManagerService;
+    @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
+    @Mock private SystemActionPerformer mMockSystemActionPerformer;
+    @Mock private AccessibilityWindowManager mMockA11yWindowManager;
+    @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
+    @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
+
+    private AccessibilityManagerService mA11yms;
+
+    @Override
+    protected void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        LocalServices.removeServiceForTest(WindowManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+        LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerService);
+        LocalServices.addService(
+                ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
+        mA11yms = new AccessibilityManagerService(
+            InstrumentationRegistry.getContext(),
+            mMockPackageManager,
+            mMockSecurityPolicy,
+            mMockSystemActionPerformer,
+            mMockA11yWindowManager,
+            mMockA11yDisplayListener);
+    }
+
+    @SmallTest
+    public void testRegisterSystemActionWithoutPermission() throws Exception {
+        doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission(
+                Manifest.permission.MANAGE_ACCESSIBILITY,
+                AccessibilityManagerService.FUNCTION_REGISTER_SYSTEM_ACTION);
+
+        try {
+            mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
+            fail();
+        } catch (SecurityException expected) {
+        }
+        verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
+    }
+
+    @SmallTest
+    public void testRegisterSystemAction() throws Exception {
+        mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
+        verify(mMockSystemActionPerformer).registerSystemAction(ACTION_ID, TEST_ACTION);
+    }
+
+    @SmallTest
+    public void testUnregisterSystemActionWithoutPermission() throws Exception {
+        doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission(
+                Manifest.permission.MANAGE_ACCESSIBILITY,
+                AccessibilityManagerService.FUNCTION_UNREGISTER_SYSTEM_ACTION);
+
+        try {
+            mA11yms.unregisterSystemAction(ACTION_ID);
+            fail();
+        } catch (SecurityException expected) {
+        }
+        verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
+    }
+
+    @SmallTest
+    public void testUnregisterSystemAction() throws Exception {
+        mA11yms.unregisterSystemAction(ACTION_ID);
+        verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 99dd9a1..2ce70b6f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -44,6 +44,7 @@
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.Display;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 67075ed..9db5a08 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -53,6 +53,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnection;
 
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 44a514f..96ae102e5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -31,6 +31,8 @@
 import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
index de7bc44..30d00ad 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -31,6 +31,7 @@
 import android.view.KeyEvent;
 
 import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 23ce483..4123556 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -44,6 +44,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.accessibility.KeyEventDispatcher.KeyEventFilter;
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.policy.WindowManagerPolicy;
 
 import org.hamcrest.Description;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 322653b..78e651b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -33,6 +33,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.policy.WindowManagerPolicy;
 
 import org.hamcrest.Description;
@@ -212,4 +213,4 @@
             description.appendText("Matches key event");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index 773b877..82c6498 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -47,6 +47,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 36e854c..1ac4a8e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -54,6 +54,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.accessibility.utils.MotionEventMatcher;
 
 import org.hamcrest.Description;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 8da927d..c8baca6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -37,6 +37,7 @@
 import android.os.IBinder;
 import android.view.accessibility.AccessibilityEvent;
 
+import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index f71be7b..a6625ab 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -22,6 +22,7 @@
     static_libs: [
         "junit",
         "hamcrest-library",
+        "androidx.test.runner",
     ],
 
     libs: [
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
rename to tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
index e2b517f..bce2ab5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
+++ b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.accessibility;
+package com.android.server.accessibility.test;
 
 import android.os.Handler;
 import android.os.Looper;
@@ -31,7 +31,7 @@
  * at their target.
  */
 public class MessageCapturingHandler extends Handler {
-    List<Pair<Message, Long>> timedMessages = new ArrayList<>();
+    public List<Pair<Message, Long>> timedMessages = new ArrayList<>();
 
     Handler.Callback mCallback;