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