Merge "API on AccessibilityService side to support customized system actions."
This commit is contained in:
@@ -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>);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -55,4 +55,6 @@ import android.view.KeyEvent;
|
||||
void onAccessibilityButtonClicked(int displayId);
|
||||
|
||||
void onAccessibilityButtonAvailabilityChanged(boolean available);
|
||||
|
||||
void onSystemActionsChanged();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -1253,6 +1253,11 @@ public final class UiAutomation {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemActionsChanged() {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
|
||||
/* do nothing */
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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;
|
||||
@@ -774,6 +775,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
|
||||
return mSystemActionPerformer.performSystemAction(action);
|
||||
}
|
||||
|
||||
@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)) {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -494,6 +494,13 @@ public class AbstractAccessibilityServiceConnectionTest {
|
||||
verify(mMockSystemActionPerformer).performSystemAction(GLOBAL_ACTION_HOME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSystemActions() {
|
||||
List<AccessibilityNodeInfo.AccessibilityAction> actions =
|
||||
mServiceConnection.getSystemActions();
|
||||
verify(mMockSystemActionPerformer).getSystemActions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isFingerprintGestureDetectionAvailable_hasFingerPrintSupport_returnTrue() {
|
||||
when(mMockFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable())
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user