diff options
| author | 2020-01-17 19:09:11 +0000 | |
|---|---|---|
| committer | 2020-01-17 19:09:11 +0000 | |
| commit | 60d32dbf5d9b34aeb693b575ea03cadb0b6338dd (patch) | |
| tree | 95d7a4d4f6024e8842b0032ffb89cb17849b22cd | |
| parent | 85f8385f42d8ab44f6f873fcc235188077d95178 (diff) | |
| parent | 8302c6cd858a185868ac14d279e0d6ac4213f564 (diff) | |
Merge "API on AccessibilityService side to support customized system actions."
11 files changed, 325 insertions, 31 deletions
diff --git a/api/current.txt b/api/current.txt index 0b35d64651ab..0d233713d8f6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2854,6 +2854,7 @@ package android.accessibilityservice { method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); method @NonNull public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController(); + method @NonNull public final java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getSystemActions(); method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays(); method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); @@ -2863,6 +2864,7 @@ package android.accessibilityservice { method public abstract void onInterrupt(); method protected boolean onKeyEvent(android.view.KeyEvent); method protected void onServiceConnected(); + method public void onSystemActionsChanged(); method public final boolean performGlobalAction(int); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 0bd8ce692884..ee6ccc2f5cd7 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -48,6 +48,7 @@ import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.os.HandlerCaller; @@ -56,6 +57,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -392,7 +394,8 @@ public abstract class AccessibilityService extends Service { private static final String LOG_TAG = "AccessibilityService"; /** - * Interface used by IAccessibilityServiceWrapper to call the service from its main thread. + * Interface used by IAccessibilityServiceClientWrapper to call the service from its main + * thread. * @hide */ public interface Callbacks { @@ -413,6 +416,8 @@ public abstract class AccessibilityService extends Service { /** Accessbility button clicked callbacks for different displays */ void onAccessibilityButtonClicked(int displayId); void onAccessibilityButtonAvailabilityChanged(boolean available); + /** This is called when the system action list is changed. */ + void onSystemActionsChanged(); } /** @@ -1643,6 +1648,29 @@ public abstract class AccessibilityService extends Service { available); } + /** This is called when the system action list is changed. */ + public void onSystemActionsChanged() { + } + + /** + * Returns a list of system actions available in the system right now. + * + * @return A list of available system actions. + */ + public final @NonNull List<AccessibilityAction> getSystemActions() { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (connection != null) { + try { + return connection.getSystemActions(); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while calling getSystemActions", re); + re.rethrowFromSystemServer(); + } + } + return Collections.emptyList(); + } + /** * Performs a global action. Such an action can be performed * at any moment regardless of the current application or user @@ -1894,6 +1922,11 @@ public abstract class AccessibilityService extends Service { public void onAccessibilityButtonAvailabilityChanged(boolean available) { AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available); } + + @Override + public void onSystemActionsChanged() { + AccessibilityService.this.onSystemActionsChanged(); + } }); } @@ -1918,6 +1951,7 @@ public abstract class AccessibilityService extends Service { private static final int DO_ON_FINGERPRINT_GESTURE = 11; private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12; private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13; + private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14; private final HandlerCaller mCaller; @@ -2014,6 +2048,11 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } + /** This is called when the system action list is changed. */ + public void onSystemActionsChanged() { + mCaller.sendMessage(mCaller.obtainMessage(DO_ON_SYSTEM_ACTIONS_CHANGED)); + } + @Override public void executeMessage(Message message) { switch (message.what) { @@ -2035,14 +2074,14 @@ public abstract class AccessibilityService extends Service { /* ignore - best effort */ } } - } return; - + return; + } case DO_ON_INTERRUPT: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onInterrupt(); } - } return; - + return; + } case DO_INIT: { mConnectionId = message.arg1; SomeArgs args = (SomeArgs) message.obj; @@ -2062,18 +2101,18 @@ public abstract class AccessibilityService extends Service { AccessibilityInteractionClient.getInstance().clearCache(); mCallback.init(AccessibilityInteractionClient.NO_ID, null); } - } return; - + return; + } case DO_ON_GESTURE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onGesture((AccessibilityGestureEvent) message.obj); } - } return; - + return; + } case DO_CLEAR_ACCESSIBILITY_CACHE: { AccessibilityInteractionClient.getInstance().clearCache(); - } return; - + return; + } case DO_ON_KEY_EVENT: { KeyEvent event = (KeyEvent) message.obj; try { @@ -2096,8 +2135,8 @@ public abstract class AccessibilityService extends Service { /* ignore - best effort */ } } - } return; - + return; + } case DO_ON_MAGNIFICATION_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final SomeArgs args = (SomeArgs) message.obj; @@ -2110,45 +2149,53 @@ public abstract class AccessibilityService extends Service { mCallback.onMagnificationChanged(displayId, region, scale, centerX, centerY); } - } return; - + return; + } case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final int showMode = (int) message.arg1; mCallback.onSoftKeyboardShowModeChanged(showMode); } - } return; - + return; + } case DO_GESTURE_COMPLETE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final boolean successfully = message.arg2 == 1; mCallback.onPerformGestureResult(message.arg1, successfully); } - } return; + return; + } case DO_ON_FINGERPRINT_ACTIVE_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1); } - } return; + return; + } case DO_ON_FINGERPRINT_GESTURE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onFingerprintGesture(message.arg1); } - } return; - - case (DO_ACCESSIBILITY_BUTTON_CLICKED): { + return; + } + case DO_ACCESSIBILITY_BUTTON_CLICKED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onAccessibilityButtonClicked(message.arg1); } - } return; - - case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): { + return; + } + case DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final boolean available = (message.arg1 != 0); mCallback.onAccessibilityButtonAvailabilityChanged(available); } - } return; - + return; + } + case DO_ON_SYSTEM_ACTIONS_CHANGED: { + if (mConnectionId != AccessibilityInteractionClient.NO_ID) { + mCallback.onSystemActionsChanged(); + } + return; + } default : Log.w(LOG_TAG, "Unknown message type " + message.what); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 8ec3aea2728f..58b0d19f605a 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -55,4 +55,6 @@ import android.view.KeyEvent; void onAccessibilityButtonClicked(int displayId); void onAccessibilityButtonAvailabilityChanged(boolean available); + + void onSystemActionsChanged(); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 4ea5c62bf05b..5db4dd7470d8 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -25,8 +25,10 @@ import android.os.RemoteCallback; import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.accessibility.AccessibilityWindowInfo; +import java.util.List; /** * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService. @@ -67,6 +69,7 @@ interface IAccessibilityServiceConnection { AccessibilityServiceInfo getServiceInfo(); boolean performGlobalAction(int action); + List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions(); void disableSelf(); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 2579bd1abbfd..35cf68737ccc 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -1254,6 +1254,11 @@ public final class UiAutomation { } @Override + public void onSystemActionsChanged() { + /* do nothing */ + } + + @Override public boolean onGesture(AccessibilityGestureEvent gestureEvent) { /* do nothing */ return false; diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java new file mode 100644 index 000000000000..c65ef9a56cd8 --- /dev/null +++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2020 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 android.accessibilityservice; + +import static org.mockito.Mockito.verify; + +import android.content.Intent; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.view.accessibility.AccessibilityEvent; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests for AccessibilityService. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AccessibilityServiceTest { + private static final String TAG = "AccessibilityServiceTest"; + private static final int CONNECTION_ID = 1; + + private static class AccessibilityServiceTestClass extends AccessibilityService { + private IAccessibilityServiceClient mCallback; + private Looper mLooper; + + AccessibilityServiceTestClass() { + super(); + attachBaseContext(InstrumentationRegistry.getContext()); + mLooper = InstrumentationRegistry.getContext().getMainLooper(); + } + + public void setupCallback(IAccessibilityServiceClient callback) { + mCallback = callback; + } + + public Looper getMainLooper() { + return mLooper; + } + + public void onAccessibilityEvent(AccessibilityEvent event) { } + public void onInterrupt() { } + + @Override + public void onSystemActionsChanged() { + try { + if (mCallback != null) mCallback.onSystemActionsChanged(); + } catch (RemoteException e) { + } + } + } + + private @Mock IAccessibilityServiceClient mMockClientForCallback; + private @Mock IAccessibilityServiceConnection mMockConnection; + private @Mock IBinder mMockIBinder; + private IAccessibilityServiceClient mServiceInterface; + private AccessibilityServiceTestClass mService; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + mService = new AccessibilityServiceTestClass(); + mService.setupCallback(mMockClientForCallback); + mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent()); + mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder); + } + + @Test + public void testOnSystemActionsChanged() throws RemoteException { + mServiceInterface.onSystemActionsChanged(); + + verify(mMockClientForCallback).onSystemActionsChanged(); + } + + @Test + public void testGetSystemActions() throws RemoteException { + mService.getSystemActions(); + + verify(mMockConnection).getSystemActions(); + } +} diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index f151b810eea3..8b8e9ea4c6ee 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -25,6 +25,9 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteCallback; +import java.util.Collections; +import java.util.List; + /** * Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement * all of the methods @@ -85,6 +88,10 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon return false; } + public List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() { + return Collections.emptyList(); + } + public void disableSelf() {} public void setOnKeyEventResult(boolean handled, int sequence) {} diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index c3965c44d4c0..3e74b7a92f5d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -80,6 +80,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -775,6 +776,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } @Override + public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() { + synchronized (mLock) { + if (!hasRightsToCurrentUserLocked()) { + return Collections.emptyList(); + } + } + return mSystemActionPerformer.getSystemActions(); + } + + @Override public boolean isFingerprintGestureDetectionAvailable() { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { return false; @@ -1252,6 +1263,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ gestureEvent).sendToTarget(); } + public void notifySystemActionsChangedLocked() { + mInvocationHandler.sendEmptyMessage( + InvocationHandler.MSG_ON_SYSTEM_ACTIONS_CHANGED); + } + public void notifyClearAccessibilityNodeInfoCache() { mInvocationHandler.sendEmptyMessage( InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE); @@ -1350,6 +1366,18 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } + private void notifySystemActionsChangedInternal() { + final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); + if (listener != null) { + try { + listener.onSystemActionsChanged(); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error sending system actions change to " + mService, + re); + } + } + } + private void notifyClearAccessibilityCacheInternal() { final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { @@ -1544,6 +1572,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private static final int MSG_ON_SOFT_KEYBOARD_STATE_CHANGED = 6; private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7; private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8; + private static final int MSG_ON_SYSTEM_ACTIONS_CHANGED = 9; /** List of magnification callback states, mapping from displayId -> Boolean */ @GuardedBy("mlock") @@ -1591,7 +1620,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final boolean available = (message.arg1 != 0); notifyAccessibilityButtonAvailabilityChangedInternal(available); } break; - + case MSG_ON_SYSTEM_ACTIONS_CHANGED: { + notifySystemActionsChangedInternal(); + break; + } default: { throw new IllegalArgumentException("Unknown message: " + type); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index f3a415ef4cb5..bcaecea4c388 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -137,7 +137,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub implements AbstractAccessibilityServiceConnection.SystemSupport, AccessibilityUserState.ServiceInfoChangeListener, AccessibilityWindowManager.AccessibilityEventSender, - AccessibilitySecurityPolicy.AccessibilityUserManager { + AccessibilitySecurityPolicy.AccessibilityUserManager, + SystemActionPerformer.SystemActionsChangedListener { private static final boolean DEBUG = false; @@ -294,7 +295,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); mPackageManager = mContext.getPackageManager(); mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this); - mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService); + mSystemActionPerformer = + new SystemActionPerformer(mContext, mWindowManagerService, null, this); mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler, mWindowManagerService, this, mSecurityPolicy, this); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); @@ -904,6 +906,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + /** + * Called when the system action list is changed. + */ + @Override + public void onSystemActionsChanged() { + synchronized (mLock) { + AccessibilityUserState state = getCurrentUserStateLocked(); + notifySystemActionsChangedLocked(state); + } + } + + @VisibleForTesting + void notifySystemActionsChangedLocked(AccessibilityUserState userState) { + for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { + AccessibilityServiceConnection service = userState.mBoundServices.get(i); + service.notifySystemActionsChangedLocked(); + } + } + @VisibleForTesting public boolean notifyKeyEvent(KeyEvent event, int policyFlags) { synchronized (mLock) { 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 e1e9b7e7b7cb..cf10559c8198 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -495,6 +495,13 @@ public class AbstractAccessibilityServiceConnectionTest { } @Test + public void getSystemActions() { + List<AccessibilityNodeInfo.AccessibilityAction> actions = + mServiceConnection.getSystemActions(); + verify(mMockSystemActionPerformer).getSystemActions(); + } + + @Test public void isFingerprintGestureDetectionAvailable_hasFingerPrintSupport_returnTrue() { when(mMockFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable()) .thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 75239db92121..ec928fb278be 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -16,16 +16,28 @@ package com.android.server.accessibility; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.Manifest; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.IAccessibilityServiceClient; import android.app.PendingIntent; import android.app.RemoteAction; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.graphics.drawable.Icon; +import android.os.IBinder; +import android.os.UserHandle; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -34,6 +46,7 @@ import androidx.test.InstrumentationRegistry; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -59,6 +72,14 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION = new AccessibilityAction(ACTION_ID, LABEL); + static final ComponentName COMPONENT_NAME = new ComponentName( + "com.android.server.accessibility", "AccessibilityManagerServiceTest"); + static final int SERVICE_ID = 42; + + @Mock private Context mMockContext; + @Mock private AccessibilityServiceInfo mMockServiceInfo; + @Mock private ResolveInfo mMockResolveInfo; + @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport; @Mock private PackageManager mMockPackageManager; @Mock private WindowManagerInternal mMockWindowManagerService; @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy; @@ -66,7 +87,12 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { @Mock private AccessibilityWindowManager mMockA11yWindowManager; @Mock private AccessibilityDisplayListener mMockA11yDisplayListener; @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal; + @Mock private IBinder mMockBinder; + @Mock private IAccessibilityServiceClient mMockServiceClient; + private AccessibilityUserState mUserState; + private MessageCapturingHandler mHandler = new MessageCapturingHandler(null); + private AccessibilityServiceConnection mAccessibilityServiceConnection; private AccessibilityManagerService mA11yms; @Override @@ -74,9 +100,11 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { MockitoAnnotations.initMocks(this); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); - LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerService); + LocalServices.addService( + WindowManagerInternal.class, mMockWindowManagerService); LocalServices.addService( ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal); + mA11yms = new AccessibilityManagerService( InstrumentationRegistry.getContext(), mMockPackageManager, @@ -86,6 +114,35 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { mMockA11yDisplayListener); } + private void setupAccessibilityServiceConnection() { + when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn( + InstrumentationRegistry.getContext().getSystemService( + Context.DISPLAY_SERVICE)); + mUserState = new AccessibilityUserState(UserHandle.USER_SYSTEM, mMockContext, mA11yms); + + when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); + mMockResolveInfo.serviceInfo = mock(ServiceInfo.class); + mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class); + + when(mMockBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient); + mAccessibilityServiceConnection = new AccessibilityServiceConnection( + mUserState, + mMockContext, + COMPONENT_NAME, + mMockServiceInfo, + SERVICE_ID, + mHandler, + new Object(), + mMockSecurityPolicy, + mMockSystemSupport, + mMockWindowManagerService, + mMockSystemActionPerformer, + mMockA11yWindowManager, + mMockActivityTaskManagerInternal); + mAccessibilityServiceConnection.bindLocked(); + mAccessibilityServiceConnection.onServiceConnected(COMPONENT_NAME, mMockBinder); + } + @SmallTest public void testRegisterSystemActionWithoutPermission() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission( @@ -125,4 +182,11 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { mA11yms.unregisterSystemAction(ACTION_ID); verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID); } + + @SmallTest + public void testOnSystemActionsChanged() throws Exception { + setupAccessibilityServiceConnection(); + mA11yms.notifySystemActionsChangedLocked(mUserState); + verify(mMockServiceClient).onSystemActionsChanged(); + } } |