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

Commit 6bb459d3 authored by Sally Yuen's avatar Sally Yuen Committed by Android (Google) Code Review
Browse files

Merge "Expose prefetching strategies to services"

parents 598ee846 026c7ac0
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -3068,6 +3068,7 @@ package android.accessibilityservice {
    method @Nullable public final android.accessibilityservice.InputMethod getInputMethod();
    method @NonNull public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
    method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(int);
    method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
    method @NonNull public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
    method @NonNull public final java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getSystemActions();
@@ -51620,6 +51621,7 @@ package android.view.accessibility {
    method @Deprecated public void getBoundsInParent(android.graphics.Rect);
    method public void getBoundsInScreen(android.graphics.Rect);
    method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getChild(int, int);
    method public int getChildCount();
    method public CharSequence getClassName();
    method public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo getCollectionInfo();
@@ -51639,6 +51641,7 @@ package android.view.accessibility {
    method public CharSequence getPackageName();
    method @Nullable public CharSequence getPaneTitle();
    method public android.view.accessibility.AccessibilityNodeInfo getParent();
    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getParent(int);
    method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
    method @Nullable public CharSequence getStateDescription();
    method public CharSequence getText();
@@ -51789,8 +51792,15 @@ package android.view.accessibility {
    field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
    field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
    field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
    field public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 8; // 0x8
    field public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 4; // 0x4
    field public static final int FLAG_PREFETCH_SIBLINGS = 2; // 0x2
    field public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 32; // 0x20
    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
    field public static final int FOCUS_INPUT = 1; // 0x1
    field public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50; // 0x32
    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
@@ -51955,6 +51965,7 @@ package android.view.accessibility {
    method public int getScrollX();
    method public int getScrollY();
    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getSource();
    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getSource(int);
    method @NonNull public java.util.List<java.lang.CharSequence> getText();
    method public int getToIndex();
    method public int getWindowId();
@@ -52012,6 +52023,7 @@ package android.view.accessibility {
    method public android.view.accessibility.AccessibilityWindowInfo getParent();
    method public void getRegionInScreen(@NonNull android.graphics.Region);
    method public android.view.accessibility.AccessibilityNodeInfo getRoot();
    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getRoot(int);
    method @Nullable public CharSequence getTitle();
    method public int getType();
    method public boolean isAccessibilityFocused();
+20 −9
Original line number Diff line number Diff line
@@ -1008,14 +1008,6 @@ public abstract class AccessibilityService extends Service {
     * is currently touching or the window with input focus, if the user is not
     * touching any window. It could be from any logical display.
     * <p>
     * The currently active window is defined as the window that most recently fired one
     * of the following events:
     * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
     * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
     * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
     * In other words, the last window shown that also has input focus.
     * </p>
     * <p>
     * <strong>Note:</strong> In order to access the root node your service has
     * to declare the capability to retrieve window content by setting the
     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
@@ -1023,10 +1015,29 @@ public abstract class AccessibilityService extends Service {
     * </p>
     *
     * @return The root node if this service can retrieve window content.
     * @see AccessibilityWindowInfo#isActive() for more explanation about the active window.
     */
    public AccessibilityNodeInfo getRootInActiveWindow() {
        return getRootInActiveWindow(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID);
    }

    /**
     * Gets the root node in the currently active window if this service
     * can retrieve window content. The active window is the one that the user
     * is currently touching or the window with input focus, if the user is not
     * touching any window. It could be from any logical display.
     *
     * @param prefetchingStrategy the prefetching strategy.
     * @return The root node if this service can retrieve window content.
     *
     * @see #getRootInActiveWindow()
     * @see AccessibilityNodeInfo#getParent(int) for a description of prefetching.
     */
    @Nullable
    public AccessibilityNodeInfo getRootInActiveWindow(
            @AccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy) {
        return AccessibilityInteractionClient.getInstance(this).getRootInActiveWindow(
                mConnectionId);
                mConnectionId, prefetchingStrategy);
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -733,7 +733,8 @@ public final class UiAutomation {
        }
        // Calling out without a lock held.
        return AccessibilityInteractionClient.getInstance()
                .getRootInActiveWindow(connectionId);
                .getRootInActiveWindow(connectionId,
                        AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID);
    }

    /**
+263 −36
Original line number Diff line number Diff line
@@ -52,9 +52,10 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -348,6 +349,11 @@ public final class AccessibilityInteractionController {

        View requestedView = null;
        AccessibilityNodeInfo requestedNode = null;
        boolean interruptPrefetch =
                ((flags & AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE) == 0);

        ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
        infos.clear();
        try {
            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
                return;
@@ -357,27 +363,46 @@ public final class AccessibilityInteractionController {
            if (requestedView != null && isShown(requestedView)) {
                requestedNode = populateAccessibilityNodeInfoForView(
                        requestedView, arguments, virtualDescendantId);
                mPrefetcher.mInterruptPrefetch = interruptPrefetch;
                mPrefetcher.mFetchFlags = flags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK;

                if (!interruptPrefetch) {
                    infos.add(requestedNode);
                    mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
                            requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
                            infos);
                    mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
                }
            }
        } finally {
            if (!interruptPrefetch) {
                // Return found node and prefetched nodes in one IPC.
                updateInfosForViewportAndReturnFindNodeResult(infos, callback, interactionId, spec,
                        interactiveRegion);

                final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
                        getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode,
                               infos, flags);
                if (satisfiedRequest != null) {
                    returnFindNodeResult(satisfiedRequest);
                }
                return;
            } else {
                // Return found node.
                updateInfoForViewportAndReturnFindNodeResult(
                    requestedNode == null ? null : AccessibilityNodeInfo.obtain(requestedNode),
                        requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
                        callback, interactionId, spec, interactiveRegion);
            }
        ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
        infos.clear();
        }
        mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
                requestedNode == null ? null : AccessibilityNodeInfo.obtain(requestedNode),
                virtualDescendantId, flags, infos);
                requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), infos);
        mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
        updateInfosForViewPort(infos, spec, interactiveRegion);
        final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
                getSatisfiedRequestInPrefetch(
                        requestedNode == null ? null : requestedNode, infos, flags);

        if (satisfiedRequest != null && satisfiedRequest.mSatisfiedRequestNode != requestedNode) {
            infos.remove(satisfiedRequest.mSatisfiedRequestNode);
        }
                getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, infos,
                        flags);

        // Return prefetch result separately.
        returnPrefetchResult(interactionId, infos, callback);

        if (satisfiedRequest != null) {
@@ -1077,6 +1102,11 @@ public final class AccessibilityInteractionController {
                }
            }
            mPendingFindNodeByIdMessages.clear();
            // Remove node from prefetched infos.
            if (satisfiedRequest != null && satisfiedRequest.mSatisfiedRequestNode
                    != requestedNode) {
                infos.remove(satisfiedRequest.mSatisfiedRequestNode);
            }
            return satisfiedRequest;
        }
    }
@@ -1149,45 +1179,76 @@ public final class AccessibilityInteractionController {
     */
    private class AccessibilityNodePrefetcher {

        private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;

        private final ArrayList<View> mTempViewList = new ArrayList<View>();
        private boolean mInterruptPrefetch;
        private int mFetchFlags;

        public void prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root,
                int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos) {
                List<AccessibilityNodeInfo> outInfos) {
            if (root == null) {
                return;
            }
            AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
            final boolean prefetchPredecessors =
                    isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS);
            if (provider == null) {
                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
                if (prefetchPredecessors) {
                    prefetchPredecessorsOfRealNode(view, outInfos);
                }
                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
                    prefetchSiblingsOfRealNode(view, outInfos);
                if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS)) {
                    prefetchSiblingsOfRealNode(view, outInfos, prefetchPredecessors);
                }
                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
                if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID)) {
                    prefetchDescendantsOfRealNode(view, outInfos);
                }
            } else {
                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
                if (prefetchPredecessors) {
                    prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
                }
                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
                    prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
                if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS)) {
                    prefetchSiblingsOfVirtualNode(root, view, provider, outInfos,
                            prefetchPredecessors);
                }
                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
                if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID)) {
                    prefetchDescendantsOfVirtualNode(root, provider, outInfos);
                }
            }
            if (isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST)
                    || isFlagSet(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST)) {
                if (shouldStopPrefetching(outInfos)) {
                    return;
                }
                PrefetchDeque<DequeNode> deque = new PrefetchDeque<>(
                        mFetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_MASK,
                        outInfos);
                addChildrenOfRoot(view, root, provider, deque);
                deque.performTraversalAndPrefetch();
            }
            if (ENFORCE_NODE_TREE_CONSISTENT) {
                enforceNodeTreeConsistent(root, outInfos);
            }
        }

        private boolean shouldStopPrefetching(List prefetchededInfos) {
            return mHandler.hasUserInteractiveMessagesWaiting()
                    || prefetchededInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE;
        private void addChildrenOfRoot(View root, AccessibilityNodeInfo rootInfo,
                AccessibilityNodeProvider rootProvider, PrefetchDeque deque) {
            DequeNode rootDequeNode;
            if (rootProvider == null) {
                rootDequeNode = new ViewNode(root);
            } else {
                rootDequeNode = new VirtualNode(
                        AccessibilityNodeProvider.HOST_VIEW_ID, rootProvider);
            }
            rootDequeNode.addChildren(rootInfo, deque);
        }

        private boolean isFlagSet(@AccessibilityNodeInfo.PrefetchingStrategy int strategy) {
            return (mFetchFlags & strategy) != 0;
        }

        public boolean shouldStopPrefetching(List prefetchedInfos) {
            return ((mHandler.hasUserInteractiveMessagesWaiting() && mInterruptPrefetch)
                    || prefetchedInfos.size()
                    >= AccessibilityNodeInfo.MAX_NUMBER_OF_PREFETCHED_NODES);
        }

        private void enforceNodeTreeConsistent(
@@ -1283,7 +1344,7 @@ public final class AccessibilityInteractionController {
        }

        private void prefetchSiblingsOfRealNode(View current,
                List<AccessibilityNodeInfo> outInfos) {
                List<AccessibilityNodeInfo> outInfos, boolean predecessorsPrefetched) {
            if (shouldStopPrefetching(outInfos)) {
                return;
            }
@@ -1293,6 +1354,13 @@ public final class AccessibilityInteractionController {
                ArrayList<View> children = mTempViewList;
                children.clear();
                try {
                    if (!predecessorsPrefetched) {
                        AccessibilityNodeInfo parentInfo =
                                ((ViewGroup) parent).createAccessibilityNodeInfo();
                        if (parentInfo != null) {
                            outInfos.add(parentInfo);
                        }
                    }
                    parentGroup.addChildrenForAccessibility(children);
                    final int childCount = children.size();
                    for (int i = 0; i < childCount; i++) {
@@ -1327,8 +1395,8 @@ public final class AccessibilityInteractionController {
            if (shouldStopPrefetching(outInfos) || !(root instanceof ViewGroup)) {
                return;
            }
            HashMap<View, AccessibilityNodeInfo> addedChildren =
                new HashMap<View, AccessibilityNodeInfo>();
            LinkedHashMap<View, AccessibilityNodeInfo> addedChildren =
                    new LinkedHashMap<View, AccessibilityNodeInfo>();
            ArrayList<View> children = mTempViewList;
            children.clear();
            try {
@@ -1414,7 +1482,8 @@ public final class AccessibilityInteractionController {
        }

        private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos,
                boolean predecessorsPrefetched) {
            final long parentNodeId = current.getParentNodeId();
            final int parentAccessibilityViewId =
                    AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
@@ -1425,6 +1494,9 @@ public final class AccessibilityInteractionController {
                final AccessibilityNodeInfo parent =
                        provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
                if (parent != null) {
                    if (!predecessorsPrefetched) {
                        outInfos.add(parent);
                    }
                    final int childCount = parent.getChildCount();
                    for (int i = 0; i < childCount; i++) {
                        if (shouldStopPrefetching(outInfos)) {
@@ -1443,7 +1515,7 @@ public final class AccessibilityInteractionController {
                    }
                }
            } else {
                prefetchSiblingsOfRealNode(providerHost, outInfos);
                prefetchSiblingsOfRealNode(providerHost, outInfos, predecessorsPrefetched);
            }
        }

@@ -1626,4 +1698,159 @@ public final class AccessibilityInteractionController {
            mSatisfiedRequestInteractionId = satisfiedRequestInteractionId;
        }
    }

    private class PrefetchDeque<E extends DequeNode>
            extends ArrayDeque<E> {
        int mStrategy;
        List<AccessibilityNodeInfo> mPrefetchOutput;

        PrefetchDeque(int strategy, List<AccessibilityNodeInfo> output) {
            mStrategy = strategy;
            mPrefetchOutput = output;
        }

        /** Performs depth-first or breadth-first traversal.
         *
         * For depth-first search, we iterate through the children in backwards order and push them
         * to the stack before taking from the head. For breadth-first search, we iterate through
         * the children in order and push them to the stack before taking from the tail.
         *
         * Depth-first search:  0 has children 0, 1, 2, 4. 1 has children 5 and 6.
         * Head         Tail
         * 1  2  3  4 ->  pop: 1 -> 5  6  2  3  4
         *
         * Breadth-first search
         * Head         Tail
         * 4  3  2  1 -> remove last: 1 -> 6  5  3  2
         *
         **/
        void performTraversalAndPrefetch() {
            try {
                while (!isEmpty()) {
                    E child = getNext();
                    AccessibilityNodeInfo childInfo = child.getA11yNodeInfo();
                    if (childInfo != null) {
                        mPrefetchOutput.add(childInfo);
                    }
                    if (mPrefetcher.shouldStopPrefetching(mPrefetchOutput)) {
                        return;
                    }
                    // Add children to deque.
                    child.addChildren(childInfo, this);
                }
            } finally {
                clear();
            }
        }

        E getNext() {
            if (isStack()) {
                return pop();
            }
            return removeLast();
        }

        boolean isStack() {
            return (mStrategy & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST) != 0;
        }
    }

    interface DequeNode {
        AccessibilityNodeInfo getA11yNodeInfo();
        void addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque);
    }

    private class ViewNode implements DequeNode {
        View mView;
        private final ArrayList<View> mTempViewList = new ArrayList<>();

        ViewNode(View view) {
            mView = view;
        }

        @Override
        public AccessibilityNodeInfo getA11yNodeInfo() {
            if (mView == null) {
                return null;
            }
            return mView.createAccessibilityNodeInfo();
        }

        @Override
        public void addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque) {
            if (mView == null) {
                return;
            }
            if (!(mView instanceof ViewGroup)) {
                return;
            }
            ArrayList<View> children = mTempViewList;
            children.clear();
            try {
                mView.addChildrenForAccessibility(children);
                final int childCount = children.size();

                if (deque.isStack()) {
                    for (int i = childCount - 1; i >= 0; i--) {
                        addChild(deque, children.get(i));
                    }
                } else {
                    for (int i = 0; i < childCount; i++) {
                        addChild(deque, children.get(i));
                    }
                }
            } finally {
                children.clear();
            }
        }

        private void addChild(ArrayDeque deque, View child) {
            if (isShown(child)) {
                AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
                if (provider == null) {
                    deque.push(new ViewNode(child));
                } else {
                    deque.push(new VirtualNode(AccessibilityNodeProvider.HOST_VIEW_ID,
                            provider));
                }
            }
        }
    }

    private class VirtualNode implements DequeNode {
        long mInfoId;
        AccessibilityNodeProvider mProvider;

        VirtualNode(long id, AccessibilityNodeProvider provider) {
            mInfoId = id;
            mProvider = provider;
        }
        @Override
        public AccessibilityNodeInfo getA11yNodeInfo() {
            if (mProvider == null) {
                return null;
            }
            return mProvider.createAccessibilityNodeInfo(
                    AccessibilityNodeInfo.getVirtualDescendantId(mInfoId));
        }

        @Override
        public void addChildren(AccessibilityNodeInfo virtualRoot, PrefetchDeque deque) {
            if (virtualRoot == null) {
                return;
            }
            final int childCount = virtualRoot.getChildCount();
            if (deque.isStack()) {
                for (int i = childCount - 1; i >= 0; i--) {
                    final long childNodeId = virtualRoot.getChildId(i);
                    deque.push(new VirtualNode(childNodeId, mProvider));
                }
            } else {
                for (int i = 0; i < childCount; i++) {
                    final long childNodeId = virtualRoot.getChildId(i);
                    deque.push(new VirtualNode(childNodeId, mProvider));
                }
            }
        }
    }
}
+49 −25

File changed.

Preview size limit exceeded, changes collapsed.

Loading