OSDN Git Service

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