OSDN Git Service

Proper autofill fix to let phone process autofill Settings activity.
[android-x86/frameworks-base.git] / services / autofill / java / com / android / server / autofill / AutofillManagerServiceImpl.java
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
17 package com.android.server.autofill;
18
19 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
20 import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
21 import static android.view.autofill.AutofillManager.NO_SESSION;
22
23 import static com.android.server.autofill.Helper.sDebug;
24 import static com.android.server.autofill.Helper.sVerbose;
25
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.ActivityManager;
29 import android.app.ActivityManagerInternal;
30 import android.app.AppGlobals;
31 import android.app.IActivityManager;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.pm.ApplicationInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageManager.NameNotFoundException;
37 import android.content.pm.ServiceInfo;
38 import android.graphics.Rect;
39 import android.graphics.drawable.Drawable;
40 import android.metrics.LogMaker;
41 import android.os.AsyncTask;
42 import android.os.Binder;
43 import android.os.Bundle;
44 import android.os.IBinder;
45 import android.os.Looper;
46 import android.os.RemoteCallbackList;
47 import android.os.RemoteException;
48 import android.os.UserHandle;
49 import android.os.UserManager;
50 import android.provider.Settings;
51 import android.service.autofill.AutofillService;
52 import android.service.autofill.AutofillServiceInfo;
53 import android.service.autofill.FillEventHistory;
54 import android.service.autofill.FillEventHistory.Event;
55 import android.service.autofill.FillResponse;
56 import android.service.autofill.IAutoFillService;
57 import android.text.TextUtils;
58 import android.util.ArraySet;
59 import android.util.DebugUtils;
60 import android.util.LocalLog;
61 import android.util.Slog;
62 import android.util.SparseArray;
63 import android.view.autofill.AutofillId;
64 import android.view.autofill.AutofillManager;
65 import android.view.autofill.AutofillValue;
66 import android.view.autofill.IAutoFillManagerClient;
67
68 import com.android.internal.R;
69 import com.android.internal.annotations.GuardedBy;
70 import com.android.internal.logging.MetricsLogger;
71 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
72 import com.android.internal.os.HandlerCaller;
73 import com.android.server.LocalServices;
74 import com.android.server.autofill.ui.AutoFillUI;
75
76 import java.io.PrintWriter;
77 import java.util.ArrayList;
78 import java.util.Random;
79
80 /**
81  * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
82  * app's {@link IAutoFillService} implementation.
83  *
84  */
85 final class AutofillManagerServiceImpl {
86
87     private static final String TAG = "AutofillManagerServiceImpl";
88     private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
89
90     /** Minimum interval to prune abandoned sessions */
91     private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
92
93     static final int MSG_SERVICE_SAVE = 1;
94
95     private final int mUserId;
96     private final Context mContext;
97     private final Object mLock;
98     private final AutoFillUI mUi;
99     private final MetricsLogger mMetricsLogger = new MetricsLogger();
100
101     private RemoteCallbackList<IAutoFillManagerClient> mClients;
102     private AutofillServiceInfo mInfo;
103
104     private static final Random sRandom = new Random();
105
106     private final LocalLog mRequestsHistory;
107     private final LocalLog mUiLatencyHistory;
108
109     /**
110      * Whether service was disabled for user due to {@link UserManager} restrictions.
111      */
112     private boolean mDisabled;
113
114     /**
115      * Caches whether the setup completed for the current user.
116      */
117     @GuardedBy("mLock")
118     private boolean mSetupComplete;
119
120     private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
121         switch (msg.what) {
122             case MSG_SERVICE_SAVE:
123                 handleSessionSave(msg.arg1);
124                 break;
125             default:
126                 Slog.w(TAG, "invalid msg on handler: " + msg);
127         }
128     };
129
130     private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
131             mHandlerCallback, true);
132
133     /**
134      * Cache of pending {@link Session}s, keyed by sessionId.
135      *
136      * <p>They're kept until the {@link AutofillService} finished handling a request, an error
137      * occurs, or the session is abandoned.
138      */
139     @GuardedBy("mLock")
140     private final SparseArray<Session> mSessions = new SparseArray<>();
141
142     /** The last selection */
143     @GuardedBy("mLock")
144     private FillEventHistory mEventHistory;
145
146     /** When was {@link PruneTask} last executed? */
147     private long mLastPrune = 0;
148
149     AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
150             LocalLog uiLatencyHistory, int userId, AutoFillUI ui, boolean disabled) {
151         mContext = context;
152         mLock = lock;
153         mRequestsHistory = requestsHistory;
154         mUiLatencyHistory = uiLatencyHistory;
155         mUserId = userId;
156         mUi = ui;
157         updateLocked(disabled);
158     }
159
160     @Nullable
161     CharSequence getServiceName() {
162         final String packageName = getServicePackageName();
163         if (packageName == null) {
164             return null;
165         }
166
167         try {
168             final PackageManager pm = mContext.getPackageManager();
169             final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
170             return pm.getApplicationLabel(info);
171         } catch (Exception e) {
172             Slog.e(TAG, "Could not get label for " + packageName + ": " + e);
173             return packageName;
174         }
175     }
176
177     @Nullable
178     String getServicePackageName() {
179         final ComponentName serviceComponent = getServiceComponentName();
180         if (serviceComponent != null) {
181             return serviceComponent.getPackageName();
182         }
183         return null;
184     }
185
186     ComponentName getServiceComponentName() {
187         synchronized (mLock) {
188             if (mInfo == null) {
189                 return null;
190             }
191             return mInfo.getServiceInfo().getComponentName();
192         }
193     }
194
195     private boolean isSetupCompletedLocked() {
196         final String setupComplete = Settings.Secure.getStringForUser(
197                 mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
198         return "1".equals(setupComplete);
199     }
200
201     private String getComponentNameFromSettings() {
202         return Settings.Secure.getStringForUser(
203                 mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
204     }
205
206     void updateLocked(boolean disabled) {
207         final boolean wasEnabled = isEnabled();
208         if (sVerbose) {
209             Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
210                     + ", mSetupComplete= " + mSetupComplete
211                     + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
212         }
213         mSetupComplete = isSetupCompletedLocked();
214         mDisabled = disabled;
215         ComponentName serviceComponent = null;
216         ServiceInfo serviceInfo = null;
217         final String componentName = getComponentNameFromSettings();
218         if (!TextUtils.isEmpty(componentName)) {
219             try {
220                 serviceComponent = ComponentName.unflattenFromString(componentName);
221                 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
222                         0, mUserId);
223             } catch (RuntimeException | RemoteException e) {
224                 Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
225                 return;
226             }
227         }
228         try {
229             if (serviceInfo != null) {
230                 mInfo = new AutofillServiceInfo(mContext.getPackageManager(),
231                         serviceComponent, mUserId);
232                 if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
233             } else {
234                 mInfo = null;
235                 if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId);
236             }
237             final boolean isEnabled = isEnabled();
238             if (wasEnabled != isEnabled) {
239                 if (!isEnabled) {
240                     final int sessionCount = mSessions.size();
241                     for (int i = sessionCount - 1; i >= 0; i--) {
242                         final Session session = mSessions.valueAt(i);
243                         session.removeSelfLocked();
244                     }
245                 }
246                 sendStateToClients(false);
247             }
248         } catch (Exception e) {
249             Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e);
250         }
251     }
252
253     boolean addClientLocked(IAutoFillManagerClient client) {
254         if (mClients == null) {
255             mClients = new RemoteCallbackList<>();
256         }
257         mClients.register(client);
258         return isEnabled();
259     }
260
261     void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
262         if (!isEnabled()) {
263             return;
264         }
265         final Session session = mSessions.get(sessionId);
266         if (session != null && uid == session.uid) {
267             session.setAuthenticationResultLocked(data, authenticationId);
268         }
269     }
270
271     void setHasCallback(int sessionId, int uid, boolean hasIt) {
272         if (!isEnabled()) {
273             return;
274         }
275         final Session session = mSessions.get(sessionId);
276         if (session != null && uid == session.uid) {
277             synchronized (mLock) {
278                 session.setHasCallbackLocked(hasIt);
279             }
280         }
281     }
282
283     int startSessionLocked(@NonNull IBinder activityToken, int uid,
284             @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
285             @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
286             int flags, @NonNull ComponentName componentName) {
287         if (!isEnabled()) {
288             return 0;
289         }
290         if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags);
291
292         // Occasionally clean up abandoned sessions
293         pruneAbandonedSessionsLocked();
294
295         final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken,
296                 hasCallback, componentName);
297         if (newSession == null) {
298             return NO_SESSION;
299         }
300
301         final String historyItem =
302                 "id=" + newSession.id + " uid=" + uid + " s=" + mInfo.getServiceInfo().packageName
303                         + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds + " hc=" +
304                         hasCallback + " f=" + flags;
305         mRequestsHistory.log(historyItem);
306
307         newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
308
309         return newSession.id;
310     }
311
312     /**
313      * Remove abandoned sessions if needed.
314      */
315     private void pruneAbandonedSessionsLocked() {
316         long now = System.currentTimeMillis();
317         if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
318             mLastPrune = now;
319
320             if (mSessions.size() > 0) {
321                 (new PruneTask()).execute();
322             }
323         }
324     }
325
326     void finishSessionLocked(int sessionId, int uid) {
327         if (!isEnabled()) {
328             return;
329         }
330
331         final Session session = mSessions.get(sessionId);
332         if (session == null || uid != session.uid) {
333             if (sVerbose) {
334                 Slog.v(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")");
335             }
336             return;
337         }
338
339         final boolean finished = session.showSaveLocked();
340         if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
341
342         if (finished) {
343             session.removeSelfLocked();
344         }
345     }
346
347     void cancelSessionLocked(int sessionId, int uid) {
348         if (!isEnabled()) {
349             return;
350         }
351
352         final Session session = mSessions.get(sessionId);
353         if (session == null || uid != session.uid) {
354             Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")");
355             return;
356         }
357         session.removeSelfLocked();
358     }
359
360     void disableOwnedAutofillServicesLocked(int uid) {
361         Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo);
362         if (mInfo == null) return;
363
364         final ServiceInfo serviceInfo = mInfo.getServiceInfo();
365         if (serviceInfo.applicationInfo.uid != uid) {
366             Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid
367                     + " instead of " + serviceInfo.applicationInfo.uid
368                     + " for service " + mInfo);
369             return;
370         }
371
372
373         final long identity = Binder.clearCallingIdentity();
374         try {
375             final String autoFillService = getComponentNameFromSettings();
376             final ComponentName componentName = serviceInfo.getComponentName();
377             if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
378                 mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
379                         componentName.getPackageName());
380                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
381                         Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
382                 destroySessionsLocked();
383             } else {
384                 Slog.w(TAG, "disableOwnedServices(): ignored because current service ("
385                         + serviceInfo + ") does not match Settings (" + autoFillService + ")");
386             }
387         } finally {
388             Binder.restoreCallingIdentity(identity);
389         }
390     }
391
392     private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
393             @NonNull IBinder appCallbackToken, boolean hasCallback,
394             @NonNull ComponentName componentName) {
395         // use random ids so that one app cannot know that another app creates sessions
396         int sessionId;
397         int tries = 0;
398         do {
399             tries++;
400             if (tries > MAX_SESSION_ID_CREATE_TRIES) {
401                 Slog.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries");
402                 return null;
403             }
404
405             sessionId = sRandom.nextInt();
406         } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
407
408         assertCallerLocked(componentName);
409
410         final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
411                 sessionId, uid, activityToken, appCallbackToken, hasCallback,
412                 mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName);
413         mSessions.put(newSession.id, newSession);
414
415         return newSession;
416     }
417
418     /**
419      * Asserts the component is owned by the caller.
420      */
421     private void assertCallerLocked(@NonNull ComponentName componentName) {
422         final String packageName = componentName.getPackageName();
423         final PackageManager pm = mContext.getPackageManager();
424         final int callingUid = Binder.getCallingUid();
425         final int packageUid;
426         try {
427             packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
428         } catch (NameNotFoundException e) {
429             throw new SecurityException("Could not verify UID for " + componentName);
430         }
431         if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
432                 .hasRunningActivity(callingUid, packageName)) {
433             final String[] packages = pm.getPackagesForUid(callingUid);
434             final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
435             Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
436                     + ") passed component (" + componentName + ") owned by UID " + packageUid);
437             mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT)
438                     .setPackageName(callingPackage)
439                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName())
440                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
441                             componentName == null ? "null" : componentName.flattenToShortString()));
442             throw new SecurityException("Invalid component: " + componentName);
443         }
444     }
445
446     /**
447      * Restores a session after an activity was temporarily destroyed.
448      *
449      * @param sessionId The id of the session to restore
450      * @param uid UID of the process that tries to restore the session
451      * @param activityToken The new instance of the activity
452      * @param appCallback The callbacks to the activity
453      */
454     boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken,
455             @NonNull IBinder appCallback) {
456         final Session session = mSessions.get(sessionId);
457
458         if (session == null || uid != session.uid) {
459             return false;
460         } else {
461             session.switchActivity(activityToken, appCallback);
462             return true;
463         }
464     }
465
466     /**
467      * Updates a session and returns whether it should be restarted.
468      */
469     boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
470             AutofillValue value, int action, int flags) {
471         final Session session = mSessions.get(sessionId);
472         if (session == null || session.uid != uid) {
473             if ((flags & FLAG_MANUAL_REQUEST) != 0) {
474                 if (sDebug) {
475                     Slog.d(TAG, "restarting session " + sessionId + " due to manual request on "
476                             + autofillId);
477                 }
478                 return true;
479             }
480             if (sVerbose) {
481                 Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId
482                         + "(" + uid + ")");
483             }
484             return false;
485         }
486
487         session.updateLocked(autofillId, virtualBounds, value, action, flags);
488         return false;
489     }
490
491     void removeSessionLocked(int sessionId) {
492         mSessions.remove(sessionId);
493     }
494
495     private void handleSessionSave(int sessionId) {
496         synchronized (mLock) {
497             final Session session = mSessions.get(sessionId);
498             if (session == null) {
499                 Slog.w(TAG, "handleSessionSave(): already gone: " + sessionId);
500
501                 return;
502             }
503             session.callSaveLocked();
504         }
505     }
506
507     void onPendingSaveUi(int operation, @NonNull IBinder token) {
508         if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
509         synchronized (mLock) {
510             final int sessionCount = mSessions.size();
511             for (int i = sessionCount - 1; i >= 0; i--) {
512                 final Session session = mSessions.valueAt(i);
513                 if (session.isSaveUiPendingForTokenLocked(token)) {
514                     session.onPendingSaveUi(operation, token);
515                     return;
516                 }
517             }
518         }
519         if (sDebug) {
520             Slog.d(TAG, "No pending Save UI for token " + token + " and operation "
521                     + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_",
522                             operation));
523         }
524     }
525
526     void destroyLocked() {
527         if (sVerbose) Slog.v(TAG, "destroyLocked()");
528
529         final int numSessions = mSessions.size();
530         final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions);
531         for (int i = 0; i < numSessions; i++) {
532             final RemoteFillService remoteFillService = mSessions.valueAt(i).destroyLocked();
533             if (remoteFillService != null) {
534                 remoteFillServices.add(remoteFillService);
535             }
536         }
537         mSessions.clear();
538         for (int i = 0; i < remoteFillServices.size(); i++) {
539             remoteFillServices.valueAt(i).destroy();
540         }
541
542         sendStateToClients(true);
543     }
544
545     @NonNull
546     CharSequence getServiceLabel() {
547         return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager());
548     }
549
550     @NonNull
551     Drawable getServiceIcon() {
552         return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
553     }
554
555     /**
556      * Initializes the last fill selection after an autofill service returned a new
557      * {@link FillResponse}.
558      */
559     void setLastResponse(int serviceUid, int sessionId, @NonNull FillResponse response) {
560         synchronized (mLock) {
561             mEventHistory = new FillEventHistory(serviceUid, sessionId, response.getClientState());
562         }
563     }
564
565     /**
566      * Resets the last fill selection.
567      */
568     void resetLastResponse() {
569         synchronized (mLock) {
570             mEventHistory = null;
571         }
572     }
573
574     private boolean isValidEventLocked(String method, int sessionId) {
575         if (mEventHistory == null) {
576             Slog.w(TAG, method + ": not logging event because history is null");
577             return false;
578         }
579         if (sessionId != mEventHistory.getSessionId()) {
580             if (sDebug) {
581                 Slog.d(TAG, method + ": not logging event for session " + sessionId
582                         + " because tracked session is " + mEventHistory.getSessionId());
583             }
584             return false;
585         }
586         return true;
587     }
588
589     /**
590      * Updates the last fill selection when an authentication was selected.
591      */
592     void setAuthenticationSelected(int sessionId) {
593         synchronized (mLock) {
594             if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
595                 mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null));
596             }
597         }
598     }
599
600     /**
601      * Updates the last fill selection when an dataset authentication was selected.
602      */
603     void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId) {
604         synchronized (mLock) {
605             if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
606                 mEventHistory.addEvent(
607                         new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
608             }
609         }
610     }
611
612     /**
613      * Updates the last fill selection when an save Ui is shown.
614      */
615     void logSaveShown(int sessionId) {
616         synchronized (mLock) {
617             if (isValidEventLocked("logSaveShown()", sessionId)) {
618                 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null));
619             }
620         }
621     }
622
623     /**
624      * Updates the last fill response when a dataset was selected.
625      */
626     void logDatasetSelected(@Nullable String selectedDataset, int sessionId) {
627         synchronized (mLock) {
628             if (isValidEventLocked("setDatasetSelected()", sessionId)) {
629                 mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset));
630             }
631         }
632     }
633
634     /**
635      * Gets the fill event history.
636      *
637      * @param callingUid The calling uid
638      *
639      * @return The history or {@code null} if there is none.
640      */
641     FillEventHistory getFillEventHistory(int callingUid) {
642         synchronized (mLock) {
643             if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) {
644                 return mEventHistory;
645             }
646         }
647
648         return null;
649     }
650
651     void dumpLocked(String prefix, PrintWriter pw) {
652         final String prefix2 = prefix + "  ";
653
654         pw.print(prefix); pw.print("User: "); pw.println(mUserId);
655         pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null
656                 ? mInfo.getServiceInfo().getComponentName() : null);
657         pw.print(prefix); pw.print("Component from settings: ");
658             pw.println(getComponentNameFromSettings());
659         pw.print(prefix); pw.print("Default component: ");
660             pw.println(mContext.getString(R.string.config_defaultAutofillService));
661         pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
662         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
663         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
664
665         final int size = mSessions.size();
666         if (size == 0) {
667             pw.print(prefix); pw.println("No sessions");
668         } else {
669             pw.print(prefix); pw.print(size); pw.println(" sessions:");
670             for (int i = 0; i < size; i++) {
671                 pw.print(prefix); pw.print("#"); pw.println(i + 1);
672                 mSessions.valueAt(i).dumpLocked(prefix2, pw);
673             }
674         }
675
676         if (mEventHistory == null || mEventHistory.getEvents() == null
677                 || mEventHistory.getEvents().size() == 0) {
678             pw.print(prefix); pw.println("No event on last fill response");
679         } else {
680             pw.print(prefix); pw.println("Events of last fill response:");
681             pw.print(prefix);
682
683             int numEvents = mEventHistory.getEvents().size();
684             for (int i = 0; i < numEvents; i++) {
685                 final Event event = mEventHistory.getEvents().get(i);
686                 pw.println("  " + i + ": eventType=" + event.getType() + " datasetId="
687                         + event.getDatasetId());
688             }
689         }
690     }
691
692     void destroySessionsLocked() {
693         if (mSessions.size() == 0) {
694             mUi.destroyAll(null, null, false);
695             return;
696         }
697         while (mSessions.size() > 0) {
698             mSessions.valueAt(0).forceRemoveSelfLocked();
699         }
700     }
701
702     // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
703     void destroyFinishedSessionsLocked() {
704         final int sessionCount = mSessions.size();
705         for (int i = sessionCount - 1; i >= 0; i--) {
706             final Session session = mSessions.valueAt(i);
707             if (session.isSavingLocked()) {
708                 if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
709                 session.forceRemoveSelfLocked();
710             }
711         }
712     }
713
714     void listSessionsLocked(ArrayList<String> output) {
715         final int numSessions = mSessions.size();
716         for (int i = 0; i < numSessions; i++) {
717             output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
718                     : null) + ":" + mSessions.keyAt(i));
719         }
720     }
721
722     private void sendStateToClients(boolean resetClient) {
723         final RemoteCallbackList<IAutoFillManagerClient> clients;
724         final int userClientCount;
725         synchronized (mLock) {
726             if (mClients == null) {
727                 return;
728             }
729             clients = mClients;
730             userClientCount = clients.beginBroadcast();
731         }
732         try {
733             for (int i = 0; i < userClientCount; i++) {
734                 final IAutoFillManagerClient client = clients.getBroadcastItem(i);
735                 try {
736                     final boolean resetSession;
737                     synchronized (mLock) {
738                         resetSession = resetClient || isClientSessionDestroyedLocked(client);
739                     }
740                     client.setState(isEnabled(), resetSession, resetClient);
741                 } catch (RemoteException re) {
742                     /* ignore */
743                 }
744             }
745         } finally {
746             clients.finishBroadcast();
747         }
748     }
749
750     private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
751         final int sessionCount = mSessions.size();
752         for (int i = 0; i < sessionCount; i++) {
753             final Session session = mSessions.valueAt(i);
754             if (session.getClient().equals(client)) {
755                 return session.isDestroyed();
756             }
757         }
758         return true;
759     }
760
761     boolean isEnabled() {
762         return mSetupComplete && mInfo != null && !mDisabled;
763     }
764
765     @Override
766     public String toString() {
767         return "AutofillManagerServiceImpl: [userId=" + mUserId
768                 + ", component=" + (mInfo != null
769                 ? mInfo.getServiceInfo().getComponentName() : null) + "]";
770     }
771
772     /** Task used to prune abandoned session */
773     private class PruneTask extends AsyncTask<Void, Void, Void> {
774         @Override
775         protected Void doInBackground(Void... ignored) {
776             int numSessionsToRemove;
777
778             SparseArray<IBinder> sessionsToRemove;
779
780             synchronized (mLock) {
781                 numSessionsToRemove = mSessions.size();
782                 sessionsToRemove = new SparseArray<>(numSessionsToRemove);
783
784                 for (int i = 0; i < numSessionsToRemove; i++) {
785                     Session session = mSessions.valueAt(i);
786
787                     sessionsToRemove.put(session.id, session.getActivityTokenLocked());
788                 }
789             }
790
791             IActivityManager am = ActivityManager.getService();
792
793             // Only remove sessions which's activities are not known to the activity manager anymore
794             for (int i = 0; i < numSessionsToRemove; i++) {
795                 try {
796                     // The activity manager cannot resolve activities that have been removed
797                     if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) {
798                         sessionsToRemove.removeAt(i);
799                         i--;
800                         numSessionsToRemove--;
801                     }
802                 } catch (RemoteException e) {
803                     Slog.w(TAG, "Cannot figure out if activity is finished", e);
804                 }
805             }
806
807             synchronized (mLock) {
808                 for (int i = 0; i < numSessionsToRemove; i++) {
809                     Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i));
810
811                     if (sessionToRemove != null && sessionsToRemove.valueAt(i)
812                             == sessionToRemove.getActivityTokenLocked()) {
813                         if (sessionToRemove.isSavingLocked()) {
814                             if (sVerbose) {
815                                 Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
816                             }
817                         } else {
818                             if (sDebug) {
819                                 Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
820                                     + sessionToRemove.getActivityTokenLocked() + ")");
821                             }
822                             sessionToRemove.removeSelfLocked();
823                         }
824                     }
825                 }
826             }
827
828             return null;
829         }
830     }
831 }