AutofillManagerServiceImpl.java revision b55cd8ec1e003ba42f6e3ae4843eab4d560b0c52
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
17package com.android.server.autofill;
18
19import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
20import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
21import static android.view.autofill.AutofillManager.NO_SESSION;
22
23import static com.android.server.autofill.Helper.sDebug;
24import static com.android.server.autofill.Helper.sVerbose;
25
26import android.annotation.NonNull;
27import android.annotation.Nullable;
28import android.app.ActivityManager;
29import android.app.ActivityManagerInternal;
30import android.app.AppGlobals;
31import android.app.IActivityManager;
32import android.content.ComponentName;
33import android.content.Context;
34import android.content.pm.ApplicationInfo;
35import android.content.pm.PackageItemInfo;
36import android.content.pm.PackageManager;
37import android.content.pm.PackageManager.NameNotFoundException;
38import android.content.pm.ServiceInfo;
39import android.graphics.Rect;
40import android.graphics.drawable.Drawable;
41import android.metrics.LogMaker;
42import android.os.AsyncTask;
43import android.os.Binder;
44import android.os.Bundle;
45import android.os.Handler;
46import android.os.IBinder;
47import android.os.Looper;
48import android.os.RemoteCallbackList;
49import android.os.RemoteException;
50import android.os.SystemClock;
51import android.os.UserHandle;
52import android.os.UserManager;
53import android.provider.Settings;
54import android.service.autofill.AutofillService;
55import android.service.autofill.AutofillServiceInfo;
56import android.service.autofill.FieldClassification;
57import android.service.autofill.FieldClassification.Match;
58import android.service.autofill.FillEventHistory;
59import android.service.autofill.FillEventHistory.Event;
60import android.service.autofill.FillResponse;
61import android.service.autofill.IAutoFillService;
62import android.service.autofill.UserData;
63import android.text.TextUtils;
64import android.util.ArrayMap;
65import android.util.ArraySet;
66import android.util.DebugUtils;
67import android.util.LocalLog;
68import android.util.Slog;
69import android.util.SparseArray;
70import android.util.TimeUtils;
71import android.view.autofill.AutofillId;
72import android.view.autofill.AutofillManager;
73import android.view.autofill.AutofillValue;
74import android.view.autofill.IAutoFillManagerClient;
75
76import com.android.internal.R;
77import com.android.internal.annotations.GuardedBy;
78import com.android.internal.logging.MetricsLogger;
79import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
80import com.android.server.LocalServices;
81import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
82import com.android.server.autofill.ui.AutoFillUI;
83
84import java.io.PrintWriter;
85import java.util.ArrayList;
86import java.util.List;
87import java.util.Random;
88
89/**
90 * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
91 * app's {@link IAutoFillService} implementation.
92 *
93 */
94final class AutofillManagerServiceImpl {
95
96    private static final String TAG = "AutofillManagerServiceImpl";
97    private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
98
99    /** Minimum interval to prune abandoned sessions */
100    private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
101
102    private final int mUserId;
103    private final Context mContext;
104    private final Object mLock;
105    private final AutoFillUI mUi;
106    private final MetricsLogger mMetricsLogger = new MetricsLogger();
107
108    @GuardedBy("mLock")
109    private RemoteCallbackList<IAutoFillManagerClient> mClients;
110
111    @GuardedBy("mLock")
112    private AutofillServiceInfo mInfo;
113
114    private static final Random sRandom = new Random();
115
116    private final LocalLog mRequestsHistory;
117    private final LocalLog mUiLatencyHistory;
118    private final LocalLog mWtfHistory;
119    private final FieldClassificationStrategy mFieldClassificationStrategy;
120
121    /**
122     * Apps disabled by the service; key is package name, value is when they will be enabled again.
123     */
124    @GuardedBy("mLock")
125    private ArrayMap<String, Long> mDisabledApps;
126
127    /**
128     * Activities disabled by the service; key is component name, value is when they will be enabled
129     * again.
130     */
131    @GuardedBy("mLock")
132    private ArrayMap<ComponentName, Long> mDisabledActivities;
133
134    /**
135     * Whether service was disabled for user due to {@link UserManager} restrictions.
136     */
137    @GuardedBy("mLock")
138    private boolean mDisabled;
139
140    /**
141     * Data used for field classification.
142     */
143    @GuardedBy("mLock")
144    private UserData mUserData;
145
146    /**
147     * Caches whether the setup completed for the current user.
148     */
149    @GuardedBy("mLock")
150    private boolean mSetupComplete;
151
152    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
153
154    /**
155     * Cache of pending {@link Session}s, keyed by sessionId.
156     *
157     * <p>They're kept until the {@link AutofillService} finished handling a request, an error
158     * occurs, or the session is abandoned.
159     */
160    @GuardedBy("mLock")
161    private final SparseArray<Session> mSessions = new SparseArray<>();
162
163    /** The last selection */
164    @GuardedBy("mLock")
165    private FillEventHistory mEventHistory;
166
167    /** Shared instance, doesn't need to be logged */
168    private final AutofillCompatState mAutofillCompatState;
169
170    /** When was {@link PruneTask} last executed? */
171    private long mLastPrune = 0;
172
173    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
174            LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
175            AutofillCompatState autofillCompatState, boolean disabled) {
176        mContext = context;
177        mLock = lock;
178        mRequestsHistory = requestsHistory;
179        mUiLatencyHistory = uiLatencyHistory;
180        mWtfHistory = wtfHistory;
181        mUserId = userId;
182        mUi = ui;
183        mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
184        mAutofillCompatState = autofillCompatState;
185        updateLocked(disabled);
186    }
187
188    @Nullable
189    CharSequence getServiceName() {
190        final String packageName = getServicePackageName();
191        if (packageName == null) {
192            return null;
193        }
194
195        try {
196            final PackageManager pm = mContext.getPackageManager();
197            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
198            return pm.getApplicationLabel(info);
199        } catch (Exception e) {
200            Slog.e(TAG, "Could not get label for " + packageName + ": " + e);
201            return packageName;
202        }
203    }
204
205    @GuardedBy("mLock")
206    private int getServiceUidLocked() {
207        if (mInfo == null) {
208            Slog.w(TAG,  "getServiceUidLocked(): no mInfo");
209            return -1;
210        }
211        return mInfo.getServiceInfo().applicationInfo.uid;
212    }
213
214
215    @Nullable
216    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
217        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
218    }
219
220    @Nullable
221    String getServicePackageName() {
222        final ComponentName serviceComponent = getServiceComponentName();
223        if (serviceComponent != null) {
224            return serviceComponent.getPackageName();
225        }
226        return null;
227    }
228
229    ComponentName getServiceComponentName() {
230        synchronized (mLock) {
231            if (mInfo == null) {
232                return null;
233            }
234            return mInfo.getServiceInfo().getComponentName();
235        }
236    }
237
238    private boolean isSetupCompletedLocked() {
239        final String setupComplete = Settings.Secure.getStringForUser(
240                mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
241        return "1".equals(setupComplete);
242    }
243
244    private String getComponentNameFromSettings() {
245        return Settings.Secure.getStringForUser(
246                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
247    }
248
249    @GuardedBy("mLock")
250    void updateLocked(boolean disabled) {
251        final boolean wasEnabled = isEnabledLocked();
252        if (sVerbose) {
253            Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
254                    + ", mSetupComplete= " + mSetupComplete
255                    + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
256        }
257        mSetupComplete = isSetupCompletedLocked();
258        mDisabled = disabled;
259        ComponentName serviceComponent = null;
260        ServiceInfo serviceInfo = null;
261        final String componentName = getComponentNameFromSettings();
262        if (!TextUtils.isEmpty(componentName)) {
263            try {
264                serviceComponent = ComponentName.unflattenFromString(componentName);
265                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
266                        0, mUserId);
267                if (serviceInfo == null) {
268                    Slog.e(TAG, "Bad AutofillService name: " + componentName);
269                }
270            } catch (RuntimeException | RemoteException e) {
271                Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
272                serviceInfo = null;
273            }
274        }
275        try {
276            if (serviceInfo != null) {
277                mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId);
278                if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
279            } else {
280                mInfo = null;
281                if (sDebug) {
282                    Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
283                }
284            }
285        } catch (Exception e) {
286            Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
287            mInfo = null;
288        }
289        final boolean isEnabled = isEnabledLocked();
290        if (wasEnabled != isEnabled) {
291            if (!isEnabled) {
292                final int sessionCount = mSessions.size();
293                for (int i = sessionCount - 1; i >= 0; i--) {
294                    final Session session = mSessions.valueAt(i);
295                    session.removeSelfLocked();
296                }
297            }
298            sendStateToClients(false);
299        }
300    }
301
302    @GuardedBy("mLock")
303    boolean addClientLocked(IAutoFillManagerClient client) {
304        if (mClients == null) {
305            mClients = new RemoteCallbackList<>();
306        }
307        mClients.register(client);
308        return isEnabledLocked();
309    }
310
311    @GuardedBy("mLock")
312    void removeClientLocked(IAutoFillManagerClient client) {
313        if (mClients != null) {
314            mClients.unregister(client);
315        }
316    }
317
318    @GuardedBy("mLock")
319    void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
320        if (!isEnabledLocked()) {
321            return;
322        }
323        final Session session = mSessions.get(sessionId);
324        if (session != null && uid == session.uid) {
325            session.setAuthenticationResultLocked(data, authenticationId);
326        }
327    }
328
329    void setHasCallback(int sessionId, int uid, boolean hasIt) {
330        if (!isEnabledLocked()) {
331            return;
332        }
333        final Session session = mSessions.get(sessionId);
334        if (session != null && uid == session.uid) {
335            synchronized (mLock) {
336                session.setHasCallbackLocked(hasIt);
337            }
338        }
339    }
340
341    @GuardedBy("mLock")
342    int startSessionLocked(@NonNull IBinder activityToken, int uid,
343            @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
344            @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
345            @NonNull ComponentName componentName, boolean compatMode,
346            boolean bindInstantServiceAllowed, int flags) {
347        if (!isEnabledLocked()) {
348            return 0;
349        }
350
351        final String shortComponentName = componentName.toShortString();
352
353        if (isAutofillDisabledLocked(componentName)) {
354            if (sDebug) {
355                Slog.d(TAG, "startSession(" + shortComponentName
356                        + "): ignored because disabled by service");
357            }
358
359            final IAutoFillManagerClient client = IAutoFillManagerClient.Stub
360                    .asInterface(appCallbackToken);
361            try {
362                client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE);
363            } catch (RemoteException e) {
364                Slog.w(TAG, "Could not notify " + shortComponentName + " that it's disabled: " + e);
365            }
366
367            return NO_SESSION;
368        }
369
370        if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags);
371
372        // Occasionally clean up abandoned sessions
373        pruneAbandonedSessionsLocked();
374
375        final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken,
376                hasCallback, componentName, compatMode, bindInstantServiceAllowed, flags);
377        if (newSession == null) {
378            return NO_SESSION;
379        }
380
381        final String historyItem =
382                "id=" + newSession.id + " uid=" + uid + " a=" + shortComponentName
383                + " s=" + mInfo.getServiceInfo().packageName
384                + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds
385                + " hc=" + hasCallback + " f=" + flags;
386        mRequestsHistory.log(historyItem);
387
388        newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
389
390        return newSession.id;
391    }
392
393    /**
394     * Remove abandoned sessions if needed.
395     */
396    @GuardedBy("mLock")
397    private void pruneAbandonedSessionsLocked() {
398        long now = System.currentTimeMillis();
399        if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
400            mLastPrune = now;
401
402            if (mSessions.size() > 0) {
403                (new PruneTask()).execute();
404            }
405        }
406    }
407
408    @GuardedBy("mLock")
409    void setAutofillFailureLocked(int sessionId, int uid, @NonNull List<AutofillId> ids) {
410        if (!isEnabledLocked()) {
411            return;
412        }
413        final Session session = mSessions.get(sessionId);
414        if (session == null || uid != session.uid) {
415            Slog.v(TAG, "setAutofillFailure(): no session for " + sessionId + "(" + uid + ")");
416            return;
417        }
418        session.setAutofillFailureLocked(ids);
419    }
420
421    @GuardedBy("mLock")
422    void finishSessionLocked(int sessionId, int uid) {
423        if (!isEnabledLocked()) {
424            return;
425        }
426
427        final Session session = mSessions.get(sessionId);
428        if (session == null || uid != session.uid) {
429            if (sVerbose) {
430                Slog.v(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")");
431            }
432            return;
433        }
434
435        session.logContextCommitted();
436
437        final boolean finished = session.showSaveLocked();
438        if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
439
440        if (finished) {
441            session.removeSelfLocked();
442        }
443    }
444
445    @GuardedBy("mLock")
446    void cancelSessionLocked(int sessionId, int uid) {
447        if (!isEnabledLocked()) {
448            return;
449        }
450
451        final Session session = mSessions.get(sessionId);
452        if (session == null || uid != session.uid) {
453            Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")");
454            return;
455        }
456        session.removeSelfLocked();
457    }
458
459    @GuardedBy("mLock")
460    void disableOwnedAutofillServicesLocked(int uid) {
461        Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo);
462        if (mInfo == null) return;
463
464        final ServiceInfo serviceInfo = mInfo.getServiceInfo();
465        if (serviceInfo.applicationInfo.uid != uid) {
466            Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid
467                    + " instead of " + serviceInfo.applicationInfo.uid
468                    + " for service " + mInfo);
469            return;
470        }
471
472
473        final long identity = Binder.clearCallingIdentity();
474        try {
475            final String autoFillService = getComponentNameFromSettings();
476            final ComponentName componentName = serviceInfo.getComponentName();
477            if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
478                mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
479                        componentName.getPackageName());
480                Settings.Secure.putStringForUser(mContext.getContentResolver(),
481                        Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
482                destroySessionsLocked();
483            } else {
484                Slog.w(TAG, "disableOwnedServices(): ignored because current service ("
485                        + serviceInfo + ") does not match Settings (" + autoFillService + ")");
486            }
487        } finally {
488            Binder.restoreCallingIdentity(identity);
489        }
490    }
491
492    @GuardedBy("mLock")
493    private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
494            @NonNull IBinder appCallbackToken, boolean hasCallback,
495            @NonNull ComponentName componentName, boolean compatMode,
496            boolean bindInstantServiceAllowed, int flags) {
497        // use random ids so that one app cannot know that another app creates sessions
498        int sessionId;
499        int tries = 0;
500        do {
501            tries++;
502            if (tries > MAX_SESSION_ID_CREATE_TRIES) {
503                Slog.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries");
504                return null;
505            }
506
507            sessionId = sRandom.nextInt();
508        } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
509
510        assertCallerLocked(componentName, compatMode);
511
512        final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
513                sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory,
514                mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, compatMode,
515                bindInstantServiceAllowed, flags);
516        mSessions.put(newSession.id, newSession);
517
518        return newSession;
519    }
520
521    /**
522     * Asserts the component is owned by the caller.
523     */
524    private void assertCallerLocked(@NonNull ComponentName componentName, boolean compatMode) {
525        final String packageName = componentName.getPackageName();
526        final PackageManager pm = mContext.getPackageManager();
527        final int callingUid = Binder.getCallingUid();
528        final int packageUid;
529        try {
530            packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
531        } catch (NameNotFoundException e) {
532            throw new SecurityException("Could not verify UID for " + componentName);
533        }
534        if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
535                .hasRunningActivity(callingUid, packageName)) {
536            final String[] packages = pm.getPackagesForUid(callingUid);
537            final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
538            Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
539                    + ") passed component (" + componentName + ") owned by UID " + packageUid);
540            mMetricsLogger.write(
541                    Helper.newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT,
542                            callingPackage, getServicePackageName(), compatMode)
543                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
544                            componentName == null ? "null" : componentName.flattenToShortString()));
545
546            throw new SecurityException("Invalid component: " + componentName);
547        }
548    }
549
550    /**
551     * Restores a session after an activity was temporarily destroyed.
552     *
553     * @param sessionId The id of the session to restore
554     * @param uid UID of the process that tries to restore the session
555     * @param activityToken The new instance of the activity
556     * @param appCallback The callbacks to the activity
557     */
558    boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken,
559            @NonNull IBinder appCallback) {
560        final Session session = mSessions.get(sessionId);
561
562        if (session == null || uid != session.uid) {
563            return false;
564        } else {
565            session.switchActivity(activityToken, appCallback);
566            return true;
567        }
568    }
569
570    /**
571     * Updates a session and returns whether it should be restarted.
572     */
573    @GuardedBy("mLock")
574    boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
575            AutofillValue value, int action, int flags) {
576        final Session session = mSessions.get(sessionId);
577        if (session == null || session.uid != uid) {
578            if ((flags & FLAG_MANUAL_REQUEST) != 0) {
579                if (sDebug) {
580                    Slog.d(TAG, "restarting session " + sessionId + " due to manual request on "
581                            + autofillId);
582                }
583                return true;
584            }
585            if (sVerbose) {
586                Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId
587                        + "(" + uid + ")");
588            }
589            return false;
590        }
591
592        session.updateLocked(autofillId, virtualBounds, value, action, flags);
593        return false;
594    }
595
596    @GuardedBy("mLock")
597    void removeSessionLocked(int sessionId) {
598        mSessions.remove(sessionId);
599    }
600
601    void handleSessionSave(Session session) {
602        synchronized (mLock) {
603            if (mSessions.get(session.id) == null) {
604                Slog.w(TAG, "handleSessionSave(): already gone: " + session.id);
605
606                return;
607            }
608            session.callSaveLocked();
609        }
610    }
611
612    void onPendingSaveUi(int operation, @NonNull IBinder token) {
613        if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
614        synchronized (mLock) {
615            final int sessionCount = mSessions.size();
616            for (int i = sessionCount - 1; i >= 0; i--) {
617                final Session session = mSessions.valueAt(i);
618                if (session.isSaveUiPendingForTokenLocked(token)) {
619                    session.onPendingSaveUi(operation, token);
620                    return;
621                }
622            }
623        }
624        if (sDebug) {
625            Slog.d(TAG, "No pending Save UI for token " + token + " and operation "
626                    + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_",
627                            operation));
628        }
629    }
630
631    @GuardedBy("mLock")
632    void handlePackageUpdateLocked(String packageName) {
633        final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
634        if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
635            resetExtServiceLocked();
636        }
637    }
638
639    @GuardedBy("mLock")
640    void resetExtServiceLocked() {
641        if (sVerbose) Slog.v(TAG, "reset autofill service.");
642        mFieldClassificationStrategy.reset();
643    }
644
645    @GuardedBy("mLock")
646    void destroyLocked() {
647        if (sVerbose) Slog.v(TAG, "destroyLocked()");
648
649        resetExtServiceLocked();
650
651        final int numSessions = mSessions.size();
652        final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions);
653        for (int i = 0; i < numSessions; i++) {
654            final RemoteFillService remoteFillService = mSessions.valueAt(i).destroyLocked();
655            if (remoteFillService != null) {
656                remoteFillServices.add(remoteFillService);
657            }
658        }
659        mSessions.clear();
660        for (int i = 0; i < remoteFillServices.size(); i++) {
661            remoteFillServices.valueAt(i).destroy();
662        }
663
664        sendStateToClients(true);
665        if (mClients != null) {
666            mClients.kill();
667            mClients = null;
668        }
669    }
670
671    @NonNull
672    CharSequence getServiceLabel() {
673        final CharSequence label = mInfo.getServiceInfo().loadSafeLabel(
674                mContext.getPackageManager(), 0 /* do not ellipsize */,
675                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
676        return label;
677    }
678
679    @NonNull
680    Drawable getServiceIcon() {
681        return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
682    }
683
684    /**
685     * Initializes the last fill selection after an autofill service returned a new
686     * {@link FillResponse}.
687     */
688    void setLastResponse(int sessionId, @NonNull FillResponse response) {
689        synchronized (mLock) {
690            mEventHistory = new FillEventHistory(sessionId, response.getClientState());
691        }
692    }
693
694    /**
695     * Resets the last fill selection.
696     */
697    void resetLastResponse() {
698        synchronized (mLock) {
699            mEventHistory = null;
700        }
701    }
702
703    @GuardedBy("mLock")
704    private boolean isValidEventLocked(String method, int sessionId) {
705        if (mEventHistory == null) {
706            Slog.w(TAG, method + ": not logging event because history is null");
707            return false;
708        }
709        if (sessionId != mEventHistory.getSessionId()) {
710            if (sDebug) {
711                Slog.d(TAG, method + ": not logging event for session " + sessionId
712                        + " because tracked session is " + mEventHistory.getSessionId());
713            }
714            return false;
715        }
716        return true;
717    }
718
719    /**
720     * Updates the last fill selection when an authentication was selected.
721     */
722    void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) {
723        synchronized (mLock) {
724            if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
725                mEventHistory.addEvent(
726                        new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
727                                null, null, null, null, null, null));
728            }
729        }
730    }
731
732    /**
733     * Updates the last fill selection when an dataset authentication was selected.
734     */
735    void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
736            @Nullable Bundle clientState) {
737        synchronized (mLock) {
738            if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
739                mEventHistory.addEvent(
740                        new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
741                                clientState, null, null, null, null, null, null, null, null));
742            }
743        }
744    }
745
746    /**
747     * Updates the last fill selection when an save Ui is shown.
748     */
749    void logSaveShown(int sessionId, @Nullable Bundle clientState) {
750        synchronized (mLock) {
751            if (isValidEventLocked("logSaveShown()", sessionId)) {
752                mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
753                        null, null, null, null, null, null, null));
754            }
755        }
756    }
757
758    /**
759     * Updates the last fill response when a dataset was selected.
760     */
761    void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
762            @Nullable Bundle clientState) {
763        synchronized (mLock) {
764            if (isValidEventLocked("logDatasetSelected()", sessionId)) {
765                mEventHistory.addEvent(
766                        new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
767                                null, null, null, null, null, null, null));
768            }
769        }
770    }
771
772    /**
773     * Updates the last fill response when an autofill context is committed.
774     */
775    @GuardedBy("mLock")
776    void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
777            @Nullable ArrayList<String> selectedDatasets,
778            @Nullable ArraySet<String> ignoredDatasets,
779            @Nullable ArrayList<AutofillId> changedFieldIds,
780            @Nullable ArrayList<String> changedDatasetIds,
781            @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
782            @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
783            @NonNull String appPackageName, boolean compatMode) {
784        logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
785                changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
786                manuallyFilledDatasetIds, null, null, appPackageName, compatMode);
787    }
788
789    @GuardedBy("mLock")
790    void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
791            @Nullable ArrayList<String> selectedDatasets,
792            @Nullable ArraySet<String> ignoredDatasets,
793            @Nullable ArrayList<AutofillId> changedFieldIds,
794            @Nullable ArrayList<String> changedDatasetIds,
795            @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
796            @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
797            @Nullable ArrayList<AutofillId> detectedFieldIdsList,
798            @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
799            @NonNull String appPackageName, boolean compatMode) {
800        if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
801            if (sVerbose) {
802                Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
803                        + ", selectedDatasets=" + selectedDatasets
804                        + ", ignoredDatasetIds=" + ignoredDatasets
805                        + ", changedAutofillIds=" + changedFieldIds
806                        + ", changedDatasetIds=" + changedDatasetIds
807                        + ", manuallyFilledFieldIds=" + manuallyFilledFieldIds
808                        + ", detectedFieldIds=" + detectedFieldIdsList
809                        + ", detectedFieldClassifications=" + detectedFieldClassificationsList
810                        + ", compatMode=" + compatMode);
811            }
812            AutofillId[] detectedFieldsIds = null;
813            FieldClassification[] detectedFieldClassifications = null;
814            if (detectedFieldIdsList != null) {
815                detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
816                detectedFieldIdsList.toArray(detectedFieldsIds);
817                detectedFieldClassifications =
818                        new FieldClassification[detectedFieldClassificationsList.size()];
819                detectedFieldClassificationsList.toArray(detectedFieldClassifications);
820
821                final int numberFields = detectedFieldsIds.length;
822                int totalSize = 0;
823                float totalScore = 0;
824                for (int i = 0; i < numberFields; i++) {
825                    final FieldClassification fc = detectedFieldClassifications[i];
826                    final List<Match> matches = fc.getMatches();
827                    final int size = matches.size();
828                    totalSize += size;
829                    for (int j = 0; j < size; j++) {
830                        totalScore += matches.get(j).getScore();
831                    }
832                }
833
834                final int averageScore = (int) ((totalScore * 100) / totalSize);
835                mMetricsLogger.write(Helper
836                        .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
837                                appPackageName, getServicePackageName(), compatMode)
838                        .setCounterValue(numberFields)
839                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
840                                averageScore));
841            }
842            mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
843                    clientState, selectedDatasets, ignoredDatasets,
844                    changedFieldIds, changedDatasetIds,
845                    manuallyFilledFieldIds, manuallyFilledDatasetIds,
846                    detectedFieldsIds, detectedFieldClassifications));
847        }
848    }
849
850    /**
851     * Gets the fill event history.
852     *
853     * @param callingUid The calling uid
854     *
855     * @return The history or {@code null} if there is none.
856     */
857    FillEventHistory getFillEventHistory(int callingUid) {
858        synchronized (mLock) {
859            if (mEventHistory != null
860                    && isCalledByServiceLocked("getFillEventHistory", callingUid)) {
861                return mEventHistory;
862            }
863        }
864        return null;
865    }
866
867    // Called by Session - does not need to check uid
868    UserData getUserData() {
869        synchronized (mLock) {
870            return mUserData;
871        }
872    }
873
874    // Called by AutofillManager
875    UserData getUserData(int callingUid) {
876        synchronized (mLock) {
877            if (isCalledByServiceLocked("getUserData", callingUid)) {
878                return mUserData;
879            }
880        }
881        return null;
882    }
883
884    // Called by AutofillManager
885    void setUserData(int callingUid, UserData userData) {
886        synchronized (mLock) {
887            if (!isCalledByServiceLocked("setUserData", callingUid)) {
888                return;
889            }
890            mUserData = userData;
891            // Log it
892            int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length;
893            mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED,
894                    getServicePackageName(), null)
895                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, numberFields));
896        }
897    }
898
899    @GuardedBy("mLock")
900    private boolean isCalledByServiceLocked(String methodName, int callingUid) {
901        if (getServiceUidLocked() != callingUid) {
902            Slog.w(TAG, methodName + "() called by UID " + callingUid
903                    + ", but service UID is " + getServiceUidLocked());
904            return false;
905        }
906        return true;
907    }
908
909    @GuardedBy("mLock")
910    void dumpLocked(String prefix, PrintWriter pw) {
911        final String prefix2 = prefix + "  ";
912
913        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
914        pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
915        pw.print(prefix); pw.print("Autofill Service Info: ");
916        if (mInfo == null) {
917            pw.println("N/A");
918        } else {
919            pw.println();
920            mInfo.dump(prefix2, pw);
921            pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel());
922        }
923        pw.print(prefix); pw.print("Component from settings: ");
924            pw.println(getComponentNameFromSettings());
925        pw.print(prefix); pw.print("Default component: ");
926            pw.println(mContext.getString(R.string.config_defaultAutofillService));
927        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
928        pw.print(prefix); pw.print("Field classification enabled: ");
929            pw.println(isFieldClassificationEnabledLocked());
930        pw.print(prefix); pw.print("Compat pkgs: ");
931        final ArrayMap<String, Long> compatPkgs = getCompatibilityPackagesLocked();
932        if (compatPkgs == null) {
933            pw.println("N/A");
934        } else {
935            pw.println(compatPkgs);
936        }
937        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
938        pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
939
940        pw.print(prefix); pw.print("Disabled apps: ");
941
942        if (mDisabledApps == null) {
943            pw.println("N/A");
944        } else {
945            final int size = mDisabledApps.size();
946            pw.println(size);
947            final StringBuilder builder = new StringBuilder();
948            final long now = SystemClock.elapsedRealtime();
949            for (int i = 0; i < size; i++) {
950                final String packageName = mDisabledApps.keyAt(i);
951                final long expiration = mDisabledApps.valueAt(i);
952                 builder.append(prefix).append(prefix)
953                     .append(i).append(". ").append(packageName).append(": ");
954                 TimeUtils.formatDuration((expiration - now), builder);
955                 builder.append('\n');
956             }
957             pw.println(builder);
958        }
959
960        pw.print(prefix); pw.print("Disabled activities: ");
961
962        if (mDisabledActivities == null) {
963            pw.println("N/A");
964        } else {
965            final int size = mDisabledActivities.size();
966            pw.println(size);
967            final StringBuilder builder = new StringBuilder();
968            final long now = SystemClock.elapsedRealtime();
969            for (int i = 0; i < size; i++) {
970                final ComponentName component = mDisabledActivities.keyAt(i);
971                final long expiration = mDisabledActivities.valueAt(i);
972                 builder.append(prefix).append(prefix)
973                     .append(i).append(". ").append(component).append(": ");
974                 TimeUtils.formatDuration((expiration - now), builder);
975                 builder.append('\n');
976             }
977             pw.println(builder);
978        }
979
980        final int size = mSessions.size();
981        if (size == 0) {
982            pw.print(prefix); pw.println("No sessions");
983        } else {
984            pw.print(prefix); pw.print(size); pw.println(" sessions:");
985            for (int i = 0; i < size; i++) {
986                pw.print(prefix); pw.print("#"); pw.println(i + 1);
987                mSessions.valueAt(i).dumpLocked(prefix2, pw);
988            }
989        }
990
991        pw.print(prefix); pw.print("Clients: ");
992        if (mClients == null) {
993            pw.println("N/A");
994        } else {
995            pw.println();
996            mClients.dump(pw, prefix2);
997        }
998
999        if (mEventHistory == null || mEventHistory.getEvents() == null
1000                || mEventHistory.getEvents().size() == 0) {
1001            pw.print(prefix); pw.println("No event on last fill response");
1002        } else {
1003            pw.print(prefix); pw.println("Events of last fill response:");
1004            pw.print(prefix);
1005
1006            int numEvents = mEventHistory.getEvents().size();
1007            for (int i = 0; i < numEvents; i++) {
1008                final Event event = mEventHistory.getEvents().get(i);
1009                pw.println("  " + i + ": eventType=" + event.getType() + " datasetId="
1010                        + event.getDatasetId());
1011            }
1012        }
1013
1014        pw.print(prefix); pw.print("User data: ");
1015        if (mUserData == null) {
1016            pw.println("N/A");
1017        } else {
1018            pw.println();
1019            mUserData.dump(prefix2, pw);
1020        }
1021
1022        pw.print(prefix); pw.println("Field Classification strategy: ");
1023        mFieldClassificationStrategy.dump(prefix2, pw);
1024    }
1025
1026    @GuardedBy("mLock")
1027    void destroySessionsLocked() {
1028        if (mSessions.size() == 0) {
1029            mUi.destroyAll(null, null, false);
1030            return;
1031        }
1032        while (mSessions.size() > 0) {
1033            mSessions.valueAt(0).forceRemoveSelfLocked();
1034        }
1035    }
1036
1037    // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
1038    @GuardedBy("mLock")
1039    void destroyFinishedSessionsLocked() {
1040        final int sessionCount = mSessions.size();
1041        for (int i = sessionCount - 1; i >= 0; i--) {
1042            final Session session = mSessions.valueAt(i);
1043            if (session.isSavingLocked()) {
1044                if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
1045                session.forceRemoveSelfLocked();
1046            }
1047        }
1048    }
1049
1050    @GuardedBy("mLock")
1051    void listSessionsLocked(ArrayList<String> output) {
1052        final int numSessions = mSessions.size();
1053        for (int i = 0; i < numSessions; i++) {
1054            output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
1055                    : null) + ":" + mSessions.keyAt(i));
1056        }
1057    }
1058
1059    @GuardedBy("mLock")
1060    @Nullable ArrayMap<String, Long> getCompatibilityPackagesLocked() {
1061        if (mInfo != null) {
1062            return mInfo.getCompatibilityPackages();
1063        }
1064        return null;
1065    }
1066
1067    private void sendStateToClients(boolean resetClient) {
1068        final RemoteCallbackList<IAutoFillManagerClient> clients;
1069        final int userClientCount;
1070        synchronized (mLock) {
1071            if (mClients == null) {
1072                return;
1073            }
1074            clients = mClients;
1075            userClientCount = clients.beginBroadcast();
1076        }
1077        try {
1078            for (int i = 0; i < userClientCount; i++) {
1079                final IAutoFillManagerClient client = clients.getBroadcastItem(i);
1080                try {
1081                    final boolean resetSession;
1082                    final boolean isEnabled;
1083                    synchronized (mLock) {
1084                        resetSession = resetClient || isClientSessionDestroyedLocked(client);
1085                        isEnabled = isEnabledLocked();
1086                    }
1087                    int flags = 0;
1088                    if (isEnabled) {
1089                        flags |= AutofillManager.SET_STATE_FLAG_ENABLED;
1090                    }
1091                    if (resetSession) {
1092                        flags |= AutofillManager.SET_STATE_FLAG_RESET_SESSION;
1093                    }
1094                    if (resetClient) {
1095                        flags |= AutofillManager.SET_STATE_FLAG_RESET_CLIENT;
1096                    }
1097                    if (sDebug) {
1098                        flags |= AutofillManager.SET_STATE_FLAG_DEBUG;
1099                    }
1100                    if (sVerbose) {
1101                        flags |= AutofillManager.SET_STATE_FLAG_VERBOSE;
1102                    }
1103                    client.setState(flags);
1104                } catch (RemoteException re) {
1105                    /* ignore */
1106                }
1107            }
1108        } finally {
1109            clients.finishBroadcast();
1110        }
1111    }
1112
1113    @GuardedBy("mLock")
1114    private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
1115        final int sessionCount = mSessions.size();
1116        for (int i = 0; i < sessionCount; i++) {
1117            final Session session = mSessions.valueAt(i);
1118            if (session.getClient().equals(client)) {
1119                return session.isDestroyed();
1120            }
1121        }
1122        return true;
1123    }
1124
1125    @GuardedBy("mLock")
1126    boolean isEnabledLocked() {
1127        return mSetupComplete && mInfo != null && !mDisabled;
1128    }
1129
1130    /**
1131     * Called by {@link Session} when service asked to disable autofill for an app.
1132     */
1133    void disableAutofillForApp(@NonNull String packageName, long duration, boolean compatMode) {
1134        synchronized (mLock) {
1135            if (mDisabledApps == null) {
1136                mDisabledApps = new ArrayMap<>(1);
1137            }
1138            long expiration = SystemClock.elapsedRealtime() + duration;
1139            // Protect it against overflow
1140            if (expiration < 0) {
1141                expiration = Long.MAX_VALUE;
1142            }
1143            mDisabledApps.put(packageName, expiration);
1144            int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
1145            mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
1146                    packageName, getServicePackageName(), compatMode)
1147                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration));
1148        }
1149    }
1150
1151    /**
1152     * Called by {@link Session} when service asked to disable autofill an app.
1153     */
1154    void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
1155            boolean compatMode) {
1156        synchronized (mLock) {
1157            if (mDisabledActivities == null) {
1158                mDisabledActivities = new ArrayMap<>(1);
1159            }
1160            long expiration = SystemClock.elapsedRealtime() + duration;
1161            // Protect it against overflow
1162            if (expiration < 0) {
1163                expiration = Long.MAX_VALUE;
1164            }
1165            mDisabledActivities.put(componentName, expiration);
1166            final int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
1167            // NOTE: not using Helper.newLogMaker() because we're setting the componentName instead
1168            // of package name
1169            mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY)
1170                    .setComponentName(componentName)
1171                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName())
1172                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)
1173                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, compatMode ? 1 : 0));
1174        }
1175    }
1176
1177    /**
1178     * Checks if autofill is disabled by service to the given activity.
1179     */
1180    @GuardedBy("mLock")
1181    private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
1182        // Check activities first.
1183        long elapsedTime = 0;
1184        if (mDisabledActivities != null) {
1185            elapsedTime = SystemClock.elapsedRealtime();
1186            final Long expiration = mDisabledActivities.get(componentName);
1187            if (expiration != null) {
1188                if (expiration >= elapsedTime) return true;
1189                // Restriction expired - clean it up.
1190                if (sVerbose) {
1191                    Slog.v(TAG, "Removing " + componentName.toShortString()
1192                        + " from disabled list");
1193                }
1194                mDisabledActivities.remove(componentName);
1195            }
1196        }
1197
1198        // Then check apps.
1199        final String packageName = componentName.getPackageName();
1200        if (mDisabledApps == null) return false;
1201
1202        final Long expiration = mDisabledApps.get(packageName);
1203        if (expiration == null) return false;
1204
1205        if (elapsedTime == 0) {
1206            elapsedTime = SystemClock.elapsedRealtime();
1207        }
1208
1209        if (expiration >= elapsedTime) return true;
1210
1211        // Restriction expired - clean it up.
1212        if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
1213        mDisabledApps.remove(packageName);
1214        return false;
1215    }
1216
1217    // Called by AutofillManager, checks UID.
1218    boolean isFieldClassificationEnabled(int callingUid) {
1219        synchronized (mLock) {
1220            if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) {
1221                return false;
1222            }
1223            return isFieldClassificationEnabledLocked();
1224        }
1225    }
1226
1227    // Called by internally, no need to check UID.
1228    boolean isFieldClassificationEnabledLocked() {
1229        return Settings.Secure.getIntForUser(
1230                mContext.getContentResolver(),
1231                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
1232                mUserId) == 1;
1233    }
1234
1235    FieldClassificationStrategy getFieldClassificationStrategy() {
1236        return mFieldClassificationStrategy;
1237    }
1238
1239    String[] getAvailableFieldClassificationAlgorithms(int callingUid) {
1240        synchronized (mLock) {
1241            if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
1242                return null;
1243            }
1244        }
1245        return mFieldClassificationStrategy.getAvailableAlgorithms();
1246    }
1247
1248    String getDefaultFieldClassificationAlgorithm(int callingUid) {
1249        synchronized (mLock) {
1250            if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
1251                return null;
1252            }
1253        }
1254        return mFieldClassificationStrategy.getDefaultAlgorithm();
1255    }
1256
1257    @Override
1258    public String toString() {
1259        return "AutofillManagerServiceImpl: [userId=" + mUserId
1260                + ", component=" + (mInfo != null
1261                ? mInfo.getServiceInfo().getComponentName() : null) + "]";
1262    }
1263
1264    /** Task used to prune abandoned session */
1265    private class PruneTask extends AsyncTask<Void, Void, Void> {
1266        @Override
1267        protected Void doInBackground(Void... ignored) {
1268            int numSessionsToRemove;
1269
1270            SparseArray<IBinder> sessionsToRemove;
1271
1272            synchronized (mLock) {
1273                numSessionsToRemove = mSessions.size();
1274                sessionsToRemove = new SparseArray<>(numSessionsToRemove);
1275
1276                for (int i = 0; i < numSessionsToRemove; i++) {
1277                    Session session = mSessions.valueAt(i);
1278
1279                    sessionsToRemove.put(session.id, session.getActivityTokenLocked());
1280                }
1281            }
1282
1283            IActivityManager am = ActivityManager.getService();
1284
1285            // Only remove sessions which's activities are not known to the activity manager anymore
1286            for (int i = 0; i < numSessionsToRemove; i++) {
1287                try {
1288                    // The activity manager cannot resolve activities that have been removed
1289                    if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) {
1290                        sessionsToRemove.removeAt(i);
1291                        i--;
1292                        numSessionsToRemove--;
1293                    }
1294                } catch (RemoteException e) {
1295                    Slog.w(TAG, "Cannot figure out if activity is finished", e);
1296                }
1297            }
1298
1299            synchronized (mLock) {
1300                for (int i = 0; i < numSessionsToRemove; i++) {
1301                    Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i));
1302
1303                    if (sessionToRemove != null && sessionsToRemove.valueAt(i)
1304                            == sessionToRemove.getActivityTokenLocked()) {
1305                        if (sessionToRemove.isSavingLocked()) {
1306                            if (sVerbose) {
1307                                Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
1308                            }
1309                        } else {
1310                            if (sDebug) {
1311                                Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
1312                                    + sessionToRemove.getActivityTokenLocked() + ")");
1313                            }
1314                            sessionToRemove.removeSelfLocked();
1315                        }
1316                    }
1317                }
1318            }
1319
1320            return null;
1321        }
1322    }
1323}
1324