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

Commit 6ab00add authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Speed up structure update before OnFillRequest" into oc-dev

parents a9154ab3 22567d34
Loading
Loading
Loading
Loading
+88 −0
Original line number Diff line number Diff line
@@ -19,11 +19,19 @@ package android.service.autofill;
import static android.view.autofill.Helper.sDebug;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.SparseIntArray;
import android.view.autofill.AutofillId;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * This class represents a context for each fill request made via {@link
@@ -43,6 +51,13 @@ public final class FillContext implements Parcelable {
    private final int mRequestId;
    private final @NonNull AssistStructure mStructure;

    /**
     * Lookup table AutofillId->ViewNode to speed up {@link #findViewNodesByAutofillIds}
     * This is purely a cache and can be deleted at any time
     */
    @Nullable private ArrayMap<AutofillId, AssistStructure.ViewNode> mViewNodeLookupTable;


    /** @hide */
    public FillContext(int requestId, @NonNull AssistStructure structure) {
        mRequestId = requestId;
@@ -90,6 +105,79 @@ public final class FillContext implements Parcelable {
        parcel.writeParcelable(mStructure, flags);
    }

    /**
     * Finds {@link ViewNode}s that have the requested ids.
     *
     * @param ids The ids of the node to find
     *
     * @return The nodes indexed in the same way as the ids
     *
     * @hide
     */
    @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) {
        final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
        final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length];

        // Indexes of foundNodes that are not found yet
        final SparseIntArray missingNodeIndexes = new SparseIntArray(ids.length);

        for (int i = 0; i < ids.length; i++) {
            if (mViewNodeLookupTable != null) {
                int lookupTableIndex = mViewNodeLookupTable.indexOfKey(ids[i]);

                if (lookupTableIndex >= 0) {
                    foundNodes[i] = mViewNodeLookupTable.valueAt(lookupTableIndex);
                } else {
                    missingNodeIndexes.put(i, /* ignored */ 0);
                }
            } else {
                missingNodeIndexes.put(i, /* ignored */ 0);
            }
        }

        final int numWindowNodes = mStructure.getWindowNodeCount();
        for (int i = 0; i < numWindowNodes; i++) {
            nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode());
        }

        while (missingNodeIndexes.size() > 0 && !nodesToProcess.isEmpty()) {
            final ViewNode node = nodesToProcess.removeFirst();

            for (int i = 0; i < missingNodeIndexes.size(); i++) {
                final int index = missingNodeIndexes.keyAt(i);
                final AutofillId id = ids[index];

                if (id.equals(node.getAutofillId())) {
                    foundNodes[index] = node;

                    if (mViewNodeLookupTable == null) {
                        mViewNodeLookupTable = new ArrayMap<>(ids.length);
                    }

                    mViewNodeLookupTable.put(id, node);

                    missingNodeIndexes.removeAt(i);
                    break;
                }
            }

            for (int i = 0; i < node.getChildCount(); i++) {
                nodesToProcess.addLast(node.getChildAt(i));
            }
        }

        // Remember which ids could not be resolved to not search for them again the next time
        for (int i = 0; i < missingNodeIndexes.size(); i++) {
            if (mViewNodeLookupTable == null) {
                mViewNodeLookupTable = new ArrayMap<>(missingNodeIndexes.size());
            }

            mViewNodeLookupTable.put(ids[missingNodeIndexes.keyAt(i)], null);
        }

        return foundNodes;
    }

    public static final Parcelable.Creator<FillContext> CREATOR =
            new Parcelable.Creator<FillContext>() {
        @Override
+0 −37
Original line number Diff line number Diff line
@@ -16,11 +16,7 @@

package com.android.server.autofill;

import android.annotation.NonNull;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.os.Bundle;
import android.view.autofill.AutofillId;

import java.util.Arrays;
import java.util.Objects;
@@ -65,37 +61,4 @@ public final class Helper {
        append(builder, bundle);
        return builder.toString();
    }

    static ViewNode findViewNodeById(@NonNull AssistStructure structure, @NonNull AutofillId id) {
        final int size = structure.getWindowNodeCount();
        for (int i = 0; i < size; i++) {
            final AssistStructure.WindowNode window = structure.getWindowNodeAt(i);
            final ViewNode root = window.getRootViewNode();
            if (id.equals(root.getAutofillId())) {
                return root;
            }
            final ViewNode child = findViewNodeById(root, id);
            if (child != null) {
                return child;
            }
        }
        return null;
    }

    static ViewNode findViewNodeById(@NonNull ViewNode parent, @NonNull AutofillId id) {
        final int childrenSize = parent.getChildCount();
        if (childrenSize > 0) {
            for (int i = 0; i < childrenSize; i++) {
                final ViewNode child = parent.getChildAt(i);
                if (id.equals(child.getAutofillId())) {
                    return child;
                }
                final ViewNode grandChild = findViewNodeById(child, id);
                if (grandChild != null && id.equals(grandChild.getAutofillId())) {
                    return grandChild;
                }
            }
        }
        return null;
    }
}
+33 −16
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;

import static com.android.server.autofill.Helper.findViewNodeById;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.ViewState.STATE_AUTOFILLED;
@@ -79,7 +78,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;

/**
@@ -212,7 +210,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

                final int numContexts = mContexts.size();
                for (int i = 0; i < numContexts; i++) {
                    fillStructureWithAllowedValues(mContexts.get(i).getStructure(), flags);
                    fillContextWithAllowedValues(mContexts.get(i), flags);
                }

                request = new FillRequest(requestId, mContexts, mClientState, flags);
@@ -223,20 +221,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    };

    /**
     * Updates values of the nodes in the structure so that:
     * Returns the ids of all entries in {@link #mViewStates} in the same order.
     */
    private AutofillId[] getIdsOfAllViewStates() {
        final int numViewState = mViewStates.size();
        final AutofillId[] ids = new AutofillId[numViewState];
        for (int i = 0; i < numViewState; i++) {
            ids[i] = mViewStates.valueAt(i).id;
        }

        return ids;
    }

    /**
     * Updates values of the nodes in the context's structure so that:
     * - proper node is focused
     * - autofillValue is sent back to service when it was previously autofilled
     * - autofillValue is sent in the view used to force a request
     *
     * @param structure The structure to be filled
     * @param fillContext The context to be filled
     * @param flags The flags that started the session
     */
    private void fillStructureWithAllowedValues(@NonNull AssistStructure structure, int flags) {
        final int numViewStates = mViewStates.size();
        for (int i = 0; i < numViewStates; i++) {
    private void fillContextWithAllowedValues(@NonNull FillContext fillContext, int flags) {
        final ViewNode[] nodes = fillContext.findViewNodesByAutofillIds(getIdsOfAllViewStates());

        final int numViewState = mViewStates.size();
        for (int i = 0; i < numViewState; i++) {
            final ViewState viewState = mViewStates.valueAt(i);

            final ViewNode node = findViewNodeById(structure, viewState.id);
            final ViewNode node = nodes[i];
            if (node == null) {
                Slog.w(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id);
                continue;
@@ -841,19 +854,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

        final int numContexts = mContexts.size();

        for (int i = 0; i < numContexts; i++) {
            final FillContext context = mContexts.get(i);
        for (int contextNum = 0; contextNum < numContexts; contextNum++) {
            final FillContext context = mContexts.get(contextNum);

            final ViewNode[] nodes = context.findViewNodesByAutofillIds(getIdsOfAllViewStates());

            if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + context);

            for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
                final AutofillValue value = entry.getValue().getCurrentValue();
            for (int viewStateNum = 0; viewStateNum < mViewStates.size(); viewStateNum++) {
                final ViewState state = mViewStates.valueAt(viewStateNum);

                final AutofillId id = state.id;
                final AutofillValue value = state.getCurrentValue();
                if (value == null) {
                    if (sVerbose) Slog.v(TAG, "callSaveLocked(): skipping " + entry.getKey());
                    if (sVerbose) Slog.v(TAG, "callSaveLocked(): skipping " + id);
                    continue;
                }
                final AutofillId id = entry.getKey();
                final ViewNode node = findViewNodeById(context.getStructure(), id);
                final ViewNode node = nodes[viewStateNum];
                if (node == null) {
                    Slog.w(TAG, "callSaveLocked(): did not find node with id " + id);
                    continue;