Race condition while injecting ACTION_OUTSIDE
In order to dismiss dialog in accessibility, we would also send
ACTION_OUTSIDE while performing ACTION_CLICK. However, the result
of performing ACTION_CLICK in AccessibilityInteractionClient may
be overwritten by the result of performing ACTION_OUTSIDE. This
causes that TouchExplorer may get incorrect result and follow-up
behavior is different.
Bug: 127902192
Test: a11y CTS & unit tests
Test: After enabling Talkback, manually test on 'View Terms' page
in ManagedProvisioning to check if the section heading can
be expanded/collapsed by double tap.
Change-Id: Id6b1f63ea87418c66bf1aebdea829f656581e8ed
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 0051d01..bbd44c8 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -682,14 +682,6 @@
// Handle this hidden action separately
succeeded = handleClickableSpanActionUiThread(
target, virtualDescendantId, arguments);
- } else if (action == R.id.accessibilityActionOutsideTouch) {
- // trigger ACTION_OUTSIDE to notify windows
- final long now = SystemClock.uptimeMillis();
- MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE,
- 0, 0, 0);
- event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- mViewRootImpl.dispatchInputEvent(event);
- succeeded = true;
} else {
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
@@ -756,6 +748,33 @@
}
}
+ /**
+ * Notify outside touch event to the target window.
+ */
+ public void notifyOutsideTouchClientThread() {
+ final Message message = mHandler.obtainMessage();
+ message.what = PrivateHandler.MSG_NOTIFY_OUTSIDE_TOUCH;
+
+ // Don't care about pid and tid because there's no interrogating client for this message.
+ scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS);
+ }
+
+ private void notifyOutsideTouchUiThread() {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null
+ || mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
+ return;
+ }
+ final View root = mViewRootImpl.mView;
+ if (root != null && isShown(root)) {
+ // trigger ACTION_OUTSIDE to notify windows
+ final long now = SystemClock.uptimeMillis();
+ final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE,
+ 0, 0, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mViewRootImpl.dispatchInputEvent(event);
+ }
+ }
+
private View findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
return mViewRootImpl.mView;
@@ -1328,6 +1347,8 @@
private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100;
private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS =
FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1;
+ private static final int MSG_NOTIFY_OUTSIDE_TOUCH =
+ FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 2;
public PrivateHandler(Looper looper) {
super(looper);
@@ -1357,6 +1378,8 @@
return "MSG_APP_PREPARATION_TIMEOUT";
case MSG_CLEAR_ACCESSIBILITY_FOCUS:
return "MSG_CLEAR_ACCESSIBILITY_FOCUS";
+ case MSG_NOTIFY_OUTSIDE_TOUCH:
+ return "MSG_NOTIFY_OUTSIDE_TOUCH";
default:
throw new IllegalArgumentException("Unknown message type: " + type);
}
@@ -1396,6 +1419,9 @@
case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
clearAccessibilityFocusUiThread();
} break;
+ case MSG_NOTIFY_OUTSIDE_TOUCH: {
+ notifyOutsideTouchUiThread();
+ } break;
default:
throw new IllegalArgumentException("Unknown message type: " + type);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7ad118e..e71c7ed 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8819,6 +8819,15 @@
.clearAccessibilityFocusClientThread();
}
}
+
+ @Override
+ public void notifyOutsideTouch() {
+ ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null && viewRootImpl.mView != null) {
+ viewRootImpl.getAccessibilityInteractionController()
+ .notifyOutsideTouchClientThread();
+ }
+ }
}
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 947ff05..deb0d2a 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -57,4 +57,6 @@
int interrogatingPid, long interrogatingTid);
void clearAccessibilityFocus();
+
+ void notifyOutsideTouch();
}
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index ce7995a..2b0c86b 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -185,9 +185,6 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_HIDE_TOOLTIP}. -->
<item type="id" name="accessibilityActionHideTooltip" />
- <!-- Accessibility action to notify a window there is an outside touch. -->
- <item type="id" name="accessibilityActionOutsideTouch" />
-
<!-- A tag used to save the view added to a transition overlay -->
<item type="id" name="transition_overlay_view_tag" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2f34c94..67a8ccc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -245,7 +245,6 @@
<java-symbol type="id" name="selection_end_handle" />
<java-symbol type="id" name="insertion_handle" />
<java-symbol type="id" name="accessibilityActionClickOnClickableSpan" />
- <java-symbol type="id" name="accessibilityActionOutsideTouch" />
<java-symbol type="id" name="camera" />
<java-symbol type="id" name="mic" />
<java-symbol type="id" name="overlay" />