diff options
| author | 2022-04-13 18:10:46 +0800 | |
|---|---|---|
| committer | 2022-04-29 14:53:56 +0800 | |
| commit | 57a1da433ac379e47b4cfa31c9629bf850f12b8d (patch) | |
| tree | ea227cc6e48f247ea8bdd6a9acc8dc342282a058 | |
| parent | 5c554e289b3e87a35e89afad0334e6edd5661d3f (diff) | |
Postpone window_state_changed events until the window is added
ViewRootImpl sends window_state_changed event to notify
AccessibilityService that itself is visible on the screen. However,
the corresponding window is not available from accessibility framework
perspective because the window information is from SurfaceFlinger process
now, so the window is added only when it is really visible on the screen.
To ensure accessibilityService could get the node or the window when
receiving this event, we postpone this event until the corresponding
window is added. We also set a timeout to send those pending events.
Bug: 228442331
Bug: 226371995
Test: atest CtsAccessibilityServiceTestCases
manual test: write a sample AccessibilityService to test the apis
when receving window_state_changed events
Change-Id: I92cf2ddb9de90c050cf145f746a53f31d3a5df83
| -rw-r--r-- | services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java | 94 |
1 files changed, 91 insertions, 3 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index cbeb01a68778..4806514a0e13 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -187,6 +187,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // their capabilities are ready. private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000; + + // This postpones state changes events when a window doesn't exist with the expectation that + // a race condition will resolve. It is determined by observing elapsed time of the + // corresponding window added. + //TODO(b/230810909) : Fix it with a better idea. + private static final int POSTPONE_WINDOW_STATE_CHANGED_EVENT_TIMEOUT_MILLIS = 500; + private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE = "registerUiTestAutomationService"; @@ -272,6 +279,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final AccessibilityTraceManager mTraceManager; private final CaptioningManagerImpl mCaptioningManagerImpl; + private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables = + new ArrayList<>(); + private int mCurrentUserId = UserHandle.USER_SYSTEM; //TODO: Remove this hack @@ -930,11 +940,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final WindowManagerInternal wm = LocalServices.getService( WindowManagerInternal.class); wm.computeWindowsForAccessibility(displayId); + // The App side sends a event to notify that the window visible or focused, + // but the window information in framework is not updated yet, so we postpone it. + if (postponeWindowStateEvent(event)) { + return; + } } + synchronized (mLock) { - notifyAccessibilityServicesDelayedLocked(event, false); - notifyAccessibilityServicesDelayedLocked(event, true); - mUiAutomationManager.sendAccessibilityEventLocked(event); + dispatchAccessibilityEventLocked(event); } } @@ -943,6 +957,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private void dispatchAccessibilityEventLocked(AccessibilityEvent event) { + notifyAccessibilityServicesDelayedLocked(event, false); + notifyAccessibilityServicesDelayedLocked(event, true); + mUiAutomationManager.sendAccessibilityEventLocked(event); + } + private void sendAccessibilityEventToInputFilter(AccessibilityEvent event) { synchronized (mLock) { if (mHasInputFilter && mInputFilter != null) { @@ -3339,6 +3359,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) { + if (event.getWindowChanges() == AccessibilityEvent.WINDOWS_CHANGE_ADDED) { + // We need to ensure the window is available before sending pending + // window_state_changed events. + sendPendingWindowStateChangedEventsForAvailableWindowLocked(event.getWindowId()); + } sendAccessibilityEventLocked(event, mCurrentUserId); } @@ -4505,4 +4530,67 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } } + + private final class SendWindowStateChangedEventRunnable implements Runnable { + + private final AccessibilityEvent mPendingEvent; + private final int mWindowId; + + SendWindowStateChangedEventRunnable(@NonNull AccessibilityEvent event) { + mPendingEvent = event; + mWindowId = event.getWindowId(); + } + + @Override + public void run() { + synchronized (mLock) { + Slog.w(LOG_TAG, " wait for adding window timeout: " + mWindowId); + sendPendingEventLocked(); + } + } + + private void sendPendingEventLocked() { + mSendWindowStateChangedEventRunnables.remove(this); + dispatchAccessibilityEventLocked(mPendingEvent); + } + + private int getWindowId() { + return mWindowId; + } + } + + void sendPendingWindowStateChangedEventsForAvailableWindowLocked(int windowId) { + final int eventSize = mSendWindowStateChangedEventRunnables.size(); + for (int i = eventSize - 1; i >= 0; i--) { + final SendWindowStateChangedEventRunnable runnable = + mSendWindowStateChangedEventRunnables.get(i); + if (runnable.getWindowId() == windowId) { + mMainHandler.removeCallbacks(runnable); + runnable.sendPendingEventLocked(); + } + } + } + + /** + * Postpones the {@link AccessibilityEvent} with + * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} + * which doesn't have the corresponding window until the window is added or timeout. + * + * @return {@code true} if the event is postponed. + */ + private boolean postponeWindowStateEvent(AccessibilityEvent event) { + synchronized (mLock) { + final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked( + event.getWindowId()); + if (mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId) != null) { + return false; + } + final SendWindowStateChangedEventRunnable pendingRunnable = + new SendWindowStateChangedEventRunnable(new AccessibilityEvent(event)); + mMainHandler.postDelayed(pendingRunnable, + POSTPONE_WINDOW_STATE_CHANGED_EVENT_TIMEOUT_MILLIS); + mSendWindowStateChangedEventRunnables.add(pendingRunnable); + return true; + } + } } |