Session.java revision 6fa8a07143d1984ff42750079cf596a868644663
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package com.android.server.autofill;
19
20import static android.view.autofill.AutofillManager.FLAG_MANUAL_REQUEST;
21import static android.view.autofill.AutofillManager.FLAG_START_SESSION;
22import static android.view.autofill.AutofillManager.FLAG_VALUE_CHANGED;
23import static android.view.autofill.AutofillManager.FLAG_VIEW_ENTERED;
24import static android.view.autofill.AutofillManager.FLAG_VIEW_EXITED;
25
26import static com.android.server.autofill.Helper.DEBUG;
27import static com.android.server.autofill.Helper.VERBOSE;
28import static com.android.server.autofill.Helper.findValue;
29
30import android.annotation.NonNull;
31import android.annotation.Nullable;
32import android.app.assist.AssistStructure;
33import android.app.assist.AssistStructure.ViewNode;
34import android.app.assist.AssistStructure.WindowNode;
35import android.content.ComponentName;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentSender;
39import android.graphics.Rect;
40import android.metrics.LogMaker;
41import android.os.Binder;
42import android.os.Bundle;
43import android.os.IBinder;
44import android.os.Parcelable;
45import android.os.RemoteException;
46import android.provider.Settings;
47import android.service.autofill.AutofillService;
48import android.service.autofill.Dataset;
49import android.service.autofill.FillResponse;
50import android.service.autofill.SaveInfo;
51import android.util.ArrayMap;
52import android.util.Slog;
53import android.view.autofill.AutofillId;
54import android.view.autofill.AutofillManager;
55import android.view.autofill.AutofillValue;
56import android.view.autofill.IAutoFillManagerClient;
57import android.view.autofill.IAutofillWindowPresenter;
58
59import com.android.internal.annotations.GuardedBy;
60import com.android.internal.logging.MetricsLogger;
61import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
62import com.android.internal.os.HandlerCaller;
63import com.android.server.autofill.ui.AutoFillUI;
64
65import java.io.PrintWriter;
66import java.util.Map;
67import java.util.Map.Entry;
68
69/**
70 * A session for a given activity.
71 *
72 * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track
73 * of the current {@link ViewState} to display the appropriate UI.
74 *
75 * <p>Although the autofill requests and callbacks are stateless from the service's point of
76 * view, we need to keep state in the framework side for cases such as authentication. For
77 * example, when service return a {@link FillResponse} that contains all the fields needed
78 * to fill the activity but it requires authentication first, that response need to be held
79 * until the user authenticates or it times out.
80 */
81// TODO(b/33197203): make sure sessions are removed (and tested by CTS):
82// - On all authentication scenarios.
83// - When user does not interact back after a while.
84// - When service is unbound.
85final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
86        AutoFillUI.AutoFillUiCallback {
87    private static final String TAG = "AutofillSession";
88
89    private final AutofillManagerServiceImpl mService;
90    private final IBinder mActivityToken;
91    private final IBinder mWindowToken;
92    private final HandlerCaller mHandlerCaller;
93    private final Object mLock;
94    private final AutoFillUI mUi;
95
96    private final MetricsLogger mMetricsLogger = new MetricsLogger();
97
98    /** Package name of the app that is auto-filled */
99    @NonNull private final String mPackageName;
100
101    @GuardedBy("mLock")
102    private final Map<AutofillId, ViewState> mViewStates = new ArrayMap<>();
103
104    @GuardedBy("mLock")
105    @Nullable
106    private ViewState mCurrentViewState;
107
108    private final IAutoFillManagerClient mClient;
109
110    @GuardedBy("mLock")
111    RemoteFillService mRemoteFillService;
112
113    // TODO(b/33197203): Get a response per view instead of per activity.
114    @GuardedBy("mLock")
115    private FillResponse mCurrentResponse;
116
117    /**
118     * Used to remember which {@link Dataset} filled the session.
119     */
120    // TODO(b/33197203): might need more than one once we support partitions
121    @GuardedBy("mLock")
122    private Dataset mAutoFilledDataset;
123
124    /**
125     * Assist structure sent by the app; it will be updated (sanitized, change values for save)
126     * before sent to {@link AutofillService}.
127     */
128    @GuardedBy("mLock") AssistStructure mStructure;
129
130    /**
131     * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}.
132     */
133    private boolean mHasCallback;
134
135    /**
136     * Flags used to start the session.
137     */
138    int mFlags;
139
140    Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
141            @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
142            @NonNull Object lock, @NonNull IBinder activityToken,
143            @Nullable IBinder windowToken, @NonNull IBinder client, boolean hasCallback,
144            int flags, @NonNull ComponentName componentName, @NonNull String packageName) {
145        mService = service;
146        mLock = lock;
147        mUi = ui;
148        mHandlerCaller = handlerCaller;
149        mRemoteFillService = new RemoteFillService(context, componentName, userId, this);
150        mActivityToken = activityToken;
151        mWindowToken = windowToken;
152        mHasCallback = hasCallback;
153        mPackageName = packageName;
154        mFlags = flags;
155
156        mClient = IAutoFillManagerClient.Stub.asInterface(client);
157        try {
158            client.linkToDeath(() -> {
159                if (DEBUG) {
160                    Slog.d(TAG, "app binder died");
161                }
162
163                removeSelf();
164            }, 0);
165        } catch (RemoteException e) {
166            Slog.w(TAG, "linkToDeath() on mClient failed: " + e);
167        }
168
169        mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName);
170    }
171
172    // FillServiceCallbacks
173    @Override
174    public void onFillRequestSuccess(@Nullable FillResponse response,
175            @NonNull String servicePackageName) {
176        if (response == null) {
177            // Nothing to be done, but need to notify client.
178            notifyUnavailableToClient();
179            removeSelf();
180            return;
181        }
182
183        if ((response.getDatasets() == null || response.getDatasets().isEmpty())
184                        && response.getAuthentication() == null) {
185            // Response is "empty" from an UI point of view, need to notify client.
186            notifyUnavailableToClient();
187        }
188        synchronized (mLock) {
189            processResponseLocked(response);
190        }
191
192        LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
193                .setType(MetricsEvent.TYPE_SUCCESS)
194                .setPackageName(mPackageName)
195                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
196                        response.getDatasets() == null ? 0 : response.getDatasets().size())
197                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE,
198                        servicePackageName);
199        mMetricsLogger.write(log);
200    }
201
202    // FillServiceCallbacks
203    @Override
204    public void onFillRequestFailure(@Nullable CharSequence message,
205            @NonNull String servicePackageName) {
206        LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
207                .setType(MetricsEvent.TYPE_FAILURE)
208                .setPackageName(mPackageName)
209                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
210        mMetricsLogger.write(log);
211
212        getUiForShowing().showError(message);
213        removeSelf();
214    }
215
216    // FillServiceCallbacks
217    @Override
218    public void onSaveRequestSuccess(@NonNull String servicePackageName) {
219        LogMaker log = (new LogMaker(
220                MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
221                .setType(MetricsEvent.TYPE_SUCCESS)
222                .setPackageName(mPackageName)
223                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
224        mMetricsLogger.write(log);
225
226        // Nothing left to do...
227        removeSelf();
228    }
229
230    // FillServiceCallbacks
231    @Override
232    public void onSaveRequestFailure(@Nullable CharSequence message,
233            @NonNull String servicePackageName) {
234        LogMaker log = (new LogMaker(
235                MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
236                .setType(MetricsEvent.TYPE_FAILURE)
237                .setPackageName(mPackageName)
238                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
239        mMetricsLogger.write(log);
240
241        getUiForShowing().showError(message);
242        removeSelf();
243    }
244
245    // FillServiceCallbacks
246    @Override
247    public void authenticate(IntentSender intent) {
248        final Intent fillInIntent;
249        synchronized (mLock) {
250            fillInIntent = createAuthFillInIntent(mStructure);
251        }
252        mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent));
253    }
254
255    // FillServiceCallbacks
256    @Override
257    public void onDisableSelf() {
258        mService.disableSelf();
259        synchronized (mLock) {
260            removeSelfLocked();
261        }
262    }
263
264    // FillServiceCallbacks
265    @Override
266    public void onServiceDied(RemoteFillService service) {
267        // TODO(b/33197203): implement
268    }
269
270    // AutoFillUiCallback
271    @Override
272    public void fill(Dataset dataset) {
273        mHandlerCaller.getHandler().post(() -> autoFill(dataset));
274    }
275
276    // AutoFillUiCallback
277    @Override
278    public void save() {
279        mHandlerCaller.getHandler()
280                .obtainMessage(AutofillManagerServiceImpl.MSG_SERVICE_SAVE, mActivityToken)
281                .sendToTarget();
282    }
283
284    // AutoFillUiCallback
285    @Override
286    public void cancelSave() {
287        mHandlerCaller.getHandler().post(() -> removeSelf());
288    }
289
290    // AutoFillUiCallback
291    @Override
292    public void requestShowFillUi(AutofillId id, int width, int height,
293            IAutofillWindowPresenter presenter) {
294        try {
295            mClient.requestShowFillUi(mWindowToken, id, width, height,
296                    mCurrentViewState.mVirtualBounds, presenter);
297        } catch (RemoteException e) {
298            Slog.e(TAG, "Error requesting to show fill UI", e);
299        }
300    }
301
302    // AutoFillUiCallback
303    @Override
304    public void requestHideFillUi(AutofillId id) {
305        try {
306            mClient.requestHideFillUi(mWindowToken, id);
307        } catch (RemoteException e) {
308            Slog.e(TAG, "Error requesting to hide fill UI", e);
309        }
310    }
311
312    public void setAuthenticationResultLocked(Bundle data) {
313        if (mCurrentResponse == null || data == null) {
314            removeSelf();
315        } else {
316            Parcelable result = data.getParcelable(
317                    AutofillManager.EXTRA_AUTHENTICATION_RESULT);
318            if (result instanceof FillResponse) {
319                mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName);
320
321                mCurrentResponse = (FillResponse) result;
322                processResponseLocked(mCurrentResponse);
323            } else if (result instanceof Dataset) {
324                Dataset dataset = (Dataset) result;
325                final int index = mCurrentResponse.getDatasets().indexOf(mAutoFilledDataset);
326                if (index >= 0) {
327                    mCurrentResponse.getDatasets().set(index, dataset);
328                    autoFill(dataset);
329                }
330            }
331        }
332    }
333
334    public void setHasCallback(boolean hasIt) {
335        mHasCallback = hasIt;
336    }
337
338    /**
339     * Shows the save UI, when session can be saved.
340     *
341     * @return {@code true} if session is done, or {@code false} if it's pending user action.
342     */
343    public boolean showSaveLocked() {
344        if (mStructure == null) {
345            Slog.wtf(TAG, "showSaveLocked(): no mStructure");
346            return true;
347        }
348        if (mCurrentResponse == null) {
349            // Happens when the activity / session was finished before the service replied, or
350            // when the service cannot autofill it (and returned a null response).
351            if (DEBUG) {
352                Slog.d(TAG, "showSaveLocked(): no mCurrentResponse");
353            }
354            return true;
355        }
356        final SaveInfo saveInfo = mCurrentResponse.getSaveInfo();
357        if (DEBUG) {
358            Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo);
359        }
360
361        /*
362         * The Save dialog is only shown if all conditions below are met:
363         *
364         * - saveInfo is not null
365         * - autofillValue of all required ids is not null
366         * - autofillValue of at least one id (required or optional) has changed.
367         */
368
369        if (saveInfo == null) {
370            return true;
371        }
372
373        final AutofillId[] requiredIds = saveInfo.getRequiredIds();
374        if (requiredIds == null || requiredIds.length == 0) {
375            Slog.w(TAG, "showSaveLocked(): no required ids on saveInfo");
376            return true;
377        }
378
379        boolean allRequiredAreNotEmpty = true;
380        boolean atLeastOneChanged = false;
381        for (int i = 0; i < requiredIds.length; i++) {
382            final AutofillId id = requiredIds[i];
383            final ViewState state = mViewStates.get(id);
384            if (state == null || state.mAutofillValue == null
385                     || state.mAutofillValue.isEmpty()) {
386                final ViewNode node = findViewNodeByIdLocked(id);
387                if (node == null) {
388                    Slog.w(TAG, "Service passed invalid id on SavableInfo: " + id);
389                    allRequiredAreNotEmpty = false;
390                    break;
391                }
392                final AutofillValue initialValue = node.getAutofillValue();
393                if (initialValue == null || initialValue.isEmpty()) {
394                    if (DEBUG) {
395                        Slog.d(TAG, "finishSessionLocked(): empty initial value for " + id );
396                    }
397                    allRequiredAreNotEmpty = false;
398                    break;
399                }
400            }
401            if (state.mValueUpdated) {
402                final AutofillValue filledValue = findValue(mAutoFilledDataset, id);
403                if (!state.mAutofillValue.equals(filledValue)) {
404                    if (DEBUG) {
405                        Slog.d(TAG, "finishSessionLocked(): found a change on " + id + ": "
406                                + filledValue + " => " + state.mAutofillValue);
407                    }
408                    atLeastOneChanged = true;
409                }
410            } else {
411                if (state.mAutofillValue == null || state.mAutofillValue.isEmpty()) {
412                    if (DEBUG) {
413                        Slog.d(TAG, "finishSessionLocked(): empty value for " + id + ": "
414                                + state.mAutofillValue);
415                    }
416                    allRequiredAreNotEmpty = false;
417                    break;
418
419                }
420            }
421        }
422
423        if (allRequiredAreNotEmpty) {
424            if (!atLeastOneChanged && saveInfo.getOptionalIds() != null) {
425                for (int i = 0; i < saveInfo.getOptionalIds().length; i++) {
426                    final AutofillId id = saveInfo.getOptionalIds()[i];
427                    final ViewState state = mViewStates.get(id);
428                    if (state != null && state.mAutofillValue != null && state.mValueUpdated) {
429                        final AutofillValue filledValue = findValue(mAutoFilledDataset, id);
430                        if (!state.mAutofillValue.equals(filledValue)) {
431                            if (DEBUG) {
432                                Slog.d(TAG, "finishSessionLocked(): found a change on optional "
433                                        + id + ": " + filledValue + " => "
434                                        + state.mAutofillValue);
435                            }
436                            atLeastOneChanged = true;
437                            break;
438                        }
439                    }
440                }
441            }
442            if (atLeastOneChanged) {
443                getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName);
444                return false;
445            }
446        }
447        // Nothing changed...
448        if (DEBUG) {
449            Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities."
450                    + "allRequiredAreNotNull=" + allRequiredAreNotEmpty
451                    + ", atLeastOneChanged=" + atLeastOneChanged);
452        }
453        return true;
454    }
455
456    /**
457     * Calls service when user requested save.
458     */
459    void callSaveLocked() {
460        if (DEBUG) {
461            Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates);
462        }
463
464        final Bundle extras = this.mCurrentResponse.getExtras();
465
466        for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
467            final AutofillValue value = entry.getValue().mAutofillValue;
468            if (value == null) {
469                if (VERBOSE) {
470                    Slog.v(TAG, "callSaveLocked(): skipping " + entry.getKey());
471                }
472                continue;
473            }
474            final AutofillId id = entry.getKey();
475            final ViewNode node = findViewNodeByIdLocked(id);
476            if (node == null) {
477                Slog.w(TAG, "callSaveLocked(): did not find node with id " + id);
478                continue;
479            }
480            if (VERBOSE) {
481                Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
482            }
483
484            node.updateAutofillValue(value);
485        }
486
487        // Sanitize structure before it's sent to service.
488        mStructure.sanitizeForParceling(false);
489
490        if (VERBOSE) {
491            Slog.v(TAG, "Dumping " + mStructure + " before calling service.save()");
492            mStructure.dump();
493        }
494
495        mRemoteFillService.onSaveRequest(mStructure, extras);
496    }
497
498    void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) {
499        if (mAutoFilledDataset != null && (flags & FLAG_VALUE_CHANGED) == 0) {
500            // TODO(b/33197203): ignoring because we don't support partitions yet
501            Slog.d(TAG, "updateLocked(): ignoring " + flags + " after app was autofilled");
502            return;
503        }
504
505        ViewState viewState = mViewStates.get(id);
506        if (viewState == null) {
507            viewState = new ViewState(this, id, this);
508            mViewStates.put(id, viewState);
509        }
510
511        if ((flags & FLAG_START_SESSION) != 0) {
512            // View is triggering autofill.
513            mCurrentViewState = viewState;
514            viewState.update(value, virtualBounds);
515            return;
516        }
517
518        if ((flags & FLAG_VALUE_CHANGED) != 0) {
519            if (value != null && !value.equals(viewState.mAutofillValue)) {
520                viewState.mValueUpdated = true;
521
522                // Must check if this update was caused by autofilling the view, in which
523                // case we just update the value, but not the UI.
524                if (mAutoFilledDataset != null) {
525                    final AutofillValue filledValue = findValue(mAutoFilledDataset, id);
526                    if (value.equals(filledValue)) {
527                        viewState.mAutofillValue = value;
528                        return;
529                    }
530                }
531
532                // Change value
533                viewState.mAutofillValue = value;
534
535                // Update the chooser UI
536                if (value.isText()) {
537                    getUiForShowing().filterFillUi(value.getTextValue().toString());
538                } else {
539                    getUiForShowing().filterFillUi(null);
540                }
541            }
542
543            return;
544        }
545
546        if ((flags & FLAG_VIEW_ENTERED) != 0) {
547            // Remove the UI if the ViewState has changed.
548            if (mCurrentViewState != viewState) {
549                mUi.hideFillUi(mCurrentViewState != null ? mCurrentViewState.mId : null);
550                mCurrentViewState = viewState;
551            }
552
553            // If the ViewState is ready to be displayed, onReady() will be called.
554            viewState.update(value, virtualBounds);
555
556            // TODO(b/33197203): Remove when there is a response per activity.
557            if (mCurrentResponse != null) {
558                viewState.setResponse(mCurrentResponse);
559            }
560
561            return;
562        }
563
564        if ((flags & FLAG_VIEW_EXITED) != 0) {
565            if (mCurrentViewState == viewState) {
566                mUi.hideFillUi(viewState.mId);
567                mCurrentViewState = null;
568            }
569            return;
570        }
571
572        Slog.w(TAG, "updateLocked(): unknown flags " + flags);
573    }
574
575    @Override
576    public void onFillReady(FillResponse response, AutofillId filledId,
577            @Nullable AutofillValue value) {
578        String filterText = null;
579        if (value != null && value.isText()) {
580            filterText = value.getTextValue().toString();
581        }
582
583        getUiForShowing().showFillUi(filledId, response, filterText, mPackageName);
584    }
585
586    private void notifyUnavailableToClient() {
587        if (mCurrentViewState == null) {
588            // TODO(b/33197203): temporary sanity check; should never happen
589            Slog.w(TAG, "notifyUnavailable(): mCurrentViewState is null");
590            return;
591        }
592        if (!mHasCallback) return;
593        try {
594            mClient.notifyNoFillUi(mWindowToken, mCurrentViewState.mId);
595        } catch (RemoteException e) {
596            Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken
597                    + " id=" + mCurrentViewState.mId, e);
598        }
599    }
600
601    private void processResponseLocked(FillResponse response) {
602        if (DEBUG) {
603            Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication()
604                + "):" + response);
605        }
606
607        if (mCurrentViewState == null) {
608            // TODO(b/33197203): temporary sanity check; should never happen
609            Slog.w(TAG, "processResponseLocked(): mCurrentViewState is null");
610            return;
611        }
612
613        mCurrentResponse = response;
614
615        if (mCurrentResponse.getAuthentication() != null) {
616            // Handle authentication.
617            final Intent fillInIntent = createAuthFillInIntent(mStructure);
618            mCurrentViewState.setResponse(mCurrentResponse, fillInIntent);
619            return;
620        }
621
622        if ((mFlags & FLAG_MANUAL_REQUEST) != 0 && response.getDatasets() != null
623                && response.getDatasets().size() == 1) {
624            Slog.d(TAG, "autofilling manual request directly");
625            autoFill(response.getDatasets().get(0));
626            return;
627        }
628
629        mCurrentViewState.setResponse(mCurrentResponse);
630    }
631
632    void autoFill(Dataset dataset) {
633        synchronized (mLock) {
634            mAutoFilledDataset = dataset;
635
636            // Autofill it directly...
637            if (dataset.getAuthentication() == null) {
638                autoFillApp(dataset);
639                return;
640            }
641
642            // ...or handle authentication.
643            Intent fillInIntent = createAuthFillInIntent(mStructure);
644            startAuthentication(dataset.getAuthentication(), fillInIntent);
645        }
646    }
647
648    CharSequence getServiceName() {
649        return mService.getServiceName();
650    }
651
652    private Intent createAuthFillInIntent(AssistStructure structure) {
653        Intent fillInIntent = new Intent();
654        fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure);
655        return fillInIntent;
656    }
657
658    private void startAuthentication(IntentSender intent, Intent fillInIntent) {
659        try {
660            mClient.authenticate(intent, fillInIntent);
661        } catch (RemoteException e) {
662            Slog.e(TAG, "Error launching auth intent", e);
663        }
664    }
665
666    void dumpLocked(String prefix, PrintWriter pw) {
667        pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
668        pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags);
669        pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse);
670        pw.print(prefix); pw.print("mAutoFilledDataset: "); pw.println(mAutoFilledDataset);
671        pw.print(prefix); pw.print("mCurrentViewStates: "); pw.println(mCurrentViewState);
672        pw.print(prefix); pw.print("mViewStates: "); pw.println(mViewStates.size());
673        final String prefix2 = prefix + "  ";
674        for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
675            pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey());
676            entry.getValue().dump(prefix2, pw);
677        }
678        if (VERBOSE) {
679            pw.print(prefix); pw.print("mStructure: " );
680            // TODO(b/33197203): add method do dump AssistStructure on pw
681            if (mStructure != null) {
682                pw.println("look at logcat" );
683                mStructure.dump(); // dumps to logcat
684            } else {
685                pw.println("null");
686            }
687        }
688        pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
689        mRemoteFillService.dump(prefix, pw);
690    }
691
692    void autoFillApp(Dataset dataset) {
693        synchronized (mLock) {
694            try {
695                if (DEBUG) {
696                    Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
697                }
698                mClient.autofill(mWindowToken, dataset.getFieldIds(), dataset.getFieldValues());
699            } catch (RemoteException e) {
700                Slog.w(TAG, "Error autofilling activity: " + e);
701            }
702        }
703    }
704
705    private AutoFillUI getUiForShowing() {
706        synchronized (mLock) {
707            mUi.setCallback(this);
708            return mUi;
709        }
710    }
711
712    private ViewNode findViewNodeByIdLocked(AutofillId id) {
713        final int size = mStructure.getWindowNodeCount();
714        for (int i = 0; i < size; i++) {
715            final WindowNode window = mStructure.getWindowNodeAt(i);
716            final ViewNode root = window.getRootViewNode();
717            if (id.equals(root.getAutofillId())) {
718                return root;
719            }
720            final ViewNode child = findViewNodeByIdLocked(root, id);
721            if (child != null) {
722                return child;
723            }
724        }
725        return null;
726    }
727
728    private ViewNode findViewNodeByIdLocked(ViewNode parent, AutofillId id) {
729        final int childrenSize = parent.getChildCount();
730        if (childrenSize > 0) {
731            for (int i = 0; i < childrenSize; i++) {
732                final ViewNode child = parent.getChildAt(i);
733                if (id.equals(child.getAutofillId())) {
734                    return child;
735                }
736                final ViewNode grandChild = findViewNodeByIdLocked(child, id);
737                if (grandChild != null && id.equals(grandChild.getAutofillId())) {
738                    return grandChild;
739                }
740            }
741        }
742        return null;
743    }
744
745    void destroyLocked() {
746        mRemoteFillService.destroy();
747        mUi.setCallback(null);
748        mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);
749    }
750
751    void removeSelf() {
752        synchronized (mLock) {
753            removeSelfLocked();
754        }
755    }
756
757    void removeSelfLocked() {
758        if (VERBOSE) {
759            Slog.v(TAG, "removeSelfLocked()");
760        }
761        destroyLocked();
762        mService.removeSessionLocked(mActivityToken);
763    }
764}