From 1be88099749a3522b5c079b87fd7bd52d5d2a7c2 Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Wed, 5 Feb 2020 11:55:22 +0800 Subject: [PATCH] Do not prefetching accessibility nodes while a view is scrolling Accessibility framework default enable prefetching accessibility nodes in application, when an accessibility service is trying to query a node on the screen. These nodes are stored in the AccessibilityCache which is in the accessibility service to save the IPC between accessibility service and application on screen. This mechanism does not help performance in some scenarios. For example, an application with thousand nodes on screen and user needs to scroll the content frequently. In this case, the AccessibilityCache will be cleared frequently, and the prefetching behavior also happens frequently. It becomes an extra effort and drop the performance. In this patch, we stop prefetching for a little time while a scrolling event is detected. This can mitigate performance drop symptom while user is scrolling. Bug: 143730839 Test: Manual Change-Id: I336ee2907ba63c32c6e53259589817dfd14b99e1 --- .../AccessibilityInteractionClient.java | 73 +++++++++++++++++++ .../accessibility/AccessibilityNodeInfo.java | 3 + 2 files changed, 76 insertions(+) diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index b4c87953567b1..299ae2f0c55e9 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -31,7 +31,9 @@ import android.os.SystemClock; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; +import android.util.SparseLongArray; import android.view.Display; +import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -89,6 +91,9 @@ public final class AccessibilityInteractionClient private static final long TIMEOUT_INTERACTION_MILLIS = 5000; + private static final long DISABLE_PREFETCHING_FOR_SCROLLING_MILLIS = + (long) (ViewConfiguration.getSendRecurringAccessibilityEventsInterval() * 1.5); + private static final Object sStaticLock = new Object(); private static final LongSparseArray sClients = @@ -97,6 +102,10 @@ public final class AccessibilityInteractionClient private static final SparseArray sConnectionCache = new SparseArray<>(); + /** List of timestamps which indicate the latest time an a11y service receives a scroll event + from a window, mapping from windowId -> timestamp. */ + private static final SparseLongArray sScrollingWindows = new SparseLongArray(); + private static AccessibilityCache sAccessibilityCache = new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher()); @@ -422,6 +431,14 @@ public final class AccessibilityInteractionClient Log.i(LOG_TAG, "Node cache miss for " + idToString(accessibilityWindowId, accessibilityNodeId)); } + } else { + // No need to prefech nodes in bypass cache case. + prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK; + } + // Skip prefetching if window is scrolling. + if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0 + && isWindowScrolling(accessibilityWindowId)) { + prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK; } final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); @@ -718,6 +735,18 @@ public final class AccessibilityInteractionClient } public void onAccessibilityEvent(AccessibilityEvent event) { + switch (event.getEventType()) { + case AccessibilityEvent.TYPE_VIEW_SCROLLED: + updateScrollingWindow(event.getWindowId(), SystemClock.uptimeMillis()); + break; + case AccessibilityEvent.TYPE_WINDOWS_CHANGED: + if (event.getWindowChanges() == AccessibilityEvent.WINDOWS_CHANGE_REMOVED) { + deleteScrollingWindow(event.getWindowId()); + } + break; + default: + break; + } sAccessibilityCache.onAccessibilityEvent(event); } @@ -986,4 +1015,48 @@ public final class AccessibilityInteractionClient Log.e(LOG_TAG, disconnectedCount + " Disconnected nodes."); } } + + /** + * Update scroll event timestamp of a given window. + * + * @param windowId The window id. + * @param uptimeMillis Device uptime millis. + */ + private void updateScrollingWindow(int windowId, long uptimeMillis) { + synchronized (sScrollingWindows) { + sScrollingWindows.put(windowId, uptimeMillis); + } + } + + /** + * Remove a window from the scrolling windows list. + * + * @param windowId The window id. + */ + private void deleteScrollingWindow(int windowId) { + synchronized (sScrollingWindows) { + sScrollingWindows.delete(windowId); + } + } + + /** + * Whether or not the window is scrolling. + * + * @param windowId + * @return true if it's scrolling. + */ + private boolean isWindowScrolling(int windowId) { + synchronized (sScrollingWindows) { + final long latestScrollingTime = sScrollingWindows.get(windowId); + if (latestScrollingTime == 0) { + return false; + } + final long currentUptime = SystemClock.uptimeMillis(); + if (currentUptime > (latestScrollingTime + DISABLE_PREFETCHING_FOR_SCROLLING_MILLIS)) { + sScrollingWindows.delete(windowId); + return false; + } + } + return true; + } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index eb4f9db39e3e9..9f83862774ebc 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -137,6 +137,9 @@ public class AccessibilityNodeInfo implements Parcelable { /** @hide */ public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004; + /** @hide */ + public static final int FLAG_PREFETCH_MASK = 0x00000007; + /** @hide */ public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;