Merge "Speed up structure update before OnFillRequest" into oc-dev
am: 6ab00addb1
Change-Id: Ia109ee243482ca0ac5622ec415ca5bfffba7d8e3
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user