Loading core/java/android/view/AccessibilityInteractionController.java +9 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.os.Message; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.text.style.AccessibilityClickableSpan; import android.text.style.ClickableSpan; import android.util.LongSparseArray; Loading Loading @@ -702,6 +703,14 @@ final class AccessibilityInteractionController { // 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) { Loading core/java/android/view/WindowInfo.java +7 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ public class WindowInfo implements Parcelable { public CharSequence title; public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID; public boolean inPictureInPicture; public boolean hasFlagWatchOutsideTouch; private WindowInfo() { /* do nothing - hide constructor */ Loading @@ -74,6 +75,7 @@ public class WindowInfo implements Parcelable { window.title = other.title; window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor; window.inPictureInPicture = other.inPictureInPicture; window.hasFlagWatchOutsideTouch = other.hasFlagWatchOutsideTouch; if (other.childTokens != null && !other.childTokens.isEmpty()) { if (window.childTokens == null) { Loading Loading @@ -108,6 +110,7 @@ public class WindowInfo implements Parcelable { parcel.writeCharSequence(title); parcel.writeLong(accessibilityIdOfAnchor); parcel.writeInt(inPictureInPicture ? 1 : 0); parcel.writeInt(hasFlagWatchOutsideTouch ? 1 : 0); if (childTokens != null && !childTokens.isEmpty()) { parcel.writeInt(1); Loading @@ -130,6 +133,8 @@ public class WindowInfo implements Parcelable { builder.append(", focused=").append(focused); builder.append(", children=").append(childTokens); builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor); builder.append(", pictureInPicture=").append(inPictureInPicture); builder.append(", watchOutsideTouch=").append(hasFlagWatchOutsideTouch); builder.append(']'); return builder.toString(); } Loading @@ -145,6 +150,7 @@ public class WindowInfo implements Parcelable { title = parcel.readCharSequence(); accessibilityIdOfAnchor = parcel.readLong(); inPictureInPicture = (parcel.readInt() == 1); hasFlagWatchOutsideTouch = (parcel.readInt() == 1); final boolean hasChildren = (parcel.readInt() == 1); if (hasChildren) { Loading @@ -167,6 +173,7 @@ public class WindowInfo implements Parcelable { childTokens.clear(); } inPictureInPicture = false; hasFlagWatchOutsideTouch = false; } public static final Parcelable.Creator<WindowInfo> CREATOR = Loading core/res/res/values/ids.xml +3 −0 Original line number Diff line number Diff line Loading @@ -172,4 +172,7 @@ <!-- 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" /> </resources> core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -218,6 +218,7 @@ <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" /> Loading services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +57 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; Loading Loading @@ -2621,6 +2623,38 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return -1; } private void notifyOutsideTouchIfNeeded(int targetWindowId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int fetchFlags, int interrogatingPid, long interrogatingTid) { if (action != ACTION_CLICK && action != ACTION_LONG_CLICK) { return; } final List<Integer> outsideWindowsIds; final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>(); synchronized (mLock) { outsideWindowsIds = mSecurityPolicy.getWatchOutsideTouchWindowId(targetWindowId); for (int i = 0; i < outsideWindowsIds.size(); i++) { connectionList.add(getConnectionLocked(outsideWindowsIds.get(i))); } } for (int i = 0; i < connectionList.size(); i++) { final RemoteAccessibilityConnection connection = connectionList.get(i); if (connection != null) { try { connection.mConnection.performAccessibilityAction( AccessibilityNodeInfo.ROOT_ITEM_ID, R.id.accessibilityActionOutsideTouch, arguments, interactionId, callback, fetchFlags, interrogatingPid, interrogatingTid); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re); } } } } } @Override public void ensureWindowsAvailableTimed() { synchronized (mLock) { Loading Loading @@ -2700,6 +2734,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mPowerManager.userActivity(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); notifyOutsideTouchIfNeeded(resolvedWindowId, action, arguments, interactionId, callback, fetchFlags, interrogatingPid, interrogatingTid); if (activityToken != null) { LocalServices.getService(ActivityTaskManagerInternal.class) .setFocusedActivity(activityToken); Loading Loading @@ -3027,6 +3063,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; private boolean mTouchInteractionInProgress; private boolean mHasWatchOutsideTouchWindow; private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) { final int eventType = event.getEventType(); Loading Loading @@ -3184,6 +3221,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mWindowInfoById.valueAt(i).recycle(); } mWindowInfoById.clear(); mHasWatchOutsideTouchWindow = false; mFocusedWindowId = INVALID_WINDOW_ID; if (!mTouchInteractionInProgress) { Loading Loading @@ -3228,6 +3266,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub activeWindowGone = false; } } if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) { mHasWatchOutsideTouchWindow = true; } mWindows.add(window); mA11yWindowInfoById.put(windowId, window); mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo)); Loading Loading @@ -3646,6 +3687,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mWindowInfoById.get(windowId); } private List<Integer> getWatchOutsideTouchWindowId(int targetWindowId) { if (mWindowInfoById != null && mHasWatchOutsideTouchWindow) { final List<Integer> outsideWindowsId = new ArrayList<>(); final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); for (int i = 0; i < mWindowInfoById.size(); i++) { WindowInfo window = mWindowInfoById.valueAt(i); if (window.layer < targetWindow.layer && window.hasFlagWatchOutsideTouch) { outsideWindowsId.add(mWindowInfoById.keyAt(i)); } } return outsideWindowsId; } return Collections.emptyList(); } private AccessibilityWindowInfo getPictureInPictureWindow() { if (mWindows != null) { final int windowCount = mWindows.size(); Loading Loading
core/java/android/view/AccessibilityInteractionController.java +9 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.os.Message; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.text.style.AccessibilityClickableSpan; import android.text.style.ClickableSpan; import android.util.LongSparseArray; Loading Loading @@ -702,6 +703,14 @@ final class AccessibilityInteractionController { // 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) { Loading
core/java/android/view/WindowInfo.java +7 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ public class WindowInfo implements Parcelable { public CharSequence title; public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID; public boolean inPictureInPicture; public boolean hasFlagWatchOutsideTouch; private WindowInfo() { /* do nothing - hide constructor */ Loading @@ -74,6 +75,7 @@ public class WindowInfo implements Parcelable { window.title = other.title; window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor; window.inPictureInPicture = other.inPictureInPicture; window.hasFlagWatchOutsideTouch = other.hasFlagWatchOutsideTouch; if (other.childTokens != null && !other.childTokens.isEmpty()) { if (window.childTokens == null) { Loading Loading @@ -108,6 +110,7 @@ public class WindowInfo implements Parcelable { parcel.writeCharSequence(title); parcel.writeLong(accessibilityIdOfAnchor); parcel.writeInt(inPictureInPicture ? 1 : 0); parcel.writeInt(hasFlagWatchOutsideTouch ? 1 : 0); if (childTokens != null && !childTokens.isEmpty()) { parcel.writeInt(1); Loading @@ -130,6 +133,8 @@ public class WindowInfo implements Parcelable { builder.append(", focused=").append(focused); builder.append(", children=").append(childTokens); builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor); builder.append(", pictureInPicture=").append(inPictureInPicture); builder.append(", watchOutsideTouch=").append(hasFlagWatchOutsideTouch); builder.append(']'); return builder.toString(); } Loading @@ -145,6 +150,7 @@ public class WindowInfo implements Parcelable { title = parcel.readCharSequence(); accessibilityIdOfAnchor = parcel.readLong(); inPictureInPicture = (parcel.readInt() == 1); hasFlagWatchOutsideTouch = (parcel.readInt() == 1); final boolean hasChildren = (parcel.readInt() == 1); if (hasChildren) { Loading @@ -167,6 +173,7 @@ public class WindowInfo implements Parcelable { childTokens.clear(); } inPictureInPicture = false; hasFlagWatchOutsideTouch = false; } public static final Parcelable.Creator<WindowInfo> CREATOR = Loading
core/res/res/values/ids.xml +3 −0 Original line number Diff line number Diff line Loading @@ -172,4 +172,7 @@ <!-- 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" /> </resources>
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -218,6 +218,7 @@ <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" /> Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +57 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; Loading Loading @@ -2621,6 +2623,38 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return -1; } private void notifyOutsideTouchIfNeeded(int targetWindowId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int fetchFlags, int interrogatingPid, long interrogatingTid) { if (action != ACTION_CLICK && action != ACTION_LONG_CLICK) { return; } final List<Integer> outsideWindowsIds; final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>(); synchronized (mLock) { outsideWindowsIds = mSecurityPolicy.getWatchOutsideTouchWindowId(targetWindowId); for (int i = 0; i < outsideWindowsIds.size(); i++) { connectionList.add(getConnectionLocked(outsideWindowsIds.get(i))); } } for (int i = 0; i < connectionList.size(); i++) { final RemoteAccessibilityConnection connection = connectionList.get(i); if (connection != null) { try { connection.mConnection.performAccessibilityAction( AccessibilityNodeInfo.ROOT_ITEM_ID, R.id.accessibilityActionOutsideTouch, arguments, interactionId, callback, fetchFlags, interrogatingPid, interrogatingTid); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re); } } } } } @Override public void ensureWindowsAvailableTimed() { synchronized (mLock) { Loading Loading @@ -2700,6 +2734,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mPowerManager.userActivity(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); notifyOutsideTouchIfNeeded(resolvedWindowId, action, arguments, interactionId, callback, fetchFlags, interrogatingPid, interrogatingTid); if (activityToken != null) { LocalServices.getService(ActivityTaskManagerInternal.class) .setFocusedActivity(activityToken); Loading Loading @@ -3027,6 +3063,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; private boolean mTouchInteractionInProgress; private boolean mHasWatchOutsideTouchWindow; private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) { final int eventType = event.getEventType(); Loading Loading @@ -3184,6 +3221,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mWindowInfoById.valueAt(i).recycle(); } mWindowInfoById.clear(); mHasWatchOutsideTouchWindow = false; mFocusedWindowId = INVALID_WINDOW_ID; if (!mTouchInteractionInProgress) { Loading Loading @@ -3228,6 +3266,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub activeWindowGone = false; } } if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) { mHasWatchOutsideTouchWindow = true; } mWindows.add(window); mA11yWindowInfoById.put(windowId, window); mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo)); Loading Loading @@ -3646,6 +3687,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mWindowInfoById.get(windowId); } private List<Integer> getWatchOutsideTouchWindowId(int targetWindowId) { if (mWindowInfoById != null && mHasWatchOutsideTouchWindow) { final List<Integer> outsideWindowsId = new ArrayList<>(); final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); for (int i = 0; i < mWindowInfoById.size(); i++) { WindowInfo window = mWindowInfoById.valueAt(i); if (window.layer < targetWindow.layer && window.hasFlagWatchOutsideTouch) { outsideWindowsId.add(mWindowInfoById.keyAt(i)); } } return outsideWindowsId; } return Collections.emptyList(); } private AccessibilityWindowInfo getPictureInPictureWindow() { if (mWindows != null) { final int windowCount = mWindows.size(); Loading