Merge "Eliminate potential deadlock in AccessibilityCache"
This commit is contained in:
@@ -158,6 +158,7 @@ public class AccessibilityCache {
|
|||||||
* @param event An event.
|
* @param event An event.
|
||||||
*/
|
*/
|
||||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||||
|
AccessibilityNodeInfo nodeToRefresh = null;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.i(LOG_TAG, "onAccessibilityEvent(" + event + ")");
|
Log.i(LOG_TAG, "onAccessibilityEvent(" + event + ")");
|
||||||
@@ -166,17 +167,19 @@ public class AccessibilityCache {
|
|||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
|
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
|
||||||
if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
|
if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
|
||||||
refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
|
removeCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
|
||||||
}
|
}
|
||||||
mAccessibilityFocus = event.getSourceNodeId();
|
mAccessibilityFocus = event.getSourceNodeId();
|
||||||
mAccessibilityFocusedWindow = event.getWindowId();
|
mAccessibilityFocusedWindow = event.getWindowId();
|
||||||
refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
|
nodeToRefresh = removeCachedNodeLocked(mAccessibilityFocusedWindow,
|
||||||
|
mAccessibilityFocus);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
|
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
|
||||||
if (mAccessibilityFocus == event.getSourceNodeId()
|
if (mAccessibilityFocus == event.getSourceNodeId()
|
||||||
&& mAccessibilityFocusedWindow == event.getWindowId()) {
|
&& mAccessibilityFocusedWindow == event.getWindowId()) {
|
||||||
refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
|
nodeToRefresh = removeCachedNodeLocked(mAccessibilityFocusedWindow,
|
||||||
|
mAccessibilityFocus);
|
||||||
mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
|
mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
|
||||||
mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
||||||
}
|
}
|
||||||
@@ -184,17 +187,18 @@ public class AccessibilityCache {
|
|||||||
|
|
||||||
case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
|
case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
|
||||||
if (mInputFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
|
if (mInputFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
|
||||||
refreshCachedNodeLocked(event.getWindowId(), mInputFocus);
|
removeCachedNodeLocked(event.getWindowId(), mInputFocus);
|
||||||
}
|
}
|
||||||
mInputFocus = event.getSourceNodeId();
|
mInputFocus = event.getSourceNodeId();
|
||||||
refreshCachedNodeLocked(event.getWindowId(), mInputFocus);
|
nodeToRefresh = removeCachedNodeLocked(event.getWindowId(), mInputFocus);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case AccessibilityEvent.TYPE_VIEW_SELECTED:
|
case AccessibilityEvent.TYPE_VIEW_SELECTED:
|
||||||
case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
|
case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
|
||||||
case AccessibilityEvent.TYPE_VIEW_CLICKED:
|
case AccessibilityEvent.TYPE_VIEW_CLICKED:
|
||||||
case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
|
case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
|
||||||
refreshCachedNodeLocked(event.getWindowId(), event.getSourceNodeId());
|
nodeToRefresh = removeCachedNodeLocked(event.getWindowId(),
|
||||||
|
event.getSourceNodeId());
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
|
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
|
||||||
@@ -205,7 +209,7 @@ public class AccessibilityCache {
|
|||||||
& AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
|
& AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
|
||||||
clearSubTreeLocked(windowId, sourceId);
|
clearSubTreeLocked(windowId, sourceId);
|
||||||
} else {
|
} else {
|
||||||
refreshCachedNodeLocked(windowId, sourceId);
|
nodeToRefresh = removeCachedNodeLocked(windowId, sourceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -218,8 +222,8 @@ public class AccessibilityCache {
|
|||||||
if (event.getWindowChanges()
|
if (event.getWindowChanges()
|
||||||
== AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) {
|
== AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) {
|
||||||
// Don't need to clear all cache. Unless the changes are related to
|
// Don't need to clear all cache. Unless the changes are related to
|
||||||
// content, we won't clear all cache here.
|
// content, we won't clear all cache here with clear().
|
||||||
refreshCachedWindowLocked(event.getWindowId());
|
clearWindowCacheLocked();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
|
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
|
||||||
@@ -228,59 +232,34 @@ public class AccessibilityCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nodeToRefresh != null) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "Refreshing and re-adding cached node.");
|
||||||
|
}
|
||||||
|
if (mAccessibilityNodeRefresher.refreshNode(nodeToRefresh, true)) {
|
||||||
|
add(nodeToRefresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (CHECK_INTEGRITY) {
|
if (CHECK_INTEGRITY) {
|
||||||
checkIntegrity();
|
checkIntegrity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshCachedNodeLocked(int windowId, long sourceId) {
|
private AccessibilityNodeInfo removeCachedNodeLocked(int windowId, long sourceId) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.i(LOG_TAG, "Refreshing cached node.");
|
Log.i(LOG_TAG, "Removing cached node.");
|
||||||
}
|
}
|
||||||
|
|
||||||
LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
|
LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
|
||||||
if (nodes == null) {
|
if (nodes == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
AccessibilityNodeInfo cachedInfo = nodes.get(sourceId);
|
AccessibilityNodeInfo cachedInfo = nodes.get(sourceId);
|
||||||
// If the source is not in the cache - nothing to do.
|
// If the source is not in the cache - nothing to do.
|
||||||
if (cachedInfo == null) {
|
if (cachedInfo == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
|
||||||
// The node changed so we will just refresh it right now.
|
|
||||||
if (mAccessibilityNodeRefresher.refreshNode(cachedInfo, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Weird, we could not refresh. Just evict the entire sub-tree.
|
|
||||||
clearSubTreeLocked(windowId, sourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshCachedWindowLocked(int windowId) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.i(LOG_TAG, "Refreshing cached window.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int displayCounts = mWindowCacheByDisplay.size();
|
|
||||||
for (int i = 0; i < displayCounts; i++) {
|
|
||||||
final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
|
|
||||||
mWindowCacheByDisplay.valueAt(i);
|
|
||||||
if (windowsOfDisplay == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final AccessibilityWindowInfo window = windowsOfDisplay.get(windowId);
|
|
||||||
if (window == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!mAccessibilityNodeRefresher.refreshWindow(window)) {
|
|
||||||
// If we fail to refresh the window, clear all windows.
|
|
||||||
clearWindowCacheLocked();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
nodes.remove(sourceId);
|
||||||
|
return cachedInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -450,7 +429,7 @@ public class AccessibilityCache {
|
|||||||
if (clone.isAccessibilityFocused()) {
|
if (clone.isAccessibilityFocused()) {
|
||||||
if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
|
if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
|
||||||
&& mAccessibilityFocus != sourceId) {
|
&& mAccessibilityFocus != sourceId) {
|
||||||
refreshCachedNodeLocked(windowId, mAccessibilityFocus);
|
removeCachedNodeLocked(windowId, mAccessibilityFocus);
|
||||||
}
|
}
|
||||||
mAccessibilityFocus = sourceId;
|
mAccessibilityFocus = sourceId;
|
||||||
mAccessibilityFocusedWindow = windowId;
|
mAccessibilityFocusedWindow = windowId;
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ public class AccessibilityCacheTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
|
public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRemoved() {
|
||||||
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
||||||
nodeInfo.setAccessibilityFocused(true);
|
nodeInfo.setAccessibilityFocused(true);
|
||||||
mAccessibilityCache.add(nodeInfo);
|
mAccessibilityCache.add(nodeInfo);
|
||||||
@@ -573,7 +573,7 @@ public class AccessibilityCacheTest {
|
|||||||
mAccessibilityCache.onAccessibilityEvent(event);
|
mAccessibilityCache.onAccessibilityEvent(event);
|
||||||
event.recycle();
|
event.recycle();
|
||||||
try {
|
try {
|
||||||
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
|
assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID));
|
||||||
} finally {
|
} finally {
|
||||||
nodeInfo.recycle();
|
nodeInfo.recycle();
|
||||||
}
|
}
|
||||||
@@ -614,7 +614,7 @@ public class AccessibilityCacheTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
|
public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRemoved() {
|
||||||
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
||||||
nodeInfo.setFocused(true);
|
nodeInfo.setFocused(true);
|
||||||
mAccessibilityCache.add(nodeInfo);
|
mAccessibilityCache.add(nodeInfo);
|
||||||
@@ -624,7 +624,7 @@ public class AccessibilityCacheTest {
|
|||||||
mAccessibilityCache.onAccessibilityEvent(event);
|
mAccessibilityCache.onAccessibilityEvent(event);
|
||||||
event.recycle();
|
event.recycle();
|
||||||
try {
|
try {
|
||||||
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
|
assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID));
|
||||||
} finally {
|
} finally {
|
||||||
nodeInfo.recycle();
|
nodeInfo.recycle();
|
||||||
}
|
}
|
||||||
@@ -733,20 +733,15 @@ public class AccessibilityCacheTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRefreshed() {
|
public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRemoved() {
|
||||||
AccessibilityNodeInfo nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
AccessibilityNodeInfo nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
||||||
nodeInfo1.setAccessibilityFocused(true);
|
nodeInfo1.setAccessibilityFocused(true);
|
||||||
mAccessibilityCache.add(nodeInfo1);
|
mAccessibilityCache.add(nodeInfo1);
|
||||||
AccessibilityNodeInfo nodeInfo2 = getNodeWithA11yAndWindowId(OTHER_VIEW_ID, WINDOW_ID_1);
|
AccessibilityNodeInfo nodeInfo2 = getNodeWithA11yAndWindowId(OTHER_VIEW_ID, WINDOW_ID_1);
|
||||||
nodeInfo2.setAccessibilityFocused(true);
|
nodeInfo2.setAccessibilityFocused(true);
|
||||||
mAccessibilityCache.add(nodeInfo2);
|
mAccessibilityCache.add(nodeInfo2);
|
||||||
AccessibilityEvent event = AccessibilityEvent.obtain(
|
|
||||||
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
|
|
||||||
event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
|
|
||||||
mAccessibilityCache.onAccessibilityEvent(event);
|
|
||||||
event.recycle();
|
|
||||||
try {
|
try {
|
||||||
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo1, true);
|
assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID));
|
||||||
} finally {
|
} finally {
|
||||||
nodeInfo1.recycle();
|
nodeInfo1.recycle();
|
||||||
nodeInfo2.recycle();
|
nodeInfo2.recycle();
|
||||||
|
|||||||
Reference in New Issue
Block a user