Loading core/java/android/service/autofill/FillContext.java +88 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading Loading @@ -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 Loading services/autofill/java/com/android/server/autofill/Helper.java +0 −37 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } } services/autofill/java/com/android/server/autofill/Session.java +33 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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); Loading @@ -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; Loading Loading @@ -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; Loading Loading
core/java/android/service/autofill/FillContext.java +88 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading Loading @@ -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 Loading
services/autofill/java/com/android/server/autofill/Helper.java +0 −37 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } }
services/autofill/java/com/android/server/autofill/Session.java +33 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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); Loading @@ -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; Loading Loading @@ -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; Loading