Merge "Fix accessiblity CTS tests (framework)." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index ffdd9f7..6286af0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35790,6 +35790,7 @@
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();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 854e6be..adad082 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7022,27 +7022,23 @@
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) {
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index ca6437a..ead757e 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -54,7 +54,12 @@
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 @@
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;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 80b5c50..ad55f5f 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -55,6 +55,7 @@
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;
@@ -258,6 +259,26 @@
}
/**
+ * 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.
*
* @return The child count.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b1f1954..4e2f52c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -124,6 +124,8 @@
// 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 @@
if (mWindowsForAccessibilityCallback != null) {
mWindowsForAccessibilityCallback = null;
mWindowManagerService.setWindowsForAccessibilityCallback(null);
+ // Drop all windows we know about.
+ mSecurityPolicy.clearWindowsLocked();
}
}
@@ -1631,16 +1635,18 @@
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 @@
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 @@
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 @@
& AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
mRetrieveInteractiveWindows = (info.flags
& AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
-
- if (!mRetrieveInteractiveWindows) {
- clearIntrospectedWindows();
- }
}
/**
@@ -2065,6 +2097,7 @@
@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 @@
AccessibilityWindowInfo windowClone =
AccessibilityWindowInfo.obtain(window);
windowClone.setConnectionId(mId);
- mIntrospectedWindows.put(window.getId(), windowClone);
windows.add(windowClone);
}
return windows;
@@ -2096,6 +2128,7 @@
@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 @@
if (window != null) {
AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
windowClone.setConnectionId(mId);
- mIntrospectedWindows.put(windowId, windowClone);
return windowClone;
}
return null;
@@ -2607,19 +2639,10 @@
}
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 @@
// 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 @@
| 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 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 @@
if (window.getId() == mActiveWindowId) {
window.setActive(true);
}
+ if (window.getId() == mAccessibilityFocusedWindowId) {
+ window.setAccessibilityFocused(true);
+ }
}
}
@@ -3298,7 +3337,7 @@
if (mAccessibilityFocusedWindowId != windowId) {
mMainHandler.obtainMessage(MainHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS,
mAccessibilityFocusedWindowId, 0).sendToTarget();
- mAccessibilityFocusedWindowId = windowId;
+ mSecurityPolicy.setAccessibilityFocusedWindowLocked(windowId);
mAccessibilityFocusNodeId = nodeId;
}
}
@@ -3354,15 +3393,32 @@
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 @@
}
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;