Fix accessiblity CTS tests (framework).
1. An external contribution changed the ordering of views for accessibility. While it attempted to fix a platform issue for a comparator breaking transitivity, it changed the way we order views and results in very unnatural accessibility traversal order. It also broke CTS tets. This change tweaks the comparator which fixes the tests and improves traversal order. 2. If there is at least one accessibility service which cares about windows we register a callback in the window manager for window change notifications. We are updating the window list on this callback. There was a case where if the service requests window updates and immediately asks for the windows it gets none as we have not received a callback from the window manager yet. Now this call returns after we get the callback in a timed fashion. This is consistent with how the other introspection APIs work. 3. Window info objects are cached in the accessibility service process. When putting them in the cache a cloning call was missing resulting in some cases of clobbering windows given to the client. For example, we get some windows, cache them, and return these windows to the client. Now a call to clear the cache arrives while the user processes the windows and the client windows get clobbered. 4. Added API for checking if a window has accessiblity focus to be consistent to the API we have to check whether this window has input focus. 5. Removed some obsolete code. bug:16402352 Change-Id: Ided6da4a82cc0fc703008c58a2dff0119a3ff317
This commit is contained in:
@ -35771,6 +35771,7 @@ package android.view.accessibility {
|
||||
method public android.view.accessibility.AccessibilityWindowInfo getParent();
|
||||
method public android.view.accessibility.AccessibilityNodeInfo getRoot();
|
||||
method public int getType();
|
||||
method public boolean isAccessibilityFocused();
|
||||
method public boolean isActive();
|
||||
method public boolean isFocused();
|
||||
method public static android.view.accessibility.AccessibilityWindowInfo obtain();
|
||||
|
@ -7021,27 +7021,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
if (another == null) {
|
||||
return 1;
|
||||
}
|
||||
if (getClass() != another.getClass()) {
|
||||
return 1;
|
||||
}
|
||||
final int topDiference = mLocation.top - another.mLocation.top;
|
||||
if (topDiference != 0) {
|
||||
return topDiference;
|
||||
}
|
||||
// LTR
|
||||
// We are ordering left-to-right, top-to-bottom.
|
||||
if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
|
||||
final int leftDifference = mLocation.left - another.mLocation.left;
|
||||
// First more to the left than second.
|
||||
if (leftDifference != 0) {
|
||||
return leftDifference;
|
||||
}
|
||||
} else { // RTL
|
||||
final int rightDifference = mLocation.right - another.mLocation.right;
|
||||
// First more to the right than second.
|
||||
if (rightDifference != 0) {
|
||||
return -rightDifference;
|
||||
}
|
||||
}
|
||||
// We are ordering left-to-right, top-to-bottom.
|
||||
final int topDifference = mLocation.top - another.mLocation.top;
|
||||
if (topDifference != 0) {
|
||||
return topDifference;
|
||||
}
|
||||
// Break tie by height.
|
||||
final int heightDiference = mLocation.height() - another.mLocation.height();
|
||||
if (heightDiference != 0) {
|
||||
|
@ -54,7 +54,12 @@ final class AccessibilityCache {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Caching window: " + window.getId());
|
||||
}
|
||||
mWindowCache.put(window.getId(), window);
|
||||
final int windowId = window.getId();
|
||||
AccessibilityWindowInfo oldWindow = mWindowCache.get(windowId);
|
||||
if (oldWindow != null) {
|
||||
oldWindow.recycle();
|
||||
}
|
||||
mWindowCache.put(windowId, AccessibilityWindowInfo.obtain(window));
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,14 +188,13 @@ final class AccessibilityCache {
|
||||
sortedWindows.put(window.getLayer(), window);
|
||||
}
|
||||
|
||||
List<AccessibilityWindowInfo> windows = new ArrayList<>();
|
||||
List<AccessibilityWindowInfo> windows = new ArrayList<>(windowCount);
|
||||
for (int i = windowCount - 1; i >= 0; i--) {
|
||||
AccessibilityWindowInfo window = sortedWindows.valueAt(i);
|
||||
windows.add(AccessibilityWindowInfo.obtain(window));
|
||||
sortedWindows.removeAt(i);
|
||||
}
|
||||
|
||||
sortedWindows.clear();
|
||||
|
||||
return windows;
|
||||
}
|
||||
return null;
|
||||
|
@ -55,6 +55,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
|
||||
|
||||
private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
|
||||
private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
|
||||
private static final int BOOLEAN_PROPERTY_ACCESSIBLITY_FOCUSED = 1 << 2;
|
||||
|
||||
// Housekeeping.
|
||||
private static final int MAX_POOL_SIZE = 10;
|
||||
@ -257,6 +258,26 @@ public final class AccessibilityWindowInfo implements Parcelable {
|
||||
setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this window has accessibility focus.
|
||||
*
|
||||
* @return Whether has accessibility focus.
|
||||
*/
|
||||
public boolean isAccessibilityFocused() {
|
||||
return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBLITY_FOCUSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if this window has accessibility focus.
|
||||
*
|
||||
* @param Whether has accessibility focus.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setAccessibilityFocused(boolean focused) {
|
||||
setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBLITY_FOCUSED, focused);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of child windows.
|
||||
*
|
||||
|
@ -124,6 +124,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
// when that accessibility services are bound.
|
||||
private static final int WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS = 3000;
|
||||
|
||||
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
|
||||
"registerUiTestAutomationService";
|
||||
|
||||
@ -1367,6 +1369,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
if (mWindowsForAccessibilityCallback != null) {
|
||||
mWindowsForAccessibilityCallback = null;
|
||||
mWindowManagerService.setWindowsForAccessibilityCallback(null);
|
||||
// Drop all windows we know about.
|
||||
mSecurityPolicy.clearWindowsLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1631,16 +1635,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
pw.println("}]");
|
||||
pw.println();
|
||||
}
|
||||
final int windowCount = mSecurityPolicy.mWindows.size();
|
||||
for (int j = 0; j < windowCount; j++) {
|
||||
if (j > 0) {
|
||||
pw.append(',');
|
||||
pw.println();
|
||||
if (mSecurityPolicy.mWindows != null) {
|
||||
final int windowCount = mSecurityPolicy.mWindows.size();
|
||||
for (int j = 0; j < windowCount; j++) {
|
||||
if (j > 0) {
|
||||
pw.append(',');
|
||||
pw.println();
|
||||
}
|
||||
pw.append("Window[");
|
||||
AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(j);
|
||||
pw.append(window.toString());
|
||||
pw.append(']');
|
||||
}
|
||||
pw.append("Window[");
|
||||
AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(j);
|
||||
pw.append(window.toString());
|
||||
pw.append(']');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1821,6 +1827,39 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void ensureWindowsAvailableTimed() {
|
||||
synchronized (mLock) {
|
||||
if (mSecurityPolicy.mWindows != null) {
|
||||
return;
|
||||
}
|
||||
// If we have no registered callback, update the state we
|
||||
// we may have to register one but it didn't happen yet.
|
||||
if (mWindowsForAccessibilityCallback == null) {
|
||||
UserState userState = getCurrentUserStateLocked();
|
||||
onUserStateChangedLocked(userState);
|
||||
}
|
||||
// We have no windows but do not care about them, done.
|
||||
if (mWindowsForAccessibilityCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the windows with a timeout.
|
||||
final long startMillis = SystemClock.uptimeMillis();
|
||||
while (mSecurityPolicy.mWindows == null) {
|
||||
final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
|
||||
final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
|
||||
if (remainMillis <= 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mLock.wait(remainMillis);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents an accessibility service. It stores all per service
|
||||
* data required for the service management, provides API for starting/stopping the
|
||||
@ -1876,9 +1915,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
|
||||
final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
|
||||
|
||||
final SparseArray<AccessibilityWindowInfo> mIntrospectedWindows =
|
||||
new SparseArray<>();
|
||||
|
||||
boolean mWasConnectedAndDied;
|
||||
|
||||
// Handler only for dispatching accessibility events since we use event
|
||||
@ -1946,10 +1982,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
& AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
|
||||
mRetrieveInteractiveWindows = (info.flags
|
||||
& AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
|
||||
|
||||
if (!mRetrieveInteractiveWindows) {
|
||||
clearIntrospectedWindows();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2065,6 +2097,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
|
||||
@Override
|
||||
public List<AccessibilityWindowInfo> getWindows() {
|
||||
ensureWindowsAvailableTimed();
|
||||
synchronized (mLock) {
|
||||
// We treat calls from a profile as if made by its perent as profiles
|
||||
// share the accessibility state of the parent. The call below
|
||||
@ -2087,7 +2120,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
AccessibilityWindowInfo windowClone =
|
||||
AccessibilityWindowInfo.obtain(window);
|
||||
windowClone.setConnectionId(mId);
|
||||
mIntrospectedWindows.put(window.getId(), windowClone);
|
||||
windows.add(windowClone);
|
||||
}
|
||||
return windows;
|
||||
@ -2096,6 +2128,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
|
||||
@Override
|
||||
public AccessibilityWindowInfo getWindow(int windowId) {
|
||||
ensureWindowsAvailableTimed();
|
||||
synchronized (mLock) {
|
||||
// We treat calls from a profile as if made by its parent as profiles
|
||||
// share the accessibility state of the parent. The call below
|
||||
@ -2115,7 +2148,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
if (window != null) {
|
||||
AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
|
||||
windowClone.setConnectionId(mId);
|
||||
mIntrospectedWindows.put(windowId, windowClone);
|
||||
return windowClone;
|
||||
}
|
||||
return null;
|
||||
@ -2607,19 +2639,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
}
|
||||
|
||||
public void notifyClearAccessibilityNodeInfoCache() {
|
||||
clearIntrospectedWindows();
|
||||
mInvocationHandler.sendEmptyMessage(
|
||||
InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
|
||||
}
|
||||
|
||||
private void clearIntrospectedWindows() {
|
||||
final int windowCount = mIntrospectedWindows.size();
|
||||
for (int i = windowCount - 1; i >= 0; i--) {
|
||||
mIntrospectedWindows.valueAt(i).recycle();
|
||||
mIntrospectedWindows.removeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyGestureInternal(int gestureId) {
|
||||
final IAccessibilityServiceClient listener;
|
||||
synchronized (mLock) {
|
||||
@ -2955,6 +2978,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
|
||||
// Let the policy update the focused and active windows.
|
||||
mSecurityPolicy.updateWindowsLocked(reportedWindows);
|
||||
|
||||
// Someone may be waiting for the windows - advertise it.
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3130,7 +3156,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
|
||||
| AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
|
||||
|
||||
public final List<AccessibilityWindowInfo> mWindows = new ArrayList<>();
|
||||
public List<AccessibilityWindowInfo> mWindows;
|
||||
|
||||
public int mActiveWindowId = INVALID_WINDOW_ID;
|
||||
public int mFocusedWindowId = INVALID_WINDOW_ID;
|
||||
@ -3185,7 +3211,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
public void clearWindowsLocked() {
|
||||
List<AccessibilityWindowInfo> windows = Collections.emptyList();
|
||||
updateWindowsLocked(windows);
|
||||
mWindows = null;
|
||||
}
|
||||
|
||||
public void updateWindowsLocked(List<AccessibilityWindowInfo> windows) {
|
||||
if (mWindows == null) {
|
||||
mWindows = new ArrayList<>();
|
||||
}
|
||||
|
||||
final int oldWindowCount = mWindows.size();
|
||||
for (int i = oldWindowCount - 1; i >= 0; i--) {
|
||||
mWindows.remove(i).recycle();
|
||||
@ -3231,6 +3267,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
if (window.getId() == mActiveWindowId) {
|
||||
window.setActive(true);
|
||||
}
|
||||
if (window.getId() == mAccessibilityFocusedWindowId) {
|
||||
window.setAccessibilityFocused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3298,7 +3337,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
if (mAccessibilityFocusedWindowId != windowId) {
|
||||
mMainHandler.obtainMessage(MainHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS,
|
||||
mAccessibilityFocusedWindowId, 0).sendToTarget();
|
||||
mAccessibilityFocusedWindowId = windowId;
|
||||
mSecurityPolicy.setAccessibilityFocusedWindowLocked(windowId);
|
||||
mAccessibilityFocusNodeId = nodeId;
|
||||
}
|
||||
}
|
||||
@ -3354,15 +3393,32 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
private void setActiveWindowLocked(int windowId) {
|
||||
if (mActiveWindowId != windowId) {
|
||||
mActiveWindowId = windowId;
|
||||
final int windowCount = mWindows.size();
|
||||
for (int i = 0; i < windowCount; i++) {
|
||||
AccessibilityWindowInfo window = mWindows.get(i);
|
||||
window.setActive(window.getId() == windowId);
|
||||
if (mWindows != null) {
|
||||
final int windowCount = mWindows.size();
|
||||
for (int i = 0; i < windowCount; i++) {
|
||||
AccessibilityWindowInfo window = mWindows.get(i);
|
||||
window.setActive(window.getId() == windowId);
|
||||
}
|
||||
}
|
||||
notifyWindowsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void setAccessibilityFocusedWindowLocked(int windowId) {
|
||||
if (mAccessibilityFocusedWindowId != windowId) {
|
||||
mAccessibilityFocusedWindowId = windowId;
|
||||
if (mWindows != null) {
|
||||
final int windowCount = mWindows.size();
|
||||
for (int i = 0; i < windowCount; i++) {
|
||||
AccessibilityWindowInfo window = mWindows.get(i);
|
||||
window.setAccessibilityFocused(window.getId() == windowId);
|
||||
}
|
||||
}
|
||||
|
||||
notifyWindowsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyWindowsChanged() {
|
||||
// Let the client know the windows changed.
|
||||
AccessibilityEvent event = AccessibilityEvent.obtain(
|
||||
@ -3444,11 +3500,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
}
|
||||
|
||||
private AccessibilityWindowInfo findWindowById(int windowId) {
|
||||
final int windowCount = mWindows.size();
|
||||
for (int i = 0; i < windowCount; i++) {
|
||||
AccessibilityWindowInfo window = mWindows.get(i);
|
||||
if (window.getId() == windowId) {
|
||||
return window;
|
||||
if (mWindows != null) {
|
||||
final int windowCount = mWindows.size();
|
||||
for (int i = 0; i < windowCount; i++) {
|
||||
AccessibilityWindowInfo window = mWindows.get(i);
|
||||
if (window.getId() == windowId) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
Reference in New Issue
Block a user