Merge "Expose more SystemAPI methods on AccessibilityDisplayProxy"

This commit is contained in:
Sally Yuen
2022-11-29 16:36:27 +00:00
committed by Android (Google) Code Review
9 changed files with 443 additions and 51 deletions

View File

@@ -16120,6 +16120,12 @@ package android.view.accessibility {
public abstract class AccessibilityDisplayProxy {
ctor public AccessibilityDisplayProxy(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.accessibilityservice.AccessibilityServiceInfo>);
method public int getDisplayId();
method @NonNull public final java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAndEnabledServices();
method @NonNull public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public void interrupt();
method public void onAccessibilityEvent(@NonNull android.view.accessibility.AccessibilityEvent);
method public void onProxyConnected();
method public void setInstalledAndEnabledServices(@NonNull java.util.List<android.accessibilityservice.AccessibilityServiceInfo>);
}
public final class AccessibilityManager {

View File

@@ -20,13 +20,18 @@ import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
@@ -34,14 +39,15 @@ import android.view.inputmethod.EditorInfo;
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Allows a privileged app - an app with MANAGE_ACCESSIBILITY permission and SystemAPI access - to
* interact with the windows in the display that this proxy represents. Proxying the default display
* or a display that is not tracked will throw an exception. Only the real user has access to global
* clients like SystemUI.
* or a display that is not tracked by accessibility, such as private displays, will throw an
* exception. Only the real user has access to global clients like SystemUI.
*
* <p>
* To register and unregister a proxy, use
@@ -49,7 +55,16 @@ import java.util.concurrent.Executor;
* and {@link AccessibilityManager#unregisterDisplayProxy(AccessibilityDisplayProxy)}. If the app
* that has registered the proxy dies, the system will remove the proxy.
*
* TODO(241429275): Complete proxy impl and add additional support (if necessary) like cache methods
* <p>
* Avoid using the app's main thread. Proxy methods such as {@link #getWindows} and node methods
* like {@link AccessibilityNodeInfo#getChild(int)} will happen frequently. Node methods may also
* wait on the displayed app's UI thread to obtain accurate screen data.
*
* <p>
* To get a list of {@link AccessibilityServiceInfo}s that have populated {@link ComponentName}s and
* {@link ResolveInfo}s, retrieve the list using {@link #getInstalledAndEnabledServices()} after
* {@link #onProxyConnected()} has been called.
*
* @hide
*/
@SystemApi
@@ -91,7 +106,134 @@ public abstract class AccessibilityDisplayProxy {
}
/**
* An IAccessibilityServiceClient that handles interrupts and accessibility events.
* Handles {@link android.view.accessibility.AccessibilityEvent}s.
* <p>
* AccessibilityEvents represent changes to the UI, or what parts of the node tree have changed.
* AccessibilityDisplayProxy should use these to query new UI and send appropriate feedback
* to their users.
* <p>
* For example, a {@link AccessibilityEvent#TYPE_WINDOWS_CHANGED} indicates a change in windows,
* so a proxy may query {@link #getWindows} to obtain updated UI and potentially inform of a new
* window title. Or a proxy may emit an earcon on a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
public void onAccessibilityEvent(@NonNull AccessibilityEvent event) {
// Default no-op
}
/**
* Handles a successful system connection after
* {@link AccessibilityManager#registerDisplayProxy(AccessibilityDisplayProxy)} is called.
*
* <p>
* At this point, querying for UI is available and {@link AccessibilityEvent}s will begin being
* sent. An AccessibilityDisplayProxy may instantiate core infrastructure components here.
*/
public void onProxyConnected() {
// Default no-op
}
/**
* Handles a request to interrupt the accessibility feedback.
* <p>
* AccessibilityDisplayProxy should interrupt the accessibility activity occurring on its
* display. For example, a screen reader may interrupt speech.
*
* @see AccessibilityManager#interrupt()
* @see AccessibilityService#onInterrupt()
*/
public void interrupt() {
// Default no-op
}
/**
* Gets the focus of the window specified by {@code windowInfo}.
*
* @param windowInfo the window to search
* @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
* {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
* @return The node info of the focused view or null.
* @hide
* TODO(254545943): Do not expose until support for accessibility focus and/or input is in place
*/
@Nullable
public AccessibilityNodeInfo findFocus(@NonNull AccessibilityWindowInfo windowInfo, int focus) {
AccessibilityNodeInfo windowRoot = windowInfo.getRoot();
return windowRoot != null ? windowRoot.findFocus(focus) : null;
}
/**
* Gets the windows of the tracked display.
*
* @see AccessibilityService#getWindows()
*/
@NonNull
public List<AccessibilityWindowInfo> getWindows() {
return AccessibilityInteractionClient.getInstance().getWindowsOnDisplay(mConnectionId,
mDisplayId);
}
/**
* Sets the list of {@link AccessibilityServiceInfo}s describing the services interested in the
* {@link AccessibilityDisplayProxy}'s display.
*
* <p>These represent a11y features and services that are installed and running. These should
* not include {@link AccessibilityService}s installed on the phone.
*
* @param installedAndEnabledServices the list of installed and running a11y services.
*/
public void setInstalledAndEnabledServices(
@NonNull List<AccessibilityServiceInfo> installedAndEnabledServices) {
mInstalledAndEnabledServices = installedAndEnabledServices;
sendServiceInfos();
}
/**
* Sets the {@link AccessibilityServiceInfo} for this service if the latter is
* properly set and there is an {@link IAccessibilityServiceConnection} to the
* AccessibilityManagerService.
*/
private void sendServiceInfos() {
IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
if (mInstalledAndEnabledServices != null && mInstalledAndEnabledServices.size() > 0
&& connection != null) {
try {
connection.setInstalledAndEnabledServices(mInstalledAndEnabledServices);
AccessibilityInteractionClient.getInstance().clearCache(mConnectionId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfos", re);
re.rethrowFromSystemServer();
}
}
mInstalledAndEnabledServices = null;
}
/**
* Gets the list of {@link AccessibilityServiceInfo}s describing the services interested in the
* {@link AccessibilityDisplayProxy}'s display.
*
* @return The {@link AccessibilityServiceInfo}s of interested services.
* @see AccessibilityServiceInfo
*/
@NonNull
public final List<AccessibilityServiceInfo> getInstalledAndEnabledServices() {
IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
if (connection != null) {
try {
return connection.getInstalledAndEnabledServices();
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
re.rethrowFromSystemServer();
}
}
return Collections.emptyList();
}
/**
* An IAccessibilityServiceClient that handles interrupts, accessibility events, and system
* connection.
*/
private class IAccessibilityServiceClientImpl extends
AccessibilityService.IAccessibilityServiceClientWrapper {
@@ -100,17 +242,24 @@ public abstract class AccessibilityDisplayProxy {
super(context, executor, new AccessibilityService.Callbacks() {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
// TODO: call AccessiiblityProxy.onAccessibilityEvent
// TODO(254545943): Remove check when event processing is done more upstream in
// AccessibilityManagerService.
if (event.getDisplayId() == mDisplayId) {
AccessibilityDisplayProxy.this.onAccessibilityEvent(event);
}
}
@Override
public void onInterrupt() {
// TODO: call AccessiiblityProxy.onInterrupt
AccessibilityDisplayProxy.this.interrupt();
}
@Override
public void onServiceConnected() {
// TODO: send service infos and call AccessiiblityProxy.onProxyConnected
AccessibilityDisplayProxy.this.sendServiceInfos();
AccessibilityDisplayProxy.this.onProxyConnected();
}
@Override
public void init(int connectionId, IBinder windowToken) {
mConnectionId = connectionId;

View File

@@ -458,14 +458,20 @@ public final class AccessibilityInteractionClient
* @return The {@link AccessibilityWindowInfo} list.
*/
public List<AccessibilityWindowInfo> getWindows(int connectionId) {
final SparseArray<List<AccessibilityWindowInfo>> windows =
getWindowsOnAllDisplays(connectionId);
if (windows.size() > 0) {
return windows.valueAt(Display.DEFAULT_DISPLAY);
}
return Collections.emptyList();
return getWindowsOnDisplay(connectionId, Display.DEFAULT_DISPLAY);
}
/**
* Gets the info for all windows of the specified display.
*
* @param connectionId The id of a connection for interacting with the system.
* @return The {@link AccessibilityWindowInfo} list belonging to {@code displayId}.
*/
public List<AccessibilityWindowInfo> getWindowsOnDisplay(int connectionId, int displayId) {
final SparseArray<List<AccessibilityWindowInfo>> windows =
getWindowsOnAllDisplays(connectionId);
return windows.get(displayId, Collections.emptyList());
}
/**
* Gets the info for all windows of all displays.
*

View File

@@ -263,10 +263,24 @@ public class AccessibilityManagerTest {
}
private class MyAccessibilityProxy extends AccessibilityDisplayProxy {
// TODO(241429275): Will override A11yProxy methods in the future.
MyAccessibilityProxy(int displayId,
@NonNull List<AccessibilityServiceInfo> serviceInfos) {
super(displayId, Executors.newSingleThreadExecutor(), serviceInfos);
}
@Override
public void onAccessibilityEvent(@NonNull AccessibilityEvent event) {
}
@Override
public void onProxyConnected() {
}
@Override
public void interrupt() {
}
}
}

View File

@@ -860,7 +860,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
}
return IntPair.of(
getClientStateLocked(userState),
combineUserStateAndProxyState(getClientStateLocked(userState),
mProxyManager.getStateLocked()),
client.mLastSentRelevantEventTypes);
} else {
userState.mUserClients.register(callback, client);
@@ -872,7 +873,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
+ " and userId:" + mCurrentUserId);
}
return IntPair.of(
(resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0,
(resolvedUserId == mCurrentUserId) ? combineUserStateAndProxyState(
getClientStateLocked(userState), mProxyManager.getStateLocked())
: 0,
client.mLastSentRelevantEventTypes);
}
}
@@ -1003,6 +1006,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
notifyAccessibilityServicesDelayedLocked(event, false);
notifyAccessibilityServicesDelayedLocked(event, true);
mUiAutomationManager.sendAccessibilityEventLocked(event);
mProxyManager.sendAccessibilityEvent(event);
}
private void sendAccessibilityEventToInputFilter(AccessibilityEvent event) {
@@ -1143,9 +1147,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
List<AccessibilityServiceConnection> services =
getUserStateLocked(resolvedUserId).mBoundServices;
int numServices = services.size();
int numServices = services.size() + mProxyManager.getNumProxys();
interfacesToInterrupt = new ArrayList<>(numServices);
for (int i = 0; i < numServices; i++) {
for (int i = 0; i < services.size(); i++) {
AccessibilityServiceConnection service = services.get(i);
IBinder a11yServiceBinder = service.mService;
IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
@@ -1153,6 +1157,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
interfacesToInterrupt.add(a11yServiceInterface);
}
}
mProxyManager.addServiceInterfaces(interfacesToInterrupt);
}
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try {
@@ -1941,6 +1946,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mUiAutomationManager.getServiceInfo(), client)
? mUiAutomationManager.getRelevantEventTypes()
: 0;
relevantEventTypes |= mProxyManager.getRelevantEventTypes();
return relevantEventTypes;
}
@@ -2178,21 +2184,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
updateAccessibilityEnabledSettingLocked(userState);
}
void scheduleUpdateClientsIfNeeded(AccessibilityUserState userState) {
synchronized (mLock) {
scheduleUpdateClientsIfNeededLocked(userState);
}
private int combineUserStateAndProxyState(int userState, int proxyState) {
return userState | proxyState;
}
void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
final int clientState = getClientStateLocked(userState);
if (userState.getLastSentClientStateLocked() != clientState
final int proxyState = mProxyManager.getStateLocked();
if ((userState.getLastSentClientStateLocked() != clientState
|| mProxyManager.getLastSentStateLocked() != proxyState)
&& (mGlobalClients.getRegisteredCallbackCount() > 0
|| userState.mUserClients.getRegisteredCallbackCount() > 0)) {
|| userState.mUserClients.getRegisteredCallbackCount() > 0)) {
userState.setLastSentClientStateLocked(clientState);
mProxyManager.setLastStateLocked(proxyState);
// Send both the user and proxy state to the app for now.
// TODO(b/250929565): Send proxy state to proxy clients
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendStateToAllClients,
this, clientState, userState.mUserId));
this, combineUserStateAndProxyState(clientState, proxyState),
userState.mUserId));
}
}
@@ -2434,7 +2444,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// binding we do an update pass after each bind event, so we run this
// code and register the callback if needed.
boolean observingWindows = mUiAutomationManager.canRetrieveInteractiveWindowsLocked();
boolean observingWindows = mUiAutomationManager.canRetrieveInteractiveWindowsLocked()
|| mProxyManager.canRetrieveInteractiveWindowsLocked();
List<AccessibilityServiceConnection> boundServices = userState.mBoundServices;
final int boundServiceCount = boundServices.size();
for (int i = 0; !observingWindows && (i < boundServiceCount); i++) {
@@ -3657,7 +3668,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
+ "proxy-ed");
}
mProxyManager.registerProxy(client, displayId);
mProxyManager.registerProxy(client, displayId, mContext,
sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(),
mWindowManagerService, mA11yWindowManager);
return true;
}

View File

@@ -696,7 +696,7 @@ public class AccessibilitySecurityPolicy {
final ResolveInfo resolveInfo = service.getServiceInfo().getResolveInfo();
if (resolveInfo == null) {
// For InteractionBridge and UiAutomation
// For InteractionBridge, UiAutomation, and Proxy.
return true;
}

View File

@@ -16,8 +16,12 @@
package com.android.server.accessibility;
import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_CLASS_NAME;
import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_PACKAGE_NAME;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.content.ComponentName;
@@ -31,6 +35,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityDisplayProxy;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -53,10 +58,6 @@ import java.util.Set;
* TODO(241429275): Initialize this when a proxy is registered.
*/
public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection {
// Names used to populate ComponentName and ResolveInfo
private static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage";
private static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass";
private int mDisplayId;
private List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
@@ -75,6 +76,16 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
mDisplayId = displayId;
}
/**
* Called when the proxy is registered.
*/
void initializeServiceInterface(IAccessibilityServiceClient serviceInterface)
throws RemoteException {
mServiceInterface = serviceInterface;
mService = serviceInterface.asBinder();
mServiceInterface.init(this, mId, this.mOverlayWindowTokens.get(mDisplayId));
}
/**
* Keeps mAccessibilityServiceInfo in sync with the proxy's list of AccessibilityServiceInfos.
*
@@ -89,7 +100,7 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
synchronized (mLock) {
mInstalledAndEnabledServices = infos;
final AccessibilityServiceInfo proxyInfo = mAccessibilityServiceInfo;
// Reset values.
// Reset values. mAccessibilityServiceInfo is not completely reset since it is final
proxyInfo.flags = 0;
proxyInfo.eventTypes = 0;
proxyInfo.notificationTimeout = 0;

View File

@@ -14,9 +14,21 @@
* limitations under the License.
*/
package com.android.server.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import java.util.HashSet;
import com.android.server.wm.WindowManagerInternal;
import java.util.List;
/**
* Manages proxy connections.
@@ -27,32 +39,185 @@ import java.util.HashSet;
* TODO(241117292): Remove or cut down during simultaneous user refactoring.
*/
public class ProxyManager {
// Names used to populate ComponentName and ResolveInfo in connection.mA11yServiceInfo and in
// the infos of connection.setInstalledAndEnabledServices
static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage";
static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass";
private final Object mLock;
private final HashSet<Integer> mDisplayIds = new HashSet<>();
// Used to determine if we should notify AccessibilityManager clients of updates.
// TODO(254545943): Separate this so each display id has its own state. Currently there is no
// way to identify from AccessibilityManager which proxy state should be returned.
private int mLastState = -1;
private SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections =
new SparseArray<>();
ProxyManager(Object lock) {
mLock = lock;
}
/**
* TODO: Create the proxy service connection.
* Creates the service connection.
*/
public void registerProxy(IAccessibilityServiceClient client, int displayId) {
mDisplayIds.add(displayId);
public void registerProxy(IAccessibilityServiceClient client, int displayId,
Context context,
int id, Handler mainHandler,
AccessibilitySecurityPolicy securityPolicy,
AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
AccessibilityTrace trace,
WindowManagerInternal windowManagerInternal,
AccessibilityWindowManager awm) throws RemoteException {
// Set a default AccessibilityServiceInfo that is used before the proxy's info is
// populated. A proxy has the touch exploration and window capabilities.
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
final String componentClassDisplayName = PROXY_COMPONENT_CLASS_NAME + displayId;
info.setComponentName(new ComponentName(PROXY_COMPONENT_PACKAGE_NAME,
componentClassDisplayName));
ProxyAccessibilityServiceConnection connection =
new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info,
id, mainHandler, mLock, securityPolicy, systemSupport, trace,
windowManagerInternal,
awm, displayId);
mProxyA11yServiceConnections.put(displayId, connection);
// If the client dies, make sure to remove the connection.
IBinder.DeathRecipient deathRecipient =
new IBinder.DeathRecipient() {
@Override
public void binderDied() {
client.asBinder().unlinkToDeath(this, 0);
clearConnection(displayId);
}
};
client.asBinder().linkToDeath(deathRecipient, 0);
// Notify apps that the service state has changed.
// A11yManager#A11yServicesStateChangeListener
connection.mSystemSupport.onClientChangeLocked(true);
connection.initializeServiceInterface(client);
}
/**
* TODO: Unregister the proxy service connection based on display id.
* Unregister the proxy based on display id.
*/
public boolean unregisterProxy(int displayId) {
mDisplayIds.remove(displayId);
return true;
return clearConnection(displayId);
}
private boolean clearConnection(int displayId) {
if (mProxyA11yServiceConnections.contains(displayId)) {
mProxyA11yServiceConnections.remove(displayId);
return true;
}
return false;
}
/**
* Checks if a display id is being proxy-ed.
*/
public boolean isProxyed(int displayId) {
return mDisplayIds.contains(displayId);
return mProxyA11yServiceConnections.contains(displayId);
}
}
/**
* Sends AccessibilityEvents to all proxies.
* {@link android.view.accessibility.AccessibilityDisplayProxy} will filter based on display.
* TODO(b/250929565): Filtering should happen in the system, not in the proxy.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
proxy.notifyAccessibilityEvent(event);
}
}
/**
* Returns {@code true} if any proxy can retrieve windows.
* TODO(b/250929565): Retrieve per connection/user state.
*/
public boolean canRetrieveInteractiveWindowsLocked() {
boolean observingWindows = false;
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy.mRetrieveInteractiveWindows) {
observingWindows = true;
break;
}
}
return observingWindows;
}
/**
* If there is at least one proxy, accessibility is enabled.
*/
public int getStateLocked() {
int clientState = 0;
final boolean a11yEnabled = mProxyA11yServiceConnections.size() > 0;
if (a11yEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
}
return clientState;
// TODO(b/254545943): When A11yManager is separated, include support for other properties
// like isTouchExplorationEnabled.
}
/**
* Gets the last state.
*/
public int getLastSentStateLocked() {
return mLastState;
}
/**
* Sets the last state.
*/
public void setLastStateLocked(int proxyState) {
mLastState = proxyState;
}
/**
* Returns the relevant event types of every proxy.
* TODO(254545943): When A11yManager is separated, return based on the A11yManager display.
*/
public int getRelevantEventTypes() {
int relevantEventTypes = 0;
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
relevantEventTypes |= proxy.getRelevantEventTypes();
}
return relevantEventTypes;
}
/**
* Gets the number of current proxy connections.
* @return
*/
public int getNumProxys() {
return mProxyA11yServiceConnections.size();
}
/**
* Adds the service interfaces to a list.
* @param interfaces
*/
public void addServiceInterfaces(List<IAccessibilityServiceClient> interfaces) {
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
final IBinder proxyBinder = proxy.mService;
final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface;
if ((proxyBinder != null) && (proxyInterface != null)) {
interfaces.add(proxyInterface);
}
}
}
}

View File

@@ -58,7 +58,6 @@ import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityWindowAttributes;
import androidx.test.InstrumentationRegistry;
@@ -106,8 +105,6 @@ public class AccessibilityManagerServiceTest {
LABEL,
DESCRIPTION,
TEST_PENDING_INTENT);
private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
new AccessibilityAction(ACTION_ID, LABEL);
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
@@ -282,10 +279,12 @@ public class AccessibilityManagerServiceTest {
@Test
public void testRegisterProxy() throws Exception {
mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
verify(mProxyManager).registerProxy(mMockServiceClient, TEST_DISPLAY);
verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY),
eq(mTestableContext), anyInt(), any(), eq(mMockSecurityPolicy),
eq(mA11yms), eq(mA11yms.getTraceManager()),
eq(mMockWindowManagerService), eq(mMockA11yWindowManager));
}
@SmallTest
@Test
public void testRegisterProxyWithoutPermission() throws Exception {
@@ -296,7 +295,8 @@ public class AccessibilityManagerServiceTest {
Assert.fail();
} catch (SecurityException expected) {
}
verify(mProxyManager, never()).registerProxy(mMockServiceClient, TEST_DISPLAY);
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
any(), any(), any(), any());
}
@SmallTest
@@ -307,7 +307,8 @@ public class AccessibilityManagerServiceTest {
Assert.fail();
} catch (IllegalArgumentException expected) {
}
verify(mProxyManager, never()).registerProxy(mMockServiceClient, Display.DEFAULT_DISPLAY);
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
any(), any(), any(), any());
}
@SmallTest
@@ -318,7 +319,30 @@ public class AccessibilityManagerServiceTest {
Assert.fail();
} catch (IllegalArgumentException expected) {
}
verify(mProxyManager, never()).registerProxy(mMockServiceClient, Display.INVALID_DISPLAY);
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
any(), any(), any(), any());
}
@SmallTest
@Test
public void testUnRegisterProxyWithPermission() throws Exception {
mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
mA11yms.unregisterProxyForDisplay(TEST_DISPLAY);
verify(mProxyManager).unregisterProxy(TEST_DISPLAY);
}
@SmallTest
@Test
public void testUnRegisterProxyWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.unregisterProxyForDisplay(TEST_DISPLAY);
Assert.fail();
} catch (SecurityException expected) {
}
verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY);
}
@SmallTest
@@ -417,6 +441,8 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
userState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME);
@@ -432,6 +458,8 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
public void testOnClientChange_boundServiceCanControlMagnification_requestConnection() {
when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
setupAccessibilityServiceConnection(0);
when(mMockSecurityPolicy.canControlMagnification(any())).thenReturn(true);