diff options
| author | 2017-04-21 16:33:52 -0700 | |
|---|---|---|
| committer | 2017-04-24 16:50:14 -0700 | |
| commit | 2f517c26d23393d4f0d6b3352de6e4c92c9e107e (patch) | |
| tree | 3d01fabb58b96e99eb53cda7a2a29bef8ca54fa2 | |
| parent | 85d1c2d2905362b984563d9b5e8332010c272fc5 (diff) | |
Take new autofill structure for each partition
Bug: 36481649
Test: CtsAutofillServiceTestCases (now with a test that has an autofill
session over two full screen fragments)
Change-Id: I55f2f6203f3bd5a7082b4ce90500d2c16a260c7d
6 files changed, 250 insertions, 198 deletions
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 503617803cf0..05404598e6a9 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -66,10 +66,6 @@ public abstract class AutofillService extends Service { */ public static final String SERVICE_META_DATA = "android.autofill"; - // Internal extras - /** @hide */ - public static final String EXTRA_SESSION_ID = "android.service.autofill.extra.SESSION_ID"; - // Handler messages. private static final int MSG_CONNECT = 1; private static final int MSG_DISCONNECT = 2; diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index aa6db4d092f2..49f348c79ef2 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -39,8 +39,6 @@ import java.util.concurrent.atomic.AtomicInteger; * @see AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback) */ public final class FillRequest implements Parcelable { - private static AtomicInteger sIdCounter = new AtomicInteger(); - /** * Indicates autofill was explicitly requested by the user. */ @@ -58,12 +56,6 @@ public final class FillRequest implements Parcelable { private final @NonNull AssistStructure mStructure; private final @Nullable Bundle mClientState; - /** @hide */ - public FillRequest(@NonNull AssistStructure structure, - @Nullable Bundle clientState, @RequestFlags int flags) { - this(sIdCounter.incrementAndGet(), structure, clientState, flags); - } - private FillRequest(@NonNull Parcel parcel) { mId = parcel.readInt(); mStructure = parcel.readParcelable(null); @@ -71,7 +63,8 @@ public final class FillRequest implements Parcelable { mFlags = parcel.readInt(); } - private FillRequest(int id, @NonNull AssistStructure structure, + /** @hide */ + public FillRequest(int id, @NonNull AssistStructure structure, @Nullable Bundle clientState, @RequestFlags int flags) { mId = id; mFlags = Preconditions.checkFlagsArgument(flags, FLAG_MANUAL_REQUEST); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index c2799304bf7a..d7a6b06b5844 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -16,9 +16,6 @@ package com.android.server.autofill; -import static android.service.autofill.AutofillService.EXTRA_SESSION_ID; -import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS; -import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE; import static android.view.autofill.AutofillManager.FLAG_START_SESSION; import static android.view.autofill.AutofillManager.NO_SESSION; @@ -27,10 +24,7 @@ import static com.android.server.autofill.Helper.VERBOSE; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Activity; -import android.app.ActivityManager; import android.app.AppGlobals; -import android.app.assist.AssistStructure; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -50,7 +44,6 @@ import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; import android.service.autofill.FillEventHistory; import android.service.autofill.FillEventHistory.Event; -import android.service.autofill.FillRequest; import android.service.autofill.FillResponse; import android.service.autofill.IAutoFillService; import android.text.TextUtils; @@ -66,7 +59,6 @@ import android.view.autofill.IAutoFillManagerClient; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.HandlerCaller; -import com.android.internal.os.IResultReceiver; import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; @@ -127,56 +119,6 @@ final class AutofillManagerServiceImpl { @GuardedBy("mLock") private FillEventHistory mEventHistory; - /** - * Receiver of assist data from the app's {@link Activity}. - */ - private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { - @Override - public void send(int resultCode, Bundle resultData) throws RemoteException { - if (VERBOSE) { - Slog.v(TAG, "resultCode on mAssistReceiver: " + resultCode); - } - - final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE); - if (structure == null) { - Slog.wtf(TAG, "no assist structure for id " + resultCode); - return; - } - - final Bundle receiverExtras = resultData.getBundle(KEY_RECEIVER_EXTRAS); - if (receiverExtras == null) { - Slog.wtf(TAG, "No " + KEY_RECEIVER_EXTRAS + " on receiver"); - return; - } - - final int sessionId = receiverExtras.getInt(EXTRA_SESSION_ID); - final Session session; - synchronized (mLock) { - session = mSessions.get(sessionId); - if (session == null) { - Slog.w(TAG, "no server session for " + sessionId); - return; - } - session.setStructureLocked(structure); - } - - - // TODO(b/35708678): Must fetch the data so it's available later on - // handleSave(), even if if the activity is gone by then, but structure.ensureData() - // gives a ONE_WAY warning because system_service could block on app calls. - // We need to change AssistStructure so it provides a "one-way" writeToParcel() - // method that sends all the data - structure.ensureData(); - - // Sanitize structure before it's sent to service. - structure.sanitizeForParceling(true); - - // This is the first request, hence there is no Bundle to be sent as clientState - final FillRequest request = new FillRequest(structure, null, session.mFlags); - session.mRemoteFillService.onFillRequest(request); - } - }; - AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, int userId, AutoFillUI ui, boolean disabled) { mContext = context; @@ -393,21 +335,6 @@ final class AutofillManagerServiceImpl { mInfo.getServiceInfo().getComponentName(), packageName); mSessions.put(newSession.id, newSession); - try { - final Bundle receiverExtras = new Bundle(); - receiverExtras.putInt(EXTRA_SESSION_ID, sessionId); - final long identity = Binder.clearCallingIdentity(); - try { - if (!ActivityManager.getService().requestAutofillData(mAssistReceiver, - receiverExtras, activityToken)) { - Slog.w(TAG, "failed to request autofill data for " + activityToken); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } catch (RemoteException e) { - // Should not happen, it's a local call. - } return newSession; } diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 17c30c561729..5964172e138a 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -16,7 +16,12 @@ package com.android.server.autofill; +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.view.autofill.AutofillId; import java.util.Arrays; import java.util.Objects; @@ -53,4 +58,37 @@ final class Helper { private Helper() { throw new UnsupportedOperationException("contains static members only"); } + + 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; + } } diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index 4d0f38042276..432a092a9874 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -134,6 +134,23 @@ final class RemoteFillService implements DeathRecipient { mCallbacks.onServiceDied(this); } + /** + * Cancel the currently pending request. + * + * <p>This can be used when the request is unnecessary or will be superceeded by a request that + * will soon be queued. + */ + public void cancelCurrentRequest() { + if (mDestroyed) { + return; + } + + if (mPendingRequest != null) { + mPendingRequest.cancel(); + mPendingRequest = null; + } + } + public void onFillRequest(@NonNull FillRequest request) { cancelScheduledUnbind(); final PendingFillRequest pendingRequest = new PendingFillRequest(request, this); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 5e8a974614a9..a00a397ece08 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -18,6 +18,8 @@ package com.android.server.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS; +import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE; import static android.view.autofill.AutofillManager.FLAG_START_SESSION; import static android.view.autofill.AutofillManager.FLAG_VALUE_CHANGED; import static android.view.autofill.AutofillManager.FLAG_VIEW_ENTERED; @@ -25,19 +27,22 @@ import static android.view.autofill.AutofillManager.FLAG_VIEW_EXITED; import static com.android.server.autofill.Helper.DEBUG; import static com.android.server.autofill.Helper.VERBOSE; +import static com.android.server.autofill.Helper.findViewNodeById; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Activity; +import android.app.ActivityManager; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.AutofillOverlay; import android.app.assist.AssistStructure.ViewNode; -import android.app.assist.AssistStructure.WindowNode; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; import android.metrics.LogMaker; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; @@ -64,14 +69,16 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.HandlerCaller; +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.ArrayUtils; import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; /** * A session for a given activity. @@ -89,6 +96,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState AutoFillUI.AutoFillUiCallback { private static final String TAG = "AutofillSession"; + private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID"; + private final AutofillManagerServiceImpl mService; private final HandlerCaller mHandlerCaller; private final Object mLock; @@ -96,6 +105,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final MetricsLogger mMetricsLogger = new MetricsLogger(); + private static AtomicInteger sIdCounter = new AtomicInteger(); + /** Id of the session */ public final int id; @@ -123,8 +134,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private IAutoFillManagerClient mClient; - @GuardedBy("mLock") - RemoteFillService mRemoteFillService; + private final RemoteFillService mRemoteFillService; @GuardedBy("mLock") private SparseArray<FillResponse> mResponses; @@ -163,7 +173,124 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Flags used to start the session. */ - int mFlags; + private final int mFlags; + + /** + * Receiver of assist data from the app's {@link Activity}. + */ + private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + if (VERBOSE) { + Slog.v(TAG, "resultCode on mAssistReceiver: " + resultCode); + } + + final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE); + if (structure == null) { + Slog.wtf(TAG, "no assist structure for id " + resultCode); + return; + } + + final Bundle receiverExtras = resultData.getBundle(KEY_RECEIVER_EXTRAS); + if (receiverExtras == null) { + Slog.wtf(TAG, "No " + KEY_RECEIVER_EXTRAS + " on receiver"); + return; + } + + final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID); + + if (DEBUG) { + Slog.d(TAG, "New structure for requestId " + requestId + ": " + structure); + } + + synchronized (mLock) { + // TODO(b/35708678): Must fetch the data so it's available later on handleSave(), + // even if if the activity is gone by then, but structure .ensureData() gives a + // ONE_WAY warning because system_service could block on app calls. We need to + // change AssistStructure so it provides a "one-way" writeToParcel() method that + // sends all the data + structure.ensureData(); + + // Sanitize structure before it's sent to service. + structure.sanitizeForParceling(true); + + mStructure = structure; + } + + fillStructureWithAllowedValues(mStructure); + + FillRequest request = new FillRequest(requestId, mStructure, mClientState, mFlags); + mRemoteFillService.onFillRequest(request); + } + }; + + /** + * Updates values of the nodes in the structure so that: + * - proper node is focused + * - autofillValue is sent back to service when it was previously autofilled + * + * @param structure The structure to be filled + */ + private void fillStructureWithAllowedValues(@NonNull AssistStructure structure) { + final int numViewStates = mViewStates.size(); + for (int i = 0; i < numViewStates; i++) { + final ViewState viewState = mViewStates.valueAt(i); + + final ViewNode node = findViewNodeById(structure, viewState.id); + if (node == null) { + if (DEBUG) { + Slog.w(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id); + } + continue; + } + + final AutofillValue initialValue = viewState.getInitialValue(); + final AutofillValue filledValue = viewState.getAutofilledValue(); + final AutofillOverlay overlay = new AutofillOverlay(); + if (filledValue != null && !filledValue.equals(initialValue)) { + overlay.value = filledValue; + } + if (mCurrentViewId != null) { + overlay.focused = mCurrentViewId.equals(viewState.id); + } + + node.setAutofillOverlay(overlay); + } + } + + /** + * Reads a new structure and then request a new fill response from the fill service. + */ + private void requestNewFillResponseLocked() { + final int requestId = sIdCounter.getAndIncrement(); + + if (DEBUG) { + Slog.d(TAG, "Requesting structure for requestId " + requestId); + } + + // If the focus changes very quickly before the first request is returned each focus change + // triggers a new partition and we end up with many duplicate partitions. This is + // enhanced as the focus change can be much faster than the taking of the assist structure. + // Hence remove the currently queued request and replace it with the one queued after the + // structure is taken. This causes only one fill request per bust of focus changes. + mRemoteFillService.cancelCurrentRequest(); + + try { + final Bundle receiverExtras = new Bundle(); + receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); + final long identity = Binder.clearCallingIdentity(); + try { + if (!ActivityManager.getService().requestAutofillData(mAssistReceiver, + receiverExtras, mActivityToken)) { + Slog.w(TAG, "failed to request autofill data for " + mActivityToken); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } catch (RemoteException e) { + // Should not happen, it's a local call. + } + } Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui, @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId, @@ -433,10 +560,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mHasCallback = hasIt; } - public void setStructureLocked(AssistStructure structure) { - mStructure = structure; - } - /** * Shows the save UI, when session can be saved. * @@ -575,7 +698,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState continue; } final AutofillId id = entry.getKey(); - final ViewNode node = findViewNodeByIdLocked(id); + final ViewNode node = findViewNodeById(mStructure, id); if (node == null) { Slog.w(TAG, "callSaveLocked(): did not find node with id " + id); continue; @@ -606,24 +729,61 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mRemoteFillService.onSaveRequest(saveRequest); } + /** + * Determines if a new partition should be started for an id. + * + * @param id The id of the view that is entered + * + * @return {@code true} iff a new partition should be started + */ + private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) { + if (mResponses == null) { + return true; + } + + final int numResponses = mResponses.size(); + for (int responseNum = 0; responseNum < numResponses; responseNum++) { + final FillResponse response = mResponses.valueAt(responseNum); + + if (ArrayUtils.contains(response.getIgnoredIds(), id)) { + return false; + } + + final SaveInfo saveInfo = response.getSaveInfo(); + if (saveInfo != null) { + if (ArrayUtils.contains(saveInfo.getOptionalIds(), id) + || ArrayUtils.contains(saveInfo.getRequiredIds(), id)) { + return false; + } + } + + final ArrayList<Dataset> datasets = response.getDatasets(); + if (datasets != null) { + final int numDatasets = datasets.size(); + + for (int dataSetNum = 0; dataSetNum < numDatasets; dataSetNum++) { + final ArrayList fields = datasets.get(dataSetNum).getFieldIds(); + + if (fields != null && fields.contains(id)) { + return false; + } + } + } + } + + return true; + } + void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) { ViewState viewState = mViewStates.get(id); if (viewState == null) { - if ((flags & (FLAG_START_SESSION | FLAG_VALUE_CHANGED)) != 0) { + if ((flags & (FLAG_START_SESSION | FLAG_VALUE_CHANGED | FLAG_VIEW_ENTERED)) != 0) { if (DEBUG) { Slog.d(TAG, "Creating viewState for " + id + " on " + getFlagAsString(flags)); } viewState = new ViewState(this, id, value, this, ViewState.STATE_INITIAL); mViewStates.put(id, viewState); - } else if (mStructure != null && (flags & FLAG_VIEW_ENTERED) != 0) { - if (isIgnoredLocked(id)) { - if (DEBUG) { - Slog.d(TAG, "Not starting partition for ignored view id " + id); - } - return; - } - viewState = startPartitionLocked(id, value); } else { if (VERBOSE) Slog.v(TAG, "Ignored " + getFlagAsString(flags) + " for " + id); return; @@ -635,6 +795,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mCurrentViewId = viewState.id; viewState.update(value, virtualBounds); viewState.setState(ViewState.STATE_STARTED_SESSION); + requestNewFillResponseLocked(); return; } @@ -664,6 +825,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if ((flags & FLAG_VIEW_ENTERED) != 0) { + if (shouldStartNewPartitionLocked(id)) { + // TODO(b/37424539): proper implementation + if (mResponseWaitingAuth != null && ((flags & FLAG_START_SESSION) == 0)) { + viewState.setState(ViewState.STATE_WAITING_RESPONSE_AUTH); + } else if ((flags & FLAG_START_SESSION) == 0){ + if (DEBUG) { + Slog.d(TAG, "Starting partition for view id " + viewState.id); + } + viewState.setState(ViewState.STATE_STARTED_PARTITION); + requestNewFillResponseLocked(); + } + } + // Remove the UI if the ViewState has changed. if (mCurrentViewId != viewState.id) { mUi.hideFillUi(mCurrentViewId != null ? mCurrentViewId : null); @@ -687,49 +861,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.w(TAG, "updateLocked(): unknown flags " + flags + ": " + getFlagAsString(flags)); } - private ViewState startPartitionLocked(AutofillId id, AutofillValue value) { - // TODO(b/37424539): proper implementation - if (mResponseWaitingAuth != null) { - final ViewState viewState = - new ViewState(this, id, value, this, ViewState.STATE_WAITING_RESPONSE_AUTH); - mViewStates.put(id, viewState); - return viewState; - } - if (DEBUG) { - Slog.d(TAG, "Starting partition for view id " + id); - } - final ViewState newViewState = - new ViewState(this, id, value, this,ViewState.STATE_STARTED_PARTITION); - mViewStates.put(id, newViewState); - - // Must update value of nodes so: - // - proper node is focused - // - autofillValue is sent back to service when it was previously autofilled - for (int i = 0; i < mViewStates.size(); i++) { - final ViewState viewState = mViewStates.valueAt(i); - - final ViewNode node = findViewNodeByIdLocked(viewState.id); - if (node == null) { - Slog.w(TAG, "startPartitionLocked(): no node for " + viewState.id); - continue; - } - - final AutofillValue initialValue = viewState.getInitialValue(); - final AutofillValue filledValue = viewState.getAutofilledValue(); - final AutofillOverlay overlay = new AutofillOverlay(); - if (filledValue != null && !filledValue.equals(initialValue)) { - overlay.value = filledValue; - } - overlay.focused = id.equals(viewState.id); - node.setAutofillOverlay(overlay); - } - - FillRequest request = new FillRequest(mStructure, mClientState, 0); - mRemoteFillService.onFillRequest(request); - - return newViewState; - } - @Override public void onFillReady(FillResponse response, AutofillId filledId, @Nullable AutofillValue value) { @@ -949,23 +1080,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private boolean isIgnoredLocked(@NonNull AutofillId id) { - if (mResponses == null) return false; - - for (int i = mResponses.size() - 1; i >= 0; i--) { - final FillResponse response = mResponses.valueAt(i); - final AutofillId[] ignoredIds = response.getIgnoredIds(); - if (ignoredIds == null) continue; - for (int j = 0; j < ignoredIds.length; j++) { - final AutofillId ignoredId = ignoredIds[j]; - if (ignoredId != null && ignoredId.equals(id)) { - return true; - } - } - } - return false; - } - void dumpLocked(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.println(id); pw.print(prefix); pw.print("uid: "); pw.println(uid); @@ -1018,39 +1132,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private ViewNode findViewNodeByIdLocked(AutofillId id) { - final int size = mStructure.getWindowNodeCount(); - for (int i = 0; i < size; i++) { - final WindowNode window = mStructure.getWindowNodeAt(i); - final ViewNode root = window.getRootViewNode(); - if (id.equals(root.getAutofillId())) { - return root; - } - final ViewNode child = findViewNodeByIdLocked(root, id); - if (child != null) { - return child; - } - } - return null; - } - - private ViewNode findViewNodeByIdLocked(ViewNode parent, 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 = findViewNodeByIdLocked(child, id); - if (grandChild != null && id.equals(grandChild.getAutofillId())) { - return grandChild; - } - } - } - return null; - } - void destroyLocked() { mRemoteFillService.destroy(); mUi.setCallback(null); |