Merge "Add unit tests for ProxyManager" into udc-dev
This commit is contained in:
@@ -1012,8 +1012,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
+ Binder.getCallingPid() + " for device id " + deviceId
|
||||
+ " with package names " + Arrays.toString(client.mPackageNames));
|
||||
}
|
||||
return IntPair.of(mProxyManager.getStateLocked(deviceId,
|
||||
mUiAutomationManager.isUiAutomationRunningLocked()),
|
||||
return IntPair.of(mProxyManager.getStateLocked(deviceId),
|
||||
client.mLastSentRelevantEventTypes);
|
||||
}
|
||||
mGlobalClients.register(callback, client);
|
||||
@@ -1028,8 +1027,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
+ Binder.getCallingPid() + " for device id " + deviceId
|
||||
+ " with package names " + Arrays.toString(client.mPackageNames));
|
||||
}
|
||||
return IntPair.of(mProxyManager.getStateLocked(deviceId,
|
||||
mUiAutomationManager.isUiAutomationRunningLocked()),
|
||||
return IntPair.of(mProxyManager.getStateLocked(deviceId),
|
||||
client.mLastSentRelevantEventTypes);
|
||||
}
|
||||
userState.mUserClients.register(callback, client);
|
||||
@@ -4024,9 +4022,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mProxyManager.registerProxy(client, displayId, mContext,
|
||||
sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(),
|
||||
mWindowManagerService);
|
||||
mProxyManager.registerProxy(client, displayId, sIdCounter++, mSecurityPolicy,
|
||||
this, getTraceManager(), mWindowManagerService);
|
||||
|
||||
synchronized (mLock) {
|
||||
notifyClearAccessibilityCacheLocked();
|
||||
|
||||
@@ -61,7 +61,6 @@ import java.util.function.Consumer;
|
||||
* proxy connection will belong to a separate user state.
|
||||
*
|
||||
* TODO(241117292): Remove or cut down during simultaneous user refactoring.
|
||||
* TODO(262244375): Add unit tests.
|
||||
*/
|
||||
public class ProxyManager {
|
||||
private static final boolean DEBUG = false;
|
||||
@@ -128,7 +127,7 @@ public class ProxyManager {
|
||||
RemoteCallbackList<IAccessibilityManagerClient> getCurrentUserClientsLocked();
|
||||
}
|
||||
|
||||
ProxyManager(Object lock, AccessibilityWindowManager awm,
|
||||
public ProxyManager(Object lock, AccessibilityWindowManager awm,
|
||||
Context context, Handler mainHandler, UiAutomationManager uiAutomationManager,
|
||||
SystemSupport systemSupport) {
|
||||
mLock = lock;
|
||||
@@ -144,9 +143,7 @@ public class ProxyManager {
|
||||
* Creates the service connection.
|
||||
*/
|
||||
public void registerProxy(IAccessibilityServiceClient client, int displayId,
|
||||
Context context,
|
||||
int id, Handler mainHandler,
|
||||
AccessibilitySecurityPolicy securityPolicy,
|
||||
int id, AccessibilitySecurityPolicy securityPolicy,
|
||||
AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
|
||||
AccessibilityTrace trace,
|
||||
WindowManagerInternal windowManagerInternal) throws RemoteException {
|
||||
@@ -169,8 +166,8 @@ public class ProxyManager {
|
||||
info.setComponentName(new ComponentName(PROXY_COMPONENT_PACKAGE_NAME,
|
||||
componentClassDisplayName));
|
||||
ProxyAccessibilityServiceConnection connection =
|
||||
new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info,
|
||||
id, mainHandler, mLock, securityPolicy, systemSupport, trace,
|
||||
new ProxyAccessibilityServiceConnection(mContext, info.getComponentName(), info,
|
||||
id, mMainHandler, mLock, securityPolicy, systemSupport, trace,
|
||||
windowManagerInternal,
|
||||
mA11yWindowManager, displayId, deviceId);
|
||||
|
||||
@@ -263,7 +260,7 @@ public class ProxyManager {
|
||||
* When the connection is removed from tracking in ProxyManager, propagate changes to other a11y
|
||||
* system components like the input filter and IAccessibilityManagerClients.
|
||||
*/
|
||||
public void updateStateForRemovedDisplay(int displayId, int deviceId) {
|
||||
private void updateStateForRemovedDisplay(int displayId, int deviceId) {
|
||||
mA11yWindowManager.stopTrackingDisplayProxy(displayId);
|
||||
// A11yInputFilter isn't thread-safe, so post on the system thread.
|
||||
mMainHandler.post(
|
||||
@@ -360,8 +357,9 @@ public class ProxyManager {
|
||||
/**
|
||||
* If there is at least one proxy, accessibility is enabled.
|
||||
*/
|
||||
public int getStateLocked(int deviceId, boolean automationRunning) {
|
||||
public int getStateLocked(int deviceId) {
|
||||
int clientState = 0;
|
||||
final boolean automationRunning = mUiAutomationManager.isUiAutomationRunningLocked();
|
||||
if (automationRunning) {
|
||||
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
|
||||
}
|
||||
@@ -387,7 +385,7 @@ public class ProxyManager {
|
||||
/**
|
||||
* If there is at least one proxy, accessibility is enabled.
|
||||
*/
|
||||
public int getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy) {
|
||||
private int getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy) {
|
||||
int clientState = 0;
|
||||
if (proxy != null) {
|
||||
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
|
||||
@@ -409,14 +407,14 @@ public class ProxyManager {
|
||||
/**
|
||||
* Gets the last state for a device.
|
||||
*/
|
||||
public int getLastSentStateLocked(int deviceId) {
|
||||
private int getLastSentStateLocked(int deviceId) {
|
||||
return mLastStates.get(deviceId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last state for a device.
|
||||
*/
|
||||
public void setLastStateLocked(int deviceId, int proxyState) {
|
||||
private void setLastStateLocked(int deviceId, int proxyState) {
|
||||
mLastStates.put(deviceId, proxyState);
|
||||
}
|
||||
|
||||
@@ -429,7 +427,7 @@ public class ProxyManager {
|
||||
* Virtual Device, the app clients will get the aggregated event types for all proxy-ed displays
|
||||
* belonging to a VirtualDevice.
|
||||
*/
|
||||
public void updateRelevantEventTypesLocked(int deviceId) {
|
||||
private void updateRelevantEventTypesLocked(int deviceId) {
|
||||
if (!isProxyedDeviceId(deviceId)) {
|
||||
return;
|
||||
}
|
||||
@@ -452,7 +450,7 @@ public class ProxyManager {
|
||||
/**
|
||||
* Returns the relevant event types for a Client.
|
||||
*/
|
||||
int computeRelevantEventTypesLocked(AccessibilityManagerService.Client client) {
|
||||
public int computeRelevantEventTypesLocked(AccessibilityManagerService.Client client) {
|
||||
int relevantEventTypes = 0;
|
||||
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
|
||||
final ProxyAccessibilityServiceConnection proxy =
|
||||
@@ -578,9 +576,8 @@ public class ProxyManager {
|
||||
/**
|
||||
* Updates the states of the app AccessibilityManagers.
|
||||
*/
|
||||
public void scheduleUpdateProxyClientsIfNeededLocked(int deviceId) {
|
||||
final int proxyState = getStateLocked(deviceId,
|
||||
mUiAutomationManager.isUiAutomationRunningLocked());
|
||||
private void scheduleUpdateProxyClientsIfNeededLocked(int deviceId) {
|
||||
final int proxyState = getStateLocked(deviceId);
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "State for device id " + deviceId + " is " + proxyState);
|
||||
Slog.v(LOG_TAG, "Last state for device id " + deviceId + " is "
|
||||
@@ -606,7 +603,7 @@ public class ProxyManager {
|
||||
*
|
||||
* @see AccessibilityManager.AccessibilityServicesStateChangeListener
|
||||
*/
|
||||
public void scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId) {
|
||||
private void scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId) {
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "Notify services state change at device id " + deviceId);
|
||||
}
|
||||
@@ -625,7 +622,7 @@ public class ProxyManager {
|
||||
/**
|
||||
* Updates the focus appearance of AccessibilityManagerClients.
|
||||
*/
|
||||
public void updateFocusAppearanceLocked(int deviceId) {
|
||||
private void updateFocusAppearanceLocked(int deviceId) {
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "Update proxy focus appearance at device id " + deviceId);
|
||||
}
|
||||
@@ -764,7 +761,7 @@ public class ProxyManager {
|
||||
/**
|
||||
* Sets a Client device id if the app uid belongs to the virtual device.
|
||||
*/
|
||||
public void updateDeviceIdsIfNeededLocked(int deviceId) {
|
||||
private void updateDeviceIdsIfNeededLocked(int deviceId) {
|
||||
final RemoteCallbackList<IAccessibilityManagerClient> userClients =
|
||||
mSystemSupport.getCurrentUserClientsLocked();
|
||||
final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
|
||||
@@ -777,7 +774,7 @@ public class ProxyManager {
|
||||
/**
|
||||
* Updates the device ids of IAccessibilityManagerClients if needed.
|
||||
*/
|
||||
public void updateDeviceIdsIfNeededLocked(int deviceId,
|
||||
private void updateDeviceIdsIfNeededLocked(int deviceId,
|
||||
@NonNull RemoteCallbackList<IAccessibilityManagerClient> clients) {
|
||||
final VirtualDeviceManagerInternal localVdm = getLocalVdm();
|
||||
if (localVdm == null) {
|
||||
@@ -809,14 +806,17 @@ public class ProxyManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
|
||||
/**
|
||||
* Sets the input filter for enabling and disabling features for proxy displays.
|
||||
*/
|
||||
public void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "Set proxy input filter to " + filter);
|
||||
}
|
||||
mA11yInputFilter = filter;
|
||||
}
|
||||
|
||||
VirtualDeviceManagerInternal getLocalVdm() {
|
||||
private VirtualDeviceManagerInternal getLocalVdm() {
|
||||
if (mLocalVdm == null) {
|
||||
mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
|
||||
}
|
||||
|
||||
@@ -281,8 +281,8 @@ public class AccessibilityManagerServiceTest {
|
||||
@Test
|
||||
public void testRegisterProxy() throws Exception {
|
||||
mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
|
||||
verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY),
|
||||
eq(mTestableContext), anyInt(), any(), eq(mMockSecurityPolicy),
|
||||
verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY), anyInt(),
|
||||
eq(mMockSecurityPolicy),
|
||||
eq(mA11yms), eq(mA11yms.getTraceManager()),
|
||||
eq(mMockWindowManagerService));
|
||||
}
|
||||
@@ -295,7 +295,7 @@ public class AccessibilityManagerServiceTest {
|
||||
|
||||
assertThrows(SecurityException.class,
|
||||
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
|
||||
any(), any(), any());
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ public class AccessibilityManagerServiceTest {
|
||||
|
||||
assertThrows(SecurityException.class,
|
||||
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
|
||||
any(), any(), any());
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ public class AccessibilityManagerServiceTest {
|
||||
public void testRegisterProxyForDefaultDisplay() throws Exception {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
|
||||
any(), any(), any());
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ public class AccessibilityManagerServiceTest {
|
||||
public void testRegisterProxyForInvalidDisplay() throws Exception {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY));
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
|
||||
any(), any(), any());
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,14 @@ import android.accessibilityservice.AccessibilityTrace;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -79,12 +81,15 @@ public class ProxyAccessibilityServiceConnectionTest {
|
||||
WindowManagerInternal mMockWindowManagerInternal;
|
||||
ProxyAccessibilityServiceConnection mProxyConnection;
|
||||
AccessibilityServiceInfo mAccessibilityServiceInfo;
|
||||
private int mFocusStrokeWidthDefaultValue;
|
||||
private int mFocusColorDefaultValue;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
final Resources resources = getInstrumentation().getContext().getResources();
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mMockContext.getResources()).thenReturn(resources);
|
||||
when(mMockSecurityPolicy.checkAccessibilityAccess(any())).thenReturn(true);
|
||||
|
||||
mAccessibilityServiceInfo = new AccessibilityServiceInfo();
|
||||
mProxyConnection = new ProxyAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
|
||||
@@ -92,10 +97,13 @@ public class ProxyAccessibilityServiceConnectionTest {
|
||||
getInstrumentation().getContext().getMainLooper()),
|
||||
mMockLock, mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace,
|
||||
mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID, DEVICE_ID);
|
||||
|
||||
mFocusStrokeWidthDefaultValue = mProxyConnection.getFocusStrokeWidthLocked();
|
||||
mFocusColorDefaultValue = mProxyConnection.getFocusColorLocked();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInstalledAndEnabledServices_clientChanged() {
|
||||
public void testSetInstalledAndEnabledServices_updateInfos_notifiesSystemOfProxyChange() {
|
||||
final List<AccessibilityServiceInfo> infos = new ArrayList<>();
|
||||
final AccessibilityServiceInfo info1 = new AccessibilityServiceInfo();
|
||||
infos.add(info1);
|
||||
@@ -105,6 +113,17 @@ public class ProxyAccessibilityServiceConnectionTest {
|
||||
verify(mMockSystemSupport).onProxyChanged(DEVICE_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetFocusAppearance_updateAppearance_notifiesSystemOfProxyChange() {
|
||||
final int updatedWidth = mFocusStrokeWidthDefaultValue + 10;
|
||||
final int updatedColor = mFocusColorDefaultValue
|
||||
== Color.BLUE ? Color.RED : Color.BLUE;
|
||||
|
||||
mProxyConnection.setFocusAppearance(updatedWidth, updatedColor);
|
||||
|
||||
verify(mMockSystemSupport).onProxyChanged(DEVICE_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInstalledAndEnabledServices_returnList() {
|
||||
final List<AccessibilityServiceInfo> infos = new ArrayList<>();
|
||||
@@ -196,7 +215,7 @@ public class ProxyAccessibilityServiceConnectionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetServiceInfo_setIllegalOperationExceptionThrown_() {
|
||||
public void testSetServiceInfo_setIllegalOperationExceptionThrown() {
|
||||
UnsupportedOperationException thrown =
|
||||
assertThrows(
|
||||
UnsupportedOperationException.class,
|
||||
|
||||
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.INTERACTIVE_UI_TIMEOUT_100MS;
|
||||
import static com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.INTERACTIVE_UI_TIMEOUT_200MS;
|
||||
import static com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.NON_INTERACTIVE_UI_TIMEOUT_100MS;
|
||||
import static com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.NON_INTERACTIVE_UI_TIMEOUT_200MS;
|
||||
import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_CLASS_NAME;
|
||||
import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_PACKAGE_NAME;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.accessibilityservice.AccessibilityGestureEvent;
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.accessibilityservice.AccessibilityTrace;
|
||||
import android.accessibilityservice.IAccessibilityServiceClient;
|
||||
import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||
import android.accessibilityservice.MagnificationConfig;
|
||||
import android.companion.virtual.IVirtualDeviceManager;
|
||||
import android.companion.virtual.VirtualDeviceManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Region;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArraySet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.IAccessibilityManagerClient;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
|
||||
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
|
||||
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
|
||||
import com.android.internal.util.IntPair;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.accessibility.test.MessageCapturingHandler;
|
||||
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests for ProxyManager.
|
||||
*/
|
||||
public class ProxyManagerTest {
|
||||
private static final int DISPLAY_ID = 1000;
|
||||
private static final int DISPLAY_2_ID = 1001;
|
||||
private static final int DEVICE_ID = 10;
|
||||
private static final int STREAMED_CALLING_UID = 9876;
|
||||
|
||||
@Mock private Context mMockContext;
|
||||
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
|
||||
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
|
||||
@Mock private ProxyManager.SystemSupport mMockProxySystemSupport;
|
||||
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockConnectionSystemSupport;
|
||||
@Mock private AccessibilityTrace mMockA11yTrace;
|
||||
@Mock private WindowManagerInternal mMockWindowManagerInternal;
|
||||
@Mock private IAccessibilityServiceClient mMockAccessibilityServiceClient;
|
||||
@Mock private IBinder mMockServiceAsBinder;
|
||||
@Mock private VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal;
|
||||
@Mock private IVirtualDeviceManager mMockIVirtualDeviceManager;
|
||||
|
||||
private int mFocusStrokeWidthDefaultValue;
|
||||
private int mFocusColorDefaultValue;
|
||||
|
||||
private MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
|
||||
private ProxyManager mProxyManager;
|
||||
|
||||
@Before
|
||||
public void setup() throws RemoteException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
final Resources resources = InstrumentationRegistry.getContext().getResources();
|
||||
|
||||
mFocusStrokeWidthDefaultValue =
|
||||
resources.getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width);
|
||||
mFocusColorDefaultValue = resources.getColor(R.color.accessibility_focus_highlight_color);
|
||||
when(mMockContext.getResources()).thenReturn(resources);
|
||||
|
||||
when(mMockVirtualDeviceManagerInternal.getDeviceIdsForUid(anyInt())).thenReturn(
|
||||
new ArraySet(Set.of(DEVICE_ID)));
|
||||
LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
|
||||
LocalServices.addService(VirtualDeviceManagerInternal.class,
|
||||
mMockVirtualDeviceManagerInternal);
|
||||
|
||||
when(mMockIVirtualDeviceManager.getDeviceIdForDisplayId(anyInt())).thenReturn(DEVICE_ID);
|
||||
final VirtualDeviceManager virtualDeviceManager =
|
||||
new VirtualDeviceManager(mMockIVirtualDeviceManager, mMockContext);
|
||||
when(mMockContext.getSystemServiceName(VirtualDeviceManager.class)).thenReturn(
|
||||
Context.VIRTUAL_DEVICE_SERVICE);
|
||||
when(mMockContext.getSystemService(VirtualDeviceManager.class))
|
||||
.thenReturn(virtualDeviceManager);
|
||||
|
||||
when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
|
||||
|
||||
final RemoteCallbackList<IAccessibilityManagerClient> userClients =
|
||||
new RemoteCallbackList<>();
|
||||
final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
|
||||
new RemoteCallbackList<>();
|
||||
when(mMockProxySystemSupport.getCurrentUserClientsLocked()).thenReturn(userClients);
|
||||
when(mMockProxySystemSupport.getGlobalClientsLocked()).thenReturn(globalClients);
|
||||
|
||||
when(mMockAccessibilityServiceClient.asBinder()).thenReturn(mMockServiceAsBinder);
|
||||
|
||||
mProxyManager = new ProxyManager(new Object(), mMockA11yWindowManager, mMockContext,
|
||||
mMessageCapturingHandler, new UiAutomationManager(new Object()),
|
||||
mMockProxySystemSupport);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mMessageCapturingHandler.removeAllMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the proxy’s backing AccessibilityServiceClient is initialized when registering a
|
||||
* proxy.
|
||||
*/
|
||||
@Test
|
||||
public void registerProxy_always_connectsServiceClient() throws RemoteException {
|
||||
registerProxy(DISPLAY_ID);
|
||||
verify(mMockAccessibilityServiceClient).init(any(), anyInt(), any());
|
||||
}
|
||||
|
||||
/** Tests that unregistering a proxy removes its display from tracking. */
|
||||
@Test
|
||||
public void unregisterProxy_always_stopsTrackingDisplay() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
|
||||
mProxyManager.unregisterProxy(DISPLAY_ID);
|
||||
|
||||
verify(mMockA11yWindowManager).stopTrackingDisplayProxy(DISPLAY_ID);
|
||||
assertThat(mProxyManager.isProxyedDisplay(DISPLAY_ID)).isFalse();
|
||||
}
|
||||
/**
|
||||
* Tests that unregistering a proxied display of a virtual device, where that virtual device
|
||||
* owned only that one proxied display, removes the device from tracking.
|
||||
*/
|
||||
@Test
|
||||
public void unregisterProxy_deviceAssociatedWithSingleDisplay_stopsTrackingDevice() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
|
||||
mProxyManager.unregisterProxy(DISPLAY_ID);
|
||||
|
||||
assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isFalse();
|
||||
verify(mMockProxySystemSupport).removeDeviceIdLocked(DEVICE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that unregistering a proxied display of a virtual device, where that virtual device
|
||||
* owns more than one proxied display, does not remove the device from tracking.
|
||||
*/
|
||||
@Test
|
||||
public void unregisterProxy_deviceAssociatedWithMultipleDisplays_tracksRemainingProxy() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
registerProxy(DISPLAY_2_ID);
|
||||
|
||||
mProxyManager.unregisterProxy(DISPLAY_ID);
|
||||
|
||||
assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isTrue();
|
||||
verify(mMockProxySystemSupport, never()).removeDeviceIdLocked(DEVICE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that changing a proxy, e.g. registering/unregistering a proxy or updating its service
|
||||
* info, notifies the apps being streamed and AccessibilityManagerService.
|
||||
*/
|
||||
@Test
|
||||
public void testOnProxyChanged_always_propagatesChange() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
mMessageCapturingHandler.sendAllMessages();
|
||||
|
||||
mProxyManager.onProxyChanged(DEVICE_ID);
|
||||
|
||||
// Messages to notify IAccessibilityManagerClients should be posted.
|
||||
assertThat(mMessageCapturingHandler.hasMessages()).isTrue();
|
||||
|
||||
verify(mMockProxySystemSupport).updateWindowsForAccessibilityCallbackLocked();
|
||||
verify(mMockProxySystemSupport).notifyClearAccessibilityCacheLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getting the first device id for an app uid, such as when an app queries for
|
||||
* device-specific state, returns the right device id.
|
||||
*/
|
||||
@Test
|
||||
public void testGetFirstDeviceForUid_streamedAppQueriesState_getsHostDeviceId() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
assertThat(mProxyManager.getFirstDeviceIdForUidLocked(STREAMED_CALLING_UID))
|
||||
.isEqualTo(DEVICE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the app client state retrieved for a device reflects that touch exploration is
|
||||
* enabled since a proxy info has requested touch exploration.
|
||||
*/
|
||||
@Test
|
||||
public void testGetClientState_proxyWantsTouchExploration_returnsTouchExplorationEnabled() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
|
||||
final AccessibilityServiceInfo secondDisplayInfo = new AccessibilityServiceInfo();
|
||||
secondDisplayInfo.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
|
||||
AccessibilityServiceClientImpl client = new AccessibilityServiceClientImpl(
|
||||
secondDisplayInfo);
|
||||
registerProxy(DISPLAY_2_ID, client);
|
||||
|
||||
final int deviceClientState = mProxyManager.getStateLocked(DEVICE_ID);
|
||||
assertThat((deviceClientState
|
||||
& AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the highest interactive and non-interactive timeout is returned if there are
|
||||
* multiple proxied displays belonging to a device.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRecommendedTimeout_multipleProxies_returnsHighestTimeout() {
|
||||
final AccessibilityServiceInfo firstDisplayInfo = new AccessibilityServiceInfo();
|
||||
firstDisplayInfo.setInteractiveUiTimeoutMillis(INTERACTIVE_UI_TIMEOUT_100MS);
|
||||
firstDisplayInfo.setNonInteractiveUiTimeoutMillis(NON_INTERACTIVE_UI_TIMEOUT_200MS);
|
||||
|
||||
final AccessibilityServiceInfo secondDisplayInfo = new AccessibilityServiceInfo();
|
||||
secondDisplayInfo.setInteractiveUiTimeoutMillis(INTERACTIVE_UI_TIMEOUT_200MS);
|
||||
secondDisplayInfo.setNonInteractiveUiTimeoutMillis(NON_INTERACTIVE_UI_TIMEOUT_100MS);
|
||||
|
||||
registerProxy(DISPLAY_ID, new AccessibilityServiceClientImpl(firstDisplayInfo));
|
||||
registerProxy(DISPLAY_2_ID, new AccessibilityServiceClientImpl(secondDisplayInfo));
|
||||
|
||||
final long timeout = mProxyManager.getRecommendedTimeoutMillisLocked(DEVICE_ID);
|
||||
final int interactiveTimeout = IntPair.first(timeout);
|
||||
final int nonInteractiveTimeout = IntPair.second(timeout);
|
||||
|
||||
assertThat(interactiveTimeout).isEqualTo(INTERACTIVE_UI_TIMEOUT_200MS);
|
||||
assertThat(nonInteractiveTimeout).isEqualTo(NON_INTERACTIVE_UI_TIMEOUT_200MS);
|
||||
}
|
||||
/**
|
||||
* Tests that getting the installed and enabled services returns the info of the registered
|
||||
* proxy. (The component name reflects the display id.)
|
||||
*/
|
||||
@Test
|
||||
public void testGetInstalledAndEnabledServices_defaultInfo_returnsInfoForDisplayId() {
|
||||
final AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
||||
registerProxy(DISPLAY_ID, new AccessibilityServiceClientImpl(info));
|
||||
final List<AccessibilityServiceInfo> installedAndEnabledServices =
|
||||
mProxyManager.getInstalledAndEnabledServiceInfosLocked(
|
||||
AccessibilityServiceInfo.FEEDBACK_ALL_MASK, DEVICE_ID);
|
||||
assertThat(installedAndEnabledServices.size()).isEqualTo(1);
|
||||
AccessibilityServiceInfo proxyInfo = installedAndEnabledServices.get(0);
|
||||
|
||||
assertThat(proxyInfo.getComponentName()).isEqualTo(new ComponentName(
|
||||
PROXY_COMPONENT_PACKAGE_NAME, PROXY_COMPONENT_CLASS_NAME + DISPLAY_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the app client state retrieved for a device reflects that accessibility is
|
||||
* enabled.
|
||||
*/
|
||||
@Test
|
||||
public void testGetClientState_always_returnsAccessibilityEnabled() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
|
||||
final int deviceClientState = mProxyManager.getStateLocked(DEVICE_ID);
|
||||
assertThat((deviceClientState
|
||||
& AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the manager can retrieve interactive windows if a proxy sets
|
||||
* AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS.
|
||||
*/
|
||||
@Test
|
||||
public void testCanRetrieveInteractiveWindows_atLeastOneProxyWantsWindows_returnsTrue() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
|
||||
final AccessibilityServiceInfo secondDisplayInfo = new AccessibilityServiceInfo();
|
||||
secondDisplayInfo.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
|
||||
registerProxy(DISPLAY_2_ID, new AccessibilityServiceClientImpl(secondDisplayInfo));
|
||||
|
||||
assertThat(mProxyManager.canRetrieveInteractiveWindowsLocked()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getting service interfaces to interrupt when AccessibilityManager#interrupt
|
||||
* returns the registered proxy interface.
|
||||
*/
|
||||
@Test
|
||||
public void testGetServiceInterfacesForInterrupt_defaultProxy_returnsProxyInterface() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
final List<IAccessibilityServiceClient> interfacesToInterrupt = new ArrayList<>();
|
||||
mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt, DEVICE_ID);
|
||||
|
||||
assertThat(interfacesToInterrupt.size()).isEqualTo(1);
|
||||
assertThat(interfacesToInterrupt.get(0).asBinder()).isEqualTo(mMockServiceAsBinder);
|
||||
}
|
||||
|
||||
/** Tests that the default timeout (0) is returned when the proxy is registered. */
|
||||
@Test
|
||||
public void getRecommendedTimeout_defaultProxyInfo_getsDefaultTimeout() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
final long timeout = mProxyManager.getRecommendedTimeoutMillisLocked(DEVICE_ID);
|
||||
final int interactiveTimeout = IntPair.first(timeout);
|
||||
final int nonInteractiveTimeout = IntPair.second(timeout);
|
||||
|
||||
assertThat(interactiveTimeout).isEqualTo(0);
|
||||
assertThat(nonInteractiveTimeout).isEqualTo(0);
|
||||
}
|
||||
|
||||
/** Tests that the manager returns the updated timeout when the proxy’s timeout is updated. */
|
||||
@Test
|
||||
public void getRecommendedTimeout_updateTimeout_getsUpdatedTimeout() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
|
||||
mProxyManager.updateTimeoutsIfNeeded(NON_INTERACTIVE_UI_TIMEOUT_100MS,
|
||||
INTERACTIVE_UI_TIMEOUT_200MS);
|
||||
|
||||
final long updatedTimeout = mProxyManager.getRecommendedTimeoutMillisLocked(DEVICE_ID);
|
||||
final int updatedInteractiveTimeout = IntPair.first(updatedTimeout);
|
||||
final int updatedNonInteractiveTimeout = IntPair.second(updatedTimeout);
|
||||
|
||||
assertThat(updatedInteractiveTimeout).isEqualTo(INTERACTIVE_UI_TIMEOUT_200MS);
|
||||
assertThat(updatedNonInteractiveTimeout).isEqualTo(NON_INTERACTIVE_UI_TIMEOUT_100MS);
|
||||
}
|
||||
|
||||
/** Tests that the system’s default focus color is returned. */
|
||||
@Test
|
||||
public void testGetFocusColor_defaultProxy_getsDefaultSystemColor() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
final int focusColor = mProxyManager.getFocusColorLocked(DEVICE_ID);
|
||||
assertThat(focusColor).isEqualTo(mFocusColorDefaultValue);
|
||||
}
|
||||
|
||||
/** Tests that the system’s default focus stroke width is returned. */
|
||||
@Test
|
||||
public void testGetFocusStrokeWidth_defaultProxy_getsDefaultSystemWidth() {
|
||||
registerProxy(DISPLAY_ID);
|
||||
final int focusStrokeWidth = mProxyManager.getFocusStrokeWidthLocked(DEVICE_ID);
|
||||
assertThat(focusStrokeWidth).isEqualTo(mFocusStrokeWidthDefaultValue);
|
||||
}
|
||||
|
||||
private void registerProxy(int displayId) {
|
||||
try {
|
||||
mProxyManager.registerProxy(mMockAccessibilityServiceClient, displayId, anyInt(),
|
||||
mMockSecurityPolicy, mMockConnectionSystemSupport,
|
||||
mMockA11yTrace, mMockWindowManagerInternal);
|
||||
} catch (RemoteException e) {
|
||||
fail("Failed to register proxy " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerProxy(int displayId, AccessibilityServiceClientImpl serviceClient) {
|
||||
try {
|
||||
mProxyManager.registerProxy(serviceClient, displayId, anyInt(),
|
||||
mMockSecurityPolicy, mMockConnectionSystemSupport,
|
||||
mMockA11yTrace, mMockWindowManagerInternal);
|
||||
} catch (RemoteException e) {
|
||||
fail("Failed to register proxy " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IAccessibilityServiceClient implementation.
|
||||
* A proxy connection does not populate non-default AccessibilityServiceInfo values until the
|
||||
* proxy is connected in A11yDisplayProxy#onServiceConnected. For tests that check for
|
||||
* non-default values, populate immediately in this testing class, since a real Service is not
|
||||
* being used and connected.
|
||||
*/
|
||||
static class AccessibilityServiceClientImpl extends IAccessibilityServiceClient.Stub {
|
||||
List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
|
||||
|
||||
AccessibilityServiceClientImpl(AccessibilityServiceInfo
|
||||
installedAndEnabledService) {
|
||||
mInstalledAndEnabledServices = List.of(installedAndEnabledService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(IAccessibilityServiceConnection connection, int connectionId,
|
||||
IBinder windowToken) throws RemoteException {
|
||||
connection.setInstalledAndEnabledServices(mInstalledAndEnabledServices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent)
|
||||
throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt() throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGesture(AccessibilityGestureEvent gestureEvent) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAccessibilityCache() throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyEvent(KeyEvent event, int sequence) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMagnificationChanged(int displayId, Region region, MagnificationConfig config)
|
||||
throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent event) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchStateChanged(int displayId, int state) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSoftKeyboardShowModeChanged(int showMode) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerformGestureResult(int sequence, boolean completedSuccessfully)
|
||||
throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFingerprintCapturingGesturesChanged(boolean capturing)
|
||||
throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFingerprintGesture(int gesture) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityButtonClicked(int displayId) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityButtonAvailabilityChanged(boolean available)
|
||||
throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemActionsChanged() throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createImeSession(IAccessibilityInputMethodSessionCallback callback)
|
||||
throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImeSessionEnabled(IAccessibilityInputMethodSession session, boolean enabled)
|
||||
throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindInput() throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindInput() throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startInput(IRemoteAccessibilityInputConnection connection,
|
||||
EditorInfo editorInfo, boolean restarting) throws RemoteException {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user