Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e135e2e4 authored by Rhed Jao's avatar Rhed Jao
Browse files

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
Merged-in: I336ee2907ba63c32c6e53259589817dfd14b99e1
(cherry picked from commit 1be88099)
parent 30ac1ffe
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
@@ -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<AccessibilityInteractionClient> sClients =
@@ -97,6 +102,10 @@ public final class AccessibilityInteractionClient
    private static final SparseArray<IAccessibilityServiceConnection> 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;
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -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;