• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.android.server.autofill.ui;
17 
18 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
19 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
20 
21 import static com.android.server.autofill.Helper.sDebug;
22 import static com.android.server.autofill.Helper.sVerbose;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentSender;
30 import android.graphics.drawable.Drawable;
31 import android.metrics.LogMaker;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.RemoteException;
36 import android.service.autofill.Dataset;
37 import android.service.autofill.FillEventHistory;
38 import android.service.autofill.FillResponse;
39 import android.service.autofill.SaveInfo;
40 import android.service.autofill.ValueFinder;
41 import android.text.TextUtils;
42 import android.util.Slog;
43 import android.view.KeyEvent;
44 import android.view.autofill.AutofillId;
45 import android.view.autofill.AutofillManager;
46 import android.view.autofill.IAutofillWindowPresenter;
47 import android.widget.Toast;
48 
49 import com.android.internal.logging.MetricsLogger;
50 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
51 import com.android.server.LocalServices;
52 import com.android.server.UiModeManagerInternal;
53 import com.android.server.UiThread;
54 import com.android.server.autofill.Helper;
55 import com.android.server.autofill.PresentationStatsEventLogger;
56 import com.android.server.autofill.SaveEventLogger;
57 import com.android.server.utils.Slogf;
58 
59 import java.io.PrintWriter;
60 
61 /**
62  * Handles all autofill related UI tasks. The UI has two components:
63  * fill UI that shows a popup style window anchored at the focused
64  * input field for choosing a dataset to fill or trigger the response
65  * authentication flow; save UI that shows a toast style window for
66  * managing saving of user edits.
67  */
68 public final class AutoFillUI {
69     private static final String TAG = "AutofillUI";
70 
71     private final Handler mHandler = UiThread.getHandler();
72     private final @NonNull Context mContext;
73 
74     private @Nullable FillUi mFillUi;
75     private @Nullable SaveUi mSaveUi;
76     private @Nullable DialogFillUi mFillDialog;
77 
78     private @Nullable AutoFillUiCallback mCallback;
79 
80     private final MetricsLogger mMetricsLogger = new MetricsLogger();
81 
82     private final @NonNull OverlayControl mOverlayControl;
83     private final @NonNull UiModeManagerInternal mUiModeMgr;
84 
85     private @Nullable Runnable mCreateFillUiRunnable;
86     private @Nullable AutoFillUiCallback mSaveUiCallback;
87 
88     public interface AutoFillUiCallback {
authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent, @Nullable Bundle extras, int uiType)89         void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent,
90                 @Nullable Bundle extras, int uiType);
fill(int requestId, int datasetIndex, @NonNull Dataset dataset, @FillEventHistory.Event.UiType int uiType)91         void fill(int requestId, int datasetIndex, @NonNull Dataset dataset,
92                 @FillEventHistory.Event.UiType int uiType);
save()93         void save();
cancelSave()94         void cancelSave();
requestShowFillUi(AutofillId id, int width, int height, IAutofillWindowPresenter presenter)95         void requestShowFillUi(AutofillId id, int width, int height,
96                 IAutofillWindowPresenter presenter);
requestHideFillUi(AutofillId id)97         void requestHideFillUi(AutofillId id);
requestHideFillUiWhenDestroyed(AutofillId id)98         void requestHideFillUiWhenDestroyed(AutofillId id);
startIntentSenderAndFinishSession(IntentSender intentSender)99         void startIntentSenderAndFinishSession(IntentSender intentSender);
startIntentSender(IntentSender intentSender, Intent intent)100         void startIntentSender(IntentSender intentSender, Intent intent);
dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent)101         void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent);
cancelSession()102         void cancelSession();
requestShowSoftInput(AutofillId id)103         void requestShowSoftInput(AutofillId id);
requestFallbackFromFillDialog()104         void requestFallbackFromFillDialog();
onShown(int uiType, int datasetSize)105         void onShown(int uiType, int datasetSize);
106     }
107 
AutoFillUI(@onNull Context context)108     public AutoFillUI(@NonNull Context context) {
109         mContext = context;
110         mOverlayControl = new OverlayControl(context);
111         mUiModeMgr = LocalServices.getService(UiModeManagerInternal.class);
112     }
113 
setCallback(@onNull AutoFillUiCallback callback)114     public void setCallback(@NonNull AutoFillUiCallback callback) {
115         mHandler.post(() -> {
116             if (mCallback != callback) {
117                 if (mCallback != null) {
118                     if (isSaveUiShowing()) {
119                         // keeps showing the save UI
120                         hideFillUiUiThread(callback, true);
121                     } else {
122                         hideAllUiThread(mCallback);
123                     }
124                 }
125                 mCallback = callback;
126             }
127         });
128     }
129 
clearCallback(@onNull AutoFillUiCallback callback)130     public void clearCallback(@NonNull AutoFillUiCallback callback) {
131         mHandler.post(() -> {
132             if (mCallback == callback) {
133                 hideAllUiThread(callback);
134                 mCallback = null;
135             }
136         });
137     }
138 
139     /**
140      * Displays an error message to the user.
141      */
showError(int resId, @NonNull AutoFillUiCallback callback)142     public void showError(int resId, @NonNull AutoFillUiCallback callback) {
143         showError(mContext.getString(resId), callback);
144     }
145 
146     /**
147      * Displays an error message to the user.
148      */
showError(@ullable CharSequence message, @NonNull AutoFillUiCallback callback)149     public void showError(@Nullable CharSequence message, @NonNull AutoFillUiCallback callback) {
150         Slog.w(TAG, "showError(): " + message);
151 
152         mHandler.post(() -> {
153             if (mCallback != callback) {
154                 return;
155             }
156             hideAllUiThread(callback);
157             if (!TextUtils.isEmpty(message)) {
158                 Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
159             }
160         });
161     }
162 
163     /**
164      * Hides the fill UI.
165      */
hideFillUi(@onNull AutoFillUiCallback callback)166     public void hideFillUi(@NonNull AutoFillUiCallback callback) {
167         mHandler.post(() -> hideFillUiUiThread(callback, true));
168     }
169 
170     /**
171      * Hides the fill UI.
172      */
hideFillDialog(@onNull AutoFillUiCallback callback)173     public void hideFillDialog(@NonNull AutoFillUiCallback callback) {
174         mHandler.post(() -> hideFillDialogUiThread(callback));
175     }
176     /**
177      * Filters the options in the fill UI.
178      *
179      * @param filterText The filter prefix.
180      */
filterFillUi(@ullable String filterText, @NonNull AutoFillUiCallback callback)181     public void filterFillUi(@Nullable String filterText, @NonNull AutoFillUiCallback callback) {
182         mHandler.post(() -> {
183             if (callback != mCallback) {
184                 return;
185             }
186             if (mFillUi != null) {
187                 mFillUi.setFilterText(filterText);
188             }
189         });
190     }
191 
192     /**
193      * Shows the fill UI, removing the previous fill UI if the has changed.
194      *
195      * @param focusedId the currently focused field
196      * @param response the current fill response
197      * @param filterText text of the view to be filled
198      * @param servicePackageName package name of the autofill service filling the activity
199      * @param componentName component name of the activity that is filled
200      * @param serviceLabel label of autofill service
201      * @param serviceIcon icon of autofill service
202      * @param callback identifier for the caller
203      * @param userId the user associated wit the session
204      * @param context context with the proper state (like display id) to show the UI
205      * @param sessionId id of the autofill session
206      * @param compatMode whether the app is being autofilled in compatibility mode.
207      * @param maxInputLengthForAutofill max user input to provide suggestion
208      */
showFillUi(@onNull AutofillId focusedId, @NonNull FillResponse response, @Nullable String filterText, @Nullable String servicePackageName, @NonNull ComponentName componentName, @NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback, @NonNull Context context, int sessionId, boolean compatMode, int maxInputLengthForAutofill)209     public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
210             @Nullable String filterText, @Nullable String servicePackageName,
211             @NonNull ComponentName componentName, @NonNull CharSequence serviceLabel,
212             @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback,
213             @NonNull Context context, int sessionId, boolean compatMode,
214             int maxInputLengthForAutofill) {
215         if (sDebug) {
216             final int size = filterText == null ? 0 : filterText.length();
217             Slogf.d(TAG, "showFillUi(): id=%s, filter=%d chars, displayId=%d", focusedId, size,
218                     context.getDisplayId());
219         }
220         final LogMaker log = Helper
221                 .newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, componentName, servicePackageName,
222                         sessionId, compatMode)
223                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
224                         filterText == null ? 0 : filterText.length())
225                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
226                         response.getDatasets() == null ? 0 : response.getDatasets().size());
227 
228         final Runnable createFillUiRunnable = () -> {
229             if (callback != mCallback) {
230                 return;
231             }
232             hideAllUiThread(callback);
233             mFillUi = new FillUi(context, response, focusedId, filterText, mOverlayControl,
234                     serviceLabel, serviceIcon, mUiModeMgr.isNightMode(), maxInputLengthForAutofill,
235                     new FillUi.Callback() {
236                 @Override
237                 public void onResponsePicked(FillResponse response) {
238                     log.setType(MetricsEvent.TYPE_DETAIL);
239                     hideFillUiUiThread(callback, true);
240                     if (mCallback != null) {
241                         mCallback.authenticate(response.getRequestId(),
242                                 AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
243                                 response.getAuthentication(), response.getClientState(),
244                                 UI_TYPE_MENU);
245                     }
246                 }
247 
248                 @Override
249                 public void onShown(int datasetSize) {
250                     if (mCallback != null) {
251                         mCallback.onShown(UI_TYPE_MENU, datasetSize);
252                     }
253                 }
254 
255                 @Override
256                 public void onDatasetPicked(Dataset dataset) {
257                     log.setType(MetricsEvent.TYPE_ACTION);
258                     hideFillUiUiThread(callback, true);
259                     if (mCallback != null) {
260                         final int datasetIndex = response.getDatasets().indexOf(dataset);
261                         mCallback.fill(response.getRequestId(), datasetIndex,
262                                 dataset, UI_TYPE_MENU);
263                     }
264                 }
265 
266                 @Override
267                 public void onCanceled() {
268                     log.setType(MetricsEvent.TYPE_DISMISS);
269                     hideFillUiUiThread(callback, true);
270                 }
271 
272                 @Override
273                 public void onDestroy() {
274                     if (log.getType() == MetricsEvent.TYPE_UNKNOWN) {
275                         log.setType(MetricsEvent.TYPE_CLOSE);
276                     }
277                     mMetricsLogger.write(log);
278                 }
279 
280                 @Override
281                 public void requestShowFillUi(int width, int height,
282                         IAutofillWindowPresenter windowPresenter) {
283                     if (mCallback != null) {
284                         mCallback.requestShowFillUi(focusedId, width, height, windowPresenter);
285                     }
286                 }
287 
288                 @Override
289                 public void requestHideFillUi() {
290                     if (mCallback != null) {
291                         mCallback.requestHideFillUi(focusedId);
292                     }
293                 }
294 
295                 @Override
296                 public void requestHideFillUiWhenDestroyed() {
297                     if (mCallback != null) {
298                         mCallback.requestHideFillUiWhenDestroyed(focusedId);
299                     }
300                 }
301 
302                 @Override
303                 public void startIntentSender(IntentSender intentSender) {
304                     if (mCallback != null) {
305                         mCallback.startIntentSenderAndFinishSession(intentSender);
306                     }
307                 }
308 
309                 @Override
310                 public void dispatchUnhandledKey(KeyEvent keyEvent) {
311                     if (mCallback != null) {
312                         mCallback.dispatchUnhandledKey(focusedId, keyEvent);
313                     }
314                 }
315 
316                 @Override
317                 public void cancelSession() {
318                     if (mCallback != null) {
319                         mCallback.cancelSession();
320                     }
321                 }
322             });
323         };
324 
325         if (isSaveUiShowing()) {
326             // postpone creating the fill UI for showing the save UI
327             if (sDebug) Slog.d(TAG, "postpone fill UI request..");
328             mCreateFillUiRunnable = createFillUiRunnable;
329         } else {
330             mHandler.post(createFillUiRunnable);
331         }
332     }
333 
334     /**
335      * Shows the UI asking the user to save for autofill.
336      */
showSaveUi(@onNull CharSequence serviceLabel, @NonNull Drawable serviceIcon, @Nullable String servicePackageName, @NonNull SaveInfo info, @NonNull ValueFinder valueFinder, @NonNull ComponentName componentName, @NonNull AutoFillUiCallback callback, @NonNull Context context, @NonNull PendingUi pendingSaveUi, boolean isUpdate, boolean compatMode, boolean showServiceIcon, @Nullable SaveEventLogger mSaveEventLogger)337     public void showSaveUi(@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
338             @Nullable String servicePackageName, @NonNull SaveInfo info,
339             @NonNull ValueFinder valueFinder, @NonNull ComponentName componentName,
340             @NonNull AutoFillUiCallback callback, @NonNull Context context,
341             @NonNull PendingUi pendingSaveUi, boolean isUpdate, boolean compatMode,
342             boolean showServiceIcon, @Nullable SaveEventLogger mSaveEventLogger) {
343         if (sVerbose) {
344             Slogf.v(TAG, "showSaveUi(update=%b) for %s and display %d: %s", isUpdate,
345                     componentName.toShortString(), context.getDisplayId(), info);
346         }
347         int numIds = 0;
348         numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
349         numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length;
350 
351         final LogMaker log = Helper
352                 .newLogMaker(MetricsEvent.AUTOFILL_SAVE_UI, componentName, servicePackageName,
353                         pendingSaveUi.sessionId, compatMode)
354                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
355         if (isUpdate) {
356             log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_UPDATE, 1);
357         }
358 
359         mHandler.post(() -> {
360             if (callback != mCallback) {
361                 return;
362             }
363             hideAllUiThread(callback);
364             mSaveUiCallback = callback;
365             mSaveUi = new SaveUi(context, pendingSaveUi, serviceLabel, serviceIcon,
366                     servicePackageName, componentName, info, valueFinder, mOverlayControl,
367                     new SaveUi.OnSaveListener() {
368                 @Override
369                 public void onSave() {
370                     log.setType(MetricsEvent.TYPE_ACTION);
371                     if (mSaveEventLogger != null) {
372                         mSaveEventLogger.maybeSetSaveButtonClicked(true);
373                     }
374                     hideSaveUiUiThread(callback);
375                     callback.save();
376                     destroySaveUiUiThread(pendingSaveUi, true);
377                 }
378 
379                 @Override
380                 public void onCancel(IntentSender listener) {
381                     log.setType(MetricsEvent.TYPE_DISMISS);
382                     if (mSaveEventLogger != null) {
383                         mSaveEventLogger.maybeSetCancelButtonClicked(true);
384                     }
385                     hideSaveUiUiThread(callback);
386                     if (listener != null) {
387                         try {
388                             listener.sendIntent(mContext, 0, null, null, null);
389                         } catch (IntentSender.SendIntentException e) {
390                             Slog.e(TAG, "Error starting negative action listener: "
391                                     + listener, e);
392                         }
393                     }
394                     callback.cancelSave();
395                     destroySaveUiUiThread(pendingSaveUi, true);
396                 }
397 
398                 @Override
399                 public void onDestroy() {
400                     if (log.getType() == MetricsEvent.TYPE_UNKNOWN) {
401                         log.setType(MetricsEvent.TYPE_CLOSE);
402 
403                         callback.cancelSave();
404                     }
405                     mMetricsLogger.write(log);
406                     if (mSaveEventLogger != null) {
407                         mSaveEventLogger.maybeSetDialogDismissed(true);
408                     }
409                 }
410 
411                 @Override
412                 public void startIntentSender(IntentSender intentSender, Intent intent) {
413                     callback.startIntentSender(intentSender, intent);
414                 }
415             }, mUiModeMgr.isNightMode(), isUpdate, compatMode, showServiceIcon);
416 
417             mSaveEventLogger.maybeSetLatencySaveUiDisplayMillis();
418         });
419     }
420 
421     /**
422      * Shows the UI asking the user to choose for autofill.
423      */
showFillDialog(@onNull AutofillId focusedId, @NonNull FillResponse response, @Nullable String filterText, @Nullable String servicePackageName, @NonNull ComponentName componentName, @Nullable Drawable serviceIcon, @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode, @Nullable PresentationStatsEventLogger presentationStatsEventLogger, @NonNull Object sessionLock)424     public void showFillDialog(@NonNull AutofillId focusedId, @NonNull FillResponse response,
425             @Nullable String filterText, @Nullable String servicePackageName,
426             @NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
427             @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode,
428             @Nullable PresentationStatsEventLogger presentationStatsEventLogger,
429             @NonNull Object sessionLock) {
430         if (sVerbose) {
431             Slog.v(TAG, "showFillDialog for "
432                     + componentName.toShortString() + ": " + response);
433         }
434 
435         final LogMaker log = Helper
436                 .newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, componentName, servicePackageName,
437                         sessionId, compatMode)
438                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
439                         filterText == null ? 0 : filterText.length())
440                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
441                         response.getDatasets() == null ? 0 : response.getDatasets().size());
442 
443         mHandler.post(() -> {
444             if (callback != mCallback) {
445                 return;
446             }
447             hideAllUiThread(callback);
448             mFillDialog = new DialogFillUi(mContext, response, focusedId, filterText,
449                     serviceIcon, servicePackageName, componentName, mOverlayControl,
450                     mUiModeMgr.isNightMode(), new DialogFillUi.UiCallback() {
451                         @Override
452                         public void onResponsePicked(FillResponse response) {
453                             log(MetricsEvent.TYPE_DETAIL);
454                             hideFillDialogUiThread(callback);
455                             if (mCallback != null) {
456                                 mCallback.authenticate(response.getRequestId(),
457                                         AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
458                                         response.getAuthentication(), response.getClientState(),
459                                         UI_TYPE_DIALOG);
460                             }
461                         }
462 
463                         @Override
464                         public void onShown(int datasetsShown) {
465                             if (mCallback != null) {
466                                 mCallback.onShown(UI_TYPE_DIALOG, datasetsShown);
467                             }
468                         }
469 
470                         @Override
471                         public void onDatasetPicked(Dataset dataset) {
472                             log(MetricsEvent.TYPE_ACTION);
473                             synchronized (sessionLock) {
474                                 if (presentationStatsEventLogger != null) {
475                                     presentationStatsEventLogger.maybeSetPositiveCtaButtonClicked(
476                                             true);
477                                 }
478                             }
479                             hideFillDialogUiThread(callback);
480                             if (mCallback != null) {
481                                 final int datasetIndex = response.getDatasets().indexOf(dataset);
482                                 mCallback.fill(response.getRequestId(), datasetIndex, dataset,
483                                         UI_TYPE_DIALOG);
484                             }
485                         }
486 
487                         @Override
488                         public void onDismissed() {
489                             log(MetricsEvent.TYPE_DISMISS);
490                             synchronized (sessionLock) {
491                                 if (presentationStatsEventLogger != null) {
492                                     presentationStatsEventLogger.maybeSetDialogDismissed(true);
493                                 }
494                             }
495                             hideFillDialogUiThread(callback);
496                             callback.requestShowSoftInput(focusedId);
497                             callback.requestFallbackFromFillDialog();
498                         }
499 
500                         @Override
501                         public void onCanceled() {
502                             log(MetricsEvent.TYPE_CLOSE);
503                             synchronized (sessionLock) {
504                                 if (presentationStatsEventLogger != null) {
505                                     presentationStatsEventLogger.maybeSetNegativeCtaButtonClicked(
506                                             true);
507                                 }
508                             }
509                             hideFillDialogUiThread(callback);
510                             callback.requestShowSoftInput(focusedId);
511                             callback.requestFallbackFromFillDialog();
512                         }
513 
514                         @Override
515                         public void startIntentSender(IntentSender intentSender) {
516                             if (mCallback != null) {
517                                 mCallback.startIntentSenderAndFinishSession(intentSender);
518                             }
519                         }
520 
521                         private void log(int type) {
522                             log.setType(type);
523                             mMetricsLogger.write(log);
524                         }
525                     });
526         });
527     }
528 
529     /**
530      * Executes an operation in the pending save UI, if any.
531      */
onPendingSaveUi(int operation, @NonNull IBinder token)532     public void onPendingSaveUi(int operation, @NonNull IBinder token) {
533         mHandler.post(() -> {
534             if (mSaveUi != null) {
535                 mSaveUi.onPendingUi(operation, token);
536             } else {
537                 Slog.w(TAG, "onPendingSaveUi(" + operation + "): no save ui");
538             }
539         });
540     }
541 
542     /**
543      * Hides all autofill UIs.
544      */
hideAll(@ullable AutoFillUiCallback callback)545     public void hideAll(@Nullable AutoFillUiCallback callback) {
546         mHandler.post(() -> hideAllUiThread(callback));
547     }
548 
549     /**
550      * Destroy all autofill UIs.
551      */
destroyAll(@ullable PendingUi pendingSaveUi, @Nullable AutoFillUiCallback callback, boolean notifyClient)552     public void destroyAll(@Nullable PendingUi pendingSaveUi,
553             @Nullable AutoFillUiCallback callback, boolean notifyClient) {
554         mHandler.post(() -> destroyAllUiThread(pendingSaveUi, callback, notifyClient));
555     }
556 
isSaveUiShowing()557     public boolean isSaveUiShowing() {
558         return mSaveUi == null ? false : mSaveUi.isShowing();
559     }
560 
isFillDialogShowing()561     public boolean isFillDialogShowing() {
562         return mFillDialog == null ? false : mFillDialog.isShowing();
563     }
564 
dump(PrintWriter pw)565     public void dump(PrintWriter pw) {
566         pw.println("Autofill UI");
567         final String prefix = "  ";
568         final String prefix2 = "    ";
569         pw.print(prefix); pw.print("Night mode: "); pw.println(mUiModeMgr.isNightMode());
570         if (mFillUi != null) {
571             pw.print(prefix); pw.println("showsFillUi: true");
572             mFillUi.dump(pw, prefix2);
573         } else {
574             pw.print(prefix); pw.println("showsFillUi: false");
575         }
576         if (mSaveUi != null) {
577             pw.print(prefix); pw.println("showsSaveUi: true");
578             mSaveUi.dump(pw, prefix2);
579         } else {
580             pw.print(prefix); pw.println("showsSaveUi: false");
581         }
582         if (mFillDialog != null) {
583             pw.print(prefix); pw.println("showsFillDialog: true");
584             mFillDialog.dump(pw, prefix2);
585         } else {
586             pw.print(prefix); pw.println("showsFillDialog: false");
587         }
588     }
589 
590     @android.annotation.UiThread
hideFillUiUiThread(@ullable AutoFillUiCallback callback, boolean notifyClient)591     private void hideFillUiUiThread(@Nullable AutoFillUiCallback callback, boolean notifyClient) {
592         if (mFillUi != null && (callback == null || callback == mCallback)) {
593             mFillUi.destroy(notifyClient);
594             mFillUi = null;
595         }
596     }
597 
598     @android.annotation.UiThread
599     @Nullable
hideSaveUiUiThread(@ullable AutoFillUiCallback callback)600     private PendingUi hideSaveUiUiThread(@Nullable AutoFillUiCallback callback) {
601         if (sVerbose) {
602             Slog.v(TAG, "hideSaveUiUiThread(): mSaveUi=" + mSaveUi + ", callback=" + callback
603                     + ", mCallback=" + mCallback);
604         }
605 
606         if (mSaveUi != null && mSaveUiCallback == callback) {
607             return mSaveUi.hide();
608         }
609         return null;
610     }
611 
612     @android.annotation.UiThread
hideFillDialogUiThread(@ullable AutoFillUiCallback callback)613     private void hideFillDialogUiThread(@Nullable AutoFillUiCallback callback) {
614         if (mFillDialog != null && (callback == null || callback == mCallback)) {
615             mFillDialog.destroy();
616             mFillDialog = null;
617         }
618     }
619 
620     @android.annotation.UiThread
destroySaveUiUiThread(@ullable PendingUi pendingSaveUi, boolean notifyClient)621     private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi, boolean notifyClient) {
622         if (mSaveUi == null) {
623             // Calling destroySaveUiUiThread() twice is normal - it usually happens when the
624             // first call is made after the SaveUI is hidden and the second when the session is
625             // finished.
626             if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): already destroyed");
627             return;
628         }
629 
630         if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): " + pendingSaveUi);
631         mSaveUi.destroy();
632         mSaveUi = null;
633         mSaveUiCallback = null;
634         if (pendingSaveUi != null && notifyClient) {
635             try {
636                 if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): notifying client");
637                 pendingSaveUi.client.setSaveUiState(pendingSaveUi.sessionId, false);
638             } catch (RemoteException e) {
639                 Slog.e(TAG, "Error notifying client to set save UI state to hidden: " + e);
640             }
641         }
642 
643         if (mCreateFillUiRunnable != null) {
644             if (sDebug) Slog.d(TAG, "start the pending fill UI request..");
645             mHandler.post(mCreateFillUiRunnable);
646             mCreateFillUiRunnable = null;
647         }
648     }
649 
650     @android.annotation.UiThread
destroyAllUiThread(@ullable PendingUi pendingSaveUi, @Nullable AutoFillUiCallback callback, boolean notifyClient)651     private void destroyAllUiThread(@Nullable PendingUi pendingSaveUi,
652             @Nullable AutoFillUiCallback callback, boolean notifyClient) {
653         hideFillUiUiThread(callback, notifyClient);
654         hideFillDialogUiThread(callback);
655         destroySaveUiUiThread(pendingSaveUi, notifyClient);
656     }
657 
658     @android.annotation.UiThread
hideAllUiThread(@ullable AutoFillUiCallback callback)659     private void hideAllUiThread(@Nullable AutoFillUiCallback callback) {
660         hideFillUiUiThread(callback, true);
661         hideFillDialogUiThread(callback);
662         final PendingUi pendingSaveUi = hideSaveUiUiThread(callback);
663         if (pendingSaveUi != null && pendingSaveUi.getState() == PendingUi.STATE_FINISHED) {
664             if (sDebug) {
665                 Slog.d(TAG, "hideAllUiThread(): "
666                         + "destroying Save UI because pending restoration is finished");
667             }
668             destroySaveUiUiThread(pendingSaveUi, true);
669         }
670     }
671 }
672