Merge "Adding system support for a single accessibility focus."

This commit is contained in:
Svetoslav
2014-04-11 18:22:18 +00:00
committed by Android (Google) Code Review
8 changed files with 276 additions and 91 deletions

View File

@@ -2456,6 +2456,7 @@ package android.accessibilityservice {
public abstract class AccessibilityService extends android.app.Service { public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService(); ctor public AccessibilityService();
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
@@ -4755,6 +4756,7 @@ package android.app {
method public void clearWindowAnimationFrameStats(); method public void clearWindowAnimationFrameStats();
method public boolean clearWindowContentFrameStats(int); method public boolean clearWindowContentFrameStats(int);
method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException; method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats(); method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();

View File

@@ -525,6 +525,31 @@ public abstract class AccessibilityService extends Service {
return false; return false;
} }
/**
* Find the view that has the specified focus type. The search is performed
* across all windows.
* <p>
* <strong>Note:</strong> In order to access the windows your service has
* to declare the capability to retrieve window content by setting the
* {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
* Also the service has to opt-in to retrieve the interactive windows by
* setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
* flag.Otherwise, the search will be performed only in the active window.
* </p>
*
* @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.
*
* @see AccessibilityNodeInfo#FOCUS_INPUT
* @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
*/
public AccessibilityNodeInfo findFocus(int focus) {
return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
}
/** /**
* Gets the an {@link AccessibilityServiceInfo} describing this * Gets the an {@link AccessibilityServiceInfo} describing this
* {@link AccessibilityService}. This method is useful if one wants * {@link AccessibilityService}. This method is useful if one wants

View File

@@ -296,6 +296,28 @@ public final class UiAutomation {
return false; return false;
} }
/**
* Find the view that has the specified focus type. The search is performed
* across all windows.
* <p>
* <strong>Note:</strong> In order to access the windows you have to opt-in
* to retrieve the interactive windows by setting the
* {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
* Otherwise, the search will be performed only in the active window.
* </p>
*
* @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.
*
* @see AccessibilityNodeInfo#FOCUS_INPUT
* @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
*/
public AccessibilityNodeInfo findFocus(int focus) {
return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
}
/** /**
* Gets the an {@link AccessibilityServiceInfo} describing this UiAutomation. * Gets the an {@link AccessibilityServiceInfo} describing this UiAutomation.
* This method is useful if one wants to change some of the dynamically * This method is useful if one wants to change some of the dynamically

View File

@@ -8790,11 +8790,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
&& !pointInView(event.getX(), event.getY()))) { && !pointInView(event.getX(), event.getY()))) {
mSendingHoverAccessibilityEvents = false; mSendingHoverAccessibilityEvents = false;
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
// If the window does not have input focus we take away accessibility
// focus as soon as the user stop hovering over the view.
if (mAttachInfo != null && !mAttachInfo.mHasWindowFocus) {
getViewRootImpl().setAccessibilityFocus(null, null);
}
} }
} }

View File

@@ -162,6 +162,19 @@ public final class AccessibilityInteractionClient
false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS); false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
} }
/**
* Gets the root {@link AccessibilityNodeInfo} in a given window.
*
* @param connectionId The id of a connection for interacting with the system.
* @param windowId The window id.
* @return The root {@link AccessibilityNodeInfo} if found, null otherwise.
*/
public AccessibilityNodeInfo getRootInWindow(int connectionId, int windowId) {
return findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId,
AccessibilityNodeInfo.ROOT_NODE_ID, false,
AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
}
/** /**
* Gets the info for a window. * Gets the info for a window.
* *

View File

@@ -76,6 +76,9 @@ public class AccessibilityNodeInfo implements Parcelable {
/** @hide */ /** @hide */
public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID; public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
/** @hide */
public static final int ANY_WINDOW_ID = -2;
/** @hide */ /** @hide */
public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;

View File

@@ -166,8 +166,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final Point mTempPoint = new Point(); private final Point mTempPoint = new Point();
private final Display mDefaultDisplay;
private final PackageManager mPackageManager; private final PackageManager mPackageManager;
private final WindowManagerInternal mWindowManagerService; private final WindowManagerInternal mWindowManagerService;
@@ -176,7 +174,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final MainHandler mMainHandler; private final MainHandler mMainHandler;
private Service mQueryBridge; private InteractionBridge mInteractionBridge;
private AlertDialog mEnableTouchExplorationDialog; private AlertDialog mEnableTouchExplorationDialog;
@@ -232,10 +230,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mSecurityPolicy = new SecurityPolicy(); mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHandler(mContext.getMainLooper()); mMainHandler = new MainHandler(mContext.getMainLooper());
//TODO: (multi-display) We need to support multiple displays.
DisplayManager displayManager = (DisplayManager)
mContext.getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
registerBroadcastReceivers(); registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register( new AccessibilityContentObserver(mMainHandler).register(
context.getContentResolver()); context.getContentResolver());
@@ -401,7 +395,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return true; // yes, recycle the event return true; // yes, recycle the event
} }
if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) { if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
mSecurityPolicy.updateActiveWindowLocked(event.getWindowId(), event.getEventType()); mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(),
event.getSourceNodeId(), event.getEventType());
mSecurityPolicy.updateEventSourceLocked(event); mSecurityPolicy.updateEventSourceLocked(event);
notifyAccessibilityServicesDelayedLocked(event, false); notifyAccessibilityServicesDelayedLocked(event, false);
notifyAccessibilityServicesDelayedLocked(event, true); notifyAccessibilityServicesDelayedLocked(event, true);
@@ -725,46 +720,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @return Whether accessibility focus was found and the bounds are populated. * @return Whether accessibility focus was found and the bounds are populated.
*/ */
// TODO: (multi-display) Make sure this works for multiple displays. // TODO: (multi-display) Make sure this works for multiple displays.
boolean getAccessibilityFocusBoundsInActiveWindow(Rect outBounds) { boolean getAccessibilityFocusBounds(Rect outBounds) {
// Instead of keeping track of accessibility focus events per return getInteractionBridgeLocked().getAccessibilityFocusBoundsNotLocked(outBounds);
// window to be able to find the focus in the active window,
// we take a stateless approach and look it up. This is fine
// since we do this only when the user clicks/long presses.
Service service = getQueryBridge();
final int connectionId = service.mId;
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
client.addConnection(connectionId, service);
try {
AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
.getRootInActiveWindow(connectionId);
if (root == null) {
return false;
}
AccessibilityNodeInfo focus = root.findFocus(
AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
if (focus == null) {
return false;
}
focus.getBoundsInScreen(outBounds);
MagnificationSpec spec = service.getCompatibleMagnificationSpec(focus.getWindowId());
if (spec != null && !spec.isNop()) {
outBounds.offset((int) -spec.offsetX, (int) -spec.offsetY);
outBounds.scale(1 / spec.scale);
}
// Clip to the window rectangle.
Rect windowBounds = mTempRect;
getActiveWindowBounds(windowBounds);
outBounds.intersect(windowBounds);
// Clip to the screen rectangle.
mDefaultDisplay.getRealSize(mTempPoint);
outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y);
return true;
} finally {
client.removeConnection(connectionId);
}
} }
/** /**
@@ -855,14 +812,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
} }
private Service getQueryBridge() { private InteractionBridge getInteractionBridgeLocked() {
if (mQueryBridge == null) { if (mInteractionBridge == null) {
AccessibilityServiceInfo info = new AccessibilityServiceInfo(); mInteractionBridge = new InteractionBridge();
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
mQueryBridge = new Service(UserHandle.USER_NULL,
sFakeAccessibilityServiceComponentName, info);
} }
return mQueryBridge; return mInteractionBridge;
} }
private boolean notifyGestureLocked(int gestureId, boolean isDefault) { private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
@@ -1333,9 +1287,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private void onUserStateChangedLocked(UserState userState) { private void onUserStateChangedLocked(UserState userState) {
// TODO: Remove this hack // TODO: Remove this hack
mInitialized = true; mInitialized = true;
updateLegacyCapabilities(userState); updateLegacyCapabilitiesLocked(userState);
updateServicesLocked(userState); updateServicesLocked(userState);
updateWindowsForAccessibilityCallback(userState); updateWindowsForAccessibilityCallbackLocked(userState);
updateAccessibilityFocusBehaviorLocked(userState);
updateFilterKeyEventsLocked(userState); updateFilterKeyEventsLocked(userState);
updateTouchExplorationLocked(userState); updateTouchExplorationLocked(userState);
updateEnhancedWebAccessibilityLocked(userState); updateEnhancedWebAccessibilityLocked(userState);
@@ -1344,7 +1299,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
scheduleUpdateClientsIfNeededLocked(userState); scheduleUpdateClientsIfNeededLocked(userState);
} }
private void updateWindowsForAccessibilityCallback(UserState userState) { private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
// If there is no service that can operate with interactive windows
// then we keep the old behavior where a window loses accessibility
// focus if it is no longer active. This still changes the behavior
// for services that do not operate with interactive windows and run
// at the same time as the one(s) which does. In practice however,
// there is only one service that uses accessibility focus and it
// is typically the one that operates with interactive windows, So,
// this is fine. Note that to allow a service to work across windows
// we have to allow accessibility focus stay in any of them. Sigh...
List<Service> boundServices = userState.mBoundServices;
final int boundServiceCount = boundServices.size();
for (int i = 0; i < boundServiceCount; i++) {
Service boundService = boundServices.get(i);
if (boundService.canRetrieveInteractiveWindowsLocked()) {
userState.mAccessibilityFocusOnlyInActiveWindow = false;
return;
}
}
userState.mAccessibilityFocusOnlyInActiveWindow = true;
}
private void updateWindowsForAccessibilityCallbackLocked(UserState userState) {
if (userState.mIsAccessibilityEnabled) { if (userState.mIsAccessibilityEnabled) {
// We observe windows for accessibility only if there is at least // We observe windows for accessibility only if there is at least
// one bound service that can retrieve window content that specified // one bound service that can retrieve window content that specified
@@ -1357,8 +1334,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int boundServiceCount = boundServices.size(); final int boundServiceCount = boundServices.size();
for (int i = 0; i < boundServiceCount; i++) { for (int i = 0; i < boundServiceCount; i++) {
Service boundService = boundServices.get(i); Service boundService = boundServices.get(i);
if (mSecurityPolicy.canRetrieveWindowContentLocked(boundService) if (boundService.canRetrieveInteractiveWindowsLocked()) {
&& boundService.mRetrieveInteractiveWindows) {
boundServiceCanRetrieveInteractiveWindows = true; boundServiceCanRetrieveInteractiveWindows = true;
break; break;
} }
@@ -1381,7 +1357,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
} }
private void updateLegacyCapabilities(UserState userState) { private void updateLegacyCapabilitiesLocked(UserState userState) {
// Up to JB-MR1 we had a white list with services that can enable touch // Up to JB-MR1 we had a white list with services that can enable touch
// exploration. When a service is first started we show a dialog to the // exploration. When a service is first started we show a dialog to the
// use to get a permission to white list the service. // use to get a permission to white list the service.
@@ -1582,6 +1558,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId); DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId);
} }
private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
IBinder windowToken = mGlobalWindowTokens.get(windowId);
if (windowToken == null) {
windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
}
if (windowToken != null) {
return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
windowToken);
}
return null;
}
@Override @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP); mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
@@ -1658,6 +1646,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public static final int MSG_UPDATE_INPUT_FILTER = 6; public static final int MSG_UPDATE_INPUT_FILTER = 6;
public static final int MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG = 7; public static final int MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG = 7;
public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8; public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
public static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = 9;
public MainHandler(Looper looper) { public MainHandler(Looper looper) {
super(looper); super(looper);
@@ -1713,6 +1702,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Service service = (Service) msg.obj; Service service = (Service) msg.obj;
showEnableTouchExplorationDialog(service); showEnableTouchExplorationDialog(service);
} break; } break;
case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
final int windowId = msg.arg1;
InteractionBridge bridge;
synchronized (mLock) {
bridge = getInteractionBridgeLocked();
}
bridge.clearAccessibilityFocusNotLocked(windowId);
} break;
} }
} }
@@ -1981,6 +1979,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
} }
public boolean canRetrieveInteractiveWindowsLocked() {
return mSecurityPolicy.canRetrieveWindowContentLocked(this)
&& mRetrieveInteractiveWindows;
}
@Override @Override
public void setServiceInfo(AccessibilityServiceInfo info) { public void setServiceInfo(AccessibilityServiceInfo info) {
final long identity = Binder.clearCallingIdentity(); final long identity = Binder.clearCallingIdentity();
@@ -2109,7 +2112,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
final int interrogatingPid = Binder.getCallingPid(); final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity(); final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try { try {
connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId, connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId,
viewIdResName, interactionId, callback, mFetchFlags, interrogatingPid, viewIdResName, interactionId, callback, mFetchFlags, interrogatingPid,
@@ -2153,7 +2156,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
final int interrogatingPid = Binder.getCallingPid(); final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity(); final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try { try {
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
@@ -2197,7 +2200,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
final int interrogatingPid = Binder.getCallingPid(); final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity(); final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try { try {
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
interactionId, callback, mFetchFlags | flags, interrogatingPid, interactionId, callback, mFetchFlags | flags, interrogatingPid,
@@ -2227,7 +2230,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) { if (resolvedUserId != mCurrentUserId) {
return false; return false;
} }
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
accessibilityWindowId, focusType);
final boolean permissionGranted = final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) { if (!permissionGranted) {
@@ -2241,14 +2245,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
final int interrogatingPid = Binder.getCallingPid(); final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity(); final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try { try {
connection.findFocus(accessibilityNodeId, focusType, interactionId, callback, connection.findFocus(accessibilityNodeId, focusType, interactionId, callback,
mFetchFlags, interrogatingPid, interrogatingTid, spec); mFetchFlags, interrogatingPid, interrogatingTid, spec);
return true; return true;
} catch (RemoteException re) { } catch (RemoteException re) {
if (DEBUG) { if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()"); Slog.e(LOG_TAG, "Error calling findFocus()");
} }
} finally { } finally {
Binder.restoreCallingIdentity(identityToken); Binder.restoreCallingIdentity(identityToken);
@@ -2284,7 +2288,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
final int interrogatingPid = Binder.getCallingPid(); final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity(); final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try { try {
connection.focusSearch(accessibilityNodeId, direction, interactionId, callback, connection.focusSearch(accessibilityNodeId, direction, interactionId, callback,
mFetchFlags, interrogatingPid, interrogatingTid, spec); mFetchFlags, interrogatingPid, interrogatingTid, spec);
@@ -2729,16 +2733,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return accessibilityWindowId; return accessibilityWindowId;
} }
private MagnificationSpec getCompatibleMagnificationSpec(int windowId) { private int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
IBinder windowToken = mGlobalWindowTokens.get(windowId); if (windowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
if (windowToken == null) { return mSecurityPolicy.mActiveWindowId;
windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
} }
if (windowToken != null) { if (windowId == AccessibilityNodeInfo.ANY_WINDOW_ID) {
return mWindowManagerService.getCompatibleMagnificationSpecForWindow( if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) {
windowToken); return mSecurityPolicy.mFocusedWindowId;
} else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) {
return mSecurityPolicy.mAccessibilityFocusedWindowId;
} }
return null; }
return windowId;
} }
private final class InvocationHandler extends Handler { private final class InvocationHandler extends Handler {
@@ -3031,7 +3037,86 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
} }
private final class InteractionBridge {
private final Display mDefaultDisplay;
private final int mConnectionId;
private final AccessibilityInteractionClient mClient;
public InteractionBridge() {
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
Service service = new Service(UserHandle.USER_NULL,
sFakeAccessibilityServiceComponentName, info);
mConnectionId = service.mId;
mClient = AccessibilityInteractionClient.getInstance();
mClient.addConnection(mConnectionId, service);
//TODO: (multi-display) We need to support multiple displays.
DisplayManager displayManager = (DisplayManager)
mContext.getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
}
public void clearAccessibilityFocusNotLocked(int windowId) {
AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked(windowId);
if (focus != null) {
focus.performAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
}
}
public boolean getAccessibilityFocusBoundsNotLocked(Rect outBounds) {
AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
if (focus == null) {
return false;
}
synchronized (mLock) {
focus.getBoundsInScreen(outBounds);
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
if (spec != null && !spec.isNop()) {
outBounds.offset((int) -spec.offsetX, (int) -spec.offsetY);
outBounds.scale(1 / spec.scale);
}
// Clip to the window rectangle.
Rect windowBounds = mTempRect;
getActiveWindowBounds(windowBounds);
outBounds.intersect(windowBounds);
// Clip to the screen rectangle.
mDefaultDisplay.getRealSize(mTempPoint);
outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y);
return true;
}
}
private AccessibilityNodeInfo getAccessibilityFocusNotLocked() {
final int focusedWindowId;
synchronized (mLock) {
focusedWindowId = mSecurityPolicy.mAccessibilityFocusedWindowId;
if (focusedWindowId == SecurityPolicy.INVALID_WINDOW_ID) {
return null;
}
}
return getAccessibilityFocusNotLocked(focusedWindowId);
}
private AccessibilityNodeInfo getAccessibilityFocusNotLocked(int windowId) {
return mClient.findFocus(mConnectionId,
windowId, AccessibilityNodeInfo.ROOT_NODE_ID,
AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
}
}
final class SecurityPolicy { final class SecurityPolicy {
public static final int INVALID_WINDOW_ID = -1;
private static final int VALID_ACTIONS = private static final int VALID_ACTIONS =
AccessibilityNodeInfo.ACTION_CLICK AccessibilityNodeInfo.ACTION_CLICK
| AccessibilityNodeInfo.ACTION_LONG_CLICK | AccessibilityNodeInfo.ACTION_LONG_CLICK
@@ -3075,8 +3160,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public final List<AccessibilityWindowInfo> mWindows = public final List<AccessibilityWindowInfo> mWindows =
new ArrayList<AccessibilityWindowInfo>(); new ArrayList<AccessibilityWindowInfo>();
public int mActiveWindowId; public int mActiveWindowId = INVALID_WINDOW_ID;
public int mFocusedWindowId; public int mFocusedWindowId = INVALID_WINDOW_ID;
public int mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
public AccessibilityEvent mShowingFocusedWindowEvent; public AccessibilityEvent mShowingFocusedWindowEvent;
private boolean mTouchInteractionInProgress; private boolean mTouchInteractionInProgress;
@@ -3129,9 +3217,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mWindows.remove(i).recycle(); mWindows.remove(i).recycle();
} }
mFocusedWindowId = -1; mFocusedWindowId = INVALID_WINDOW_ID;
if (!mTouchInteractionInProgress) { if (!mTouchInteractionInProgress) {
mActiveWindowId = -1; mActiveWindowId = INVALID_WINDOW_ID;
} }
// If the active window goes away while the user is touch exploring we // If the active window goes away while the user is touch exploring we
@@ -3159,7 +3247,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
if (mTouchInteractionInProgress && activeWindowGone) { if (mTouchInteractionInProgress && activeWindowGone) {
mActiveWindowId = -1; mActiveWindowId = INVALID_WINDOW_ID;
} }
// Focused window may change the active one, so set the // Focused window may change the active one, so set the
@@ -3193,7 +3281,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
} }
public void updateActiveWindowLocked(int windowId, int eventType) { public void updateActiveAndAccessibilityFocusedWindowLocked(int windowId, long nodeId,
int eventType) {
// The active window is either the window that has input focus or // The active window is either the window that has input focus or
// the window that the user is currently touching. If the user is // the window that the user is currently touching. If the user is
// touching a window that does not have input focus as soon as the // touching a window that does not have input focus as soon as the
@@ -3228,6 +3317,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} }
} }
} break; } break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
synchronized (mLock) {
if (mAccessibilityFocusedWindowId != windowId) {
mMainHandler.obtainMessage(MainHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS,
mAccessibilityFocusedWindowId, 0).sendToTarget();
mAccessibilityFocusedWindowId = windowId;
mAccessibilityFocusNodeId = nodeId;
}
}
} break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
synchronized (mLock) {
if (mAccessibilityFocusNodeId == nodeId) {
mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
}
if (mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID
&& mAccessibilityFocusedWindowId == windowId) {
mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
}
}
} break;
} }
} }
@@ -3248,7 +3360,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// (they are a result of user touching the screen) so change of // (they are a result of user touching the screen) so change of
// the active window before all hover accessibility events from // the active window before all hover accessibility events from
// the touched window are delivered is fine. // the touched window are delivered is fine.
final int oldActiveWindow = mSecurityPolicy.mActiveWindowId;
setActiveWindowLocked(mFocusedWindowId); setActiveWindowLocked(mFocusedWindowId);
// If there is no service that can operate with active windows
// we keep accessibility focus behavior to constrain it only in
// the active window. Look at updateAccessibilityFocusBehaviorLocked
// for details.
if (oldActiveWindow != mSecurityPolicy.mActiveWindowId
&& mAccessibilityFocusedWindowId == oldActiveWindow
&& getCurrentUserStateLocked().mAccessibilityFocusOnlyInActiveWindow) {
mMainHandler.obtainMessage(MainHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS,
oldActiveWindow, 0).sendToTarget();
}
} }
} }
@@ -3403,6 +3527,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public boolean mIsDisplayMagnificationEnabled; public boolean mIsDisplayMagnificationEnabled;
public boolean mIsFilterKeyEventsEnabled; public boolean mIsFilterKeyEventsEnabled;
public boolean mHasDisplayColorAdjustment; public boolean mHasDisplayColorAdjustment;
public boolean mAccessibilityFocusOnlyInActiveWindow;
private Service mUiAutomationService; private Service mUiAutomationService;
private IAccessibilityServiceClient mUiAutomationServiceClient; private IAccessibilityServiceClient mUiAutomationServiceClient;

View File

@@ -1159,7 +1159,7 @@ class TouchExplorer implements EventStreamTransformation {
// No last touch explored event but there is accessibility focus in // No last touch explored event but there is accessibility focus in
// the active window. We click in the middle of the focus bounds. // the active window. We click in the middle of the focus bounds.
Rect focusBounds = mTempRect; Rect focusBounds = mTempRect;
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { if (mAms.getAccessibilityFocusBounds(focusBounds)) {
clickLocationX = focusBounds.centerX(); clickLocationX = focusBounds.centerX();
clickLocationY = focusBounds.centerY(); clickLocationY = focusBounds.centerY();
} else { } else {
@@ -1177,7 +1177,7 @@ class TouchExplorer implements EventStreamTransformation {
mAms.getActiveWindowBounds(activeWindowBounds); mAms.getActiveWindowBounds(activeWindowBounds);
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
Rect focusBounds = mTempRect; Rect focusBounds = mTempRect;
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { if (mAms.getAccessibilityFocusBounds(focusBounds)) {
if (!focusBounds.contains(clickLocationX, clickLocationY)) { if (!focusBounds.contains(clickLocationX, clickLocationY)) {
clickLocationX = focusBounds.centerX(); clickLocationX = focusBounds.centerX();
clickLocationY = focusBounds.centerY(); clickLocationY = focusBounds.centerY();
@@ -1332,7 +1332,7 @@ class TouchExplorer implements EventStreamTransformation {
// No last touch explored event but there is accessibility focus in // No last touch explored event but there is accessibility focus in
// the active window. We click in the middle of the focus bounds. // the active window. We click in the middle of the focus bounds.
Rect focusBounds = mTempRect; Rect focusBounds = mTempRect;
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { if (mAms.getAccessibilityFocusBounds(focusBounds)) {
clickLocationX = focusBounds.centerX(); clickLocationX = focusBounds.centerX();
clickLocationY = focusBounds.centerY(); clickLocationY = focusBounds.centerY();
} else { } else {
@@ -1350,7 +1350,7 @@ class TouchExplorer implements EventStreamTransformation {
mAms.getActiveWindowBounds(activeWindowBounds); mAms.getActiveWindowBounds(activeWindowBounds);
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
Rect focusBounds = mTempRect; Rect focusBounds = mTempRect;
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { if (mAms.getAccessibilityFocusBounds(focusBounds)) {
if (!focusBounds.contains(clickLocationX, clickLocationY)) { if (!focusBounds.contains(clickLocationX, clickLocationY)) {
clickLocationX = focusBounds.centerX(); clickLocationX = focusBounds.centerX();
clickLocationY = focusBounds.centerY(); clickLocationY = focusBounds.centerY();