AutofillManagerServiceImpl.java revision 212b1614f4328a66c58a27899fe75583c753ef35
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 ComponentName appComponentName, boolean compatMode) {
784        logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
785                changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
786                manuallyFilledDatasetIds, null, null, appComponentName, 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 ComponentName appComponentName, 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                        + ", appComponentName=" + appComponentName.toShortString()
811                        + ", compatMode=" + compatMode);
812            }
813            AutofillId[] detectedFieldsIds = null;
814            FieldClassification[] detectedFieldClassifications = null;
815            if (detectedFieldIdsList != null) {
816                detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
817                detectedFieldIdsList.toArray(detectedFieldsIds);
818                detectedFieldClassifications =
819                        new FieldClassification[detectedFieldClassificationsList.size()];
820                detectedFieldClassificationsList.toArray(detectedFieldClassifications);
821
822                final int numberFields = detectedFieldsIds.length;
823                int totalSize = 0;
824                float totalScore = 0;
825                for (int i = 0; i < numberFields; i++) {
826                    final FieldClassification fc = detectedFieldClassifications[i];
827                    final List<Match> matches = fc.getMatches();
828                    final int size = matches.size();
829                    totalSize += size;
830                    for (int j = 0; j < size; j++) {
831                        totalScore += matches.get(j).getScore();
832                    }
833                }
834
835                final int averageScore = (int) ((totalScore * 100) / totalSize);
836                mMetricsLogger.write(Helper
837                        .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
838                                appComponentName, getServicePackageName(), compatMode)
839                        .setCounterValue(numberFields)
840                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
841                                averageScore));
842            }
843            mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
844                    clientState, selectedDatasets, ignoredDatasets,
845                    changedFieldIds, changedDatasetIds,
846                    manuallyFilledFieldIds, manuallyFilledDatasetIds,
847                    detectedFieldsIds, detectedFieldClassifications));
848        }
849    }
850
851    /**
852     * Gets the fill event history.
853     *
854     * @param callingUid The calling uid
855     *
856     * @return The history or {@code null} if there is none.
857     */
858    FillEventHistory getFillEventHistory(int callingUid) {
859        synchronized (mLock) {
860            if (mEventHistory != null
861                    && isCalledByServiceLocked("getFillEventHistory", callingUid)) {
862                return mEventHistory;
863            }
864        }
865        return null;
866    }
867
868    // Called by Session - does not need to check uid
869    UserData getUserData() {
870        synchronized (mLock) {
871            return mUserData;
872        }
873    }
874
875    // Called by AutofillManager
876    UserData getUserData(int callingUid) {
877        synchronized (mLock) {
878            if (isCalledByServiceLocked("getUserData", callingUid)) {
879                return mUserData;
880            }
881        }
882        return null;
883    }
884
885    // Called by AutofillManager
886    void setUserData(int callingUid, UserData userData) {
887        synchronized (mLock) {
888            if (!isCalledByServiceLocked("setUserData", callingUid)) {
889                return;
890            }
891            mUserData = userData;
892            // Log it
893            final int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length;
894            // NOTE: contrary to most metrics, the service name is logged as the main package name
895            // here, not as MetricsEvent.FIELD_AUTOFILL_SERVICE
896            mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED)
897                    .setPackageName(getServicePackageName())
898                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, numberFields));
899        }
900    }
901
902    @GuardedBy("mLock")
903    private boolean isCalledByServiceLocked(String methodName, int callingUid) {
904        if (getServiceUidLocked() != callingUid) {
905            Slog.w(TAG, methodName + "() called by UID " + callingUid
906                    + ", but service UID is " + getServiceUidLocked());
907            return false;
908        }
909        return true;
910    }
911
912    @GuardedBy("mLock")
913    void dumpLocked(String prefix, PrintWriter pw) {
914        final String prefix2 = prefix + "  ";
915
916        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
917        pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
918        pw.print(prefix); pw.print("Autofill Service Info: ");
919        if (mInfo == null) {
920            pw.println("N/A");
921        } else {
922            pw.println();
923            mInfo.dump(prefix2, pw);
924            pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel());
925        }
926        pw.print(prefix); pw.print("Component from settings: ");
927            pw.println(getComponentNameFromSettings());
928        pw.print(prefix); pw.print("Default component: ");
929            pw.println(mContext.getString(R.string.config_defaultAutofillService));
930        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
931        pw.print(prefix); pw.print("Field classification enabled: ");
932            pw.println(isFieldClassificationEnabledLocked());
933        pw.print(prefix); pw.print("Compat pkgs: ");
934        final ArrayMap<String, Long> compatPkgs = getCompatibilityPackagesLocked();
935        if (compatPkgs == null) {
936            pw.println("N/A");
937        } else {
938            pw.println(compatPkgs);
939        }
940        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
941        pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
942
943        pw.print(prefix); pw.print("Disabled apps: ");
944
945        if (mDisabledApps == null) {
946            pw.println("N/A");
947        } else {
948            final int size = mDisabledApps.size();
949            pw.println(size);
950            final StringBuilder builder = new StringBuilder();
951            final long now = SystemClock.elapsedRealtime();
952            for (int i = 0; i < size; i++) {
953                final String packageName = mDisabledApps.keyAt(i);
954                final long expiration = mDisabledApps.valueAt(i);
955                 builder.append(prefix).append(prefix)
956                     .append(i).append(". ").append(packageName).append(": ");
957                 TimeUtils.formatDuration((expiration - now), builder);
958                 builder.append('\n');
959             }
960             pw.println(builder);
961        }
962
963        pw.print(prefix); pw.print("Disabled activities: ");
964
965        if (mDisabledActivities == null) {
966            pw.println("N/A");
967        } else {
968            final int size = mDisabledActivities.size();
969            pw.println(size);
970            final StringBuilder builder = new StringBuilder();
971            final long now = SystemClock.elapsedRealtime();
972            for (int i = 0; i < size; i++) {
973                final ComponentName component = mDisabledActivities.keyAt(i);
974                final long expiration = mDisabledActivities.valueAt(i);
975                 builder.append(prefix).append(prefix)
976                     .append(i).append(". ").append(component).append(": ");
977                 TimeUtils.formatDuration((expiration - now), builder);
978                 builder.append('\n');
979             }
980             pw.println(builder);
981        }
982
983        final int size = mSessions.size();
984        if (size == 0) {
985            pw.print(prefix); pw.println("No sessions");
986        } else {
987            pw.print(prefix); pw.print(size); pw.println(" sessions:");
988            for (int i = 0; i < size; i++) {
989                pw.print(prefix); pw.print("#"); pw.println(i + 1);
990                mSessions.valueAt(i).dumpLocked(prefix2, pw);
991            }
992        }
993
994        pw.print(prefix); pw.print("Clients: ");
995        if (mClients == null) {
996            pw.println("N/A");
997        } else {
998            pw.println();
999            mClients.dump(pw, prefix2);
1000        }
1001
1002        if (mEventHistory == null || mEventHistory.getEvents() == null
1003                || mEventHistory.getEvents().size() == 0) {
1004            pw.print(prefix); pw.println("No event on last fill response");
1005        } else {
1006            pw.print(prefix); pw.println("Events of last fill response:");
1007            pw.print(prefix);
1008
1009            int numEvents = mEventHistory.getEvents().size();
1010            for (int i = 0; i < numEvents; i++) {
1011                final Event event = mEventHistory.getEvents().get(i);
1012                pw.println("  " + i + ": eventType=" + event.getType() + " datasetId="
1013                        + event.getDatasetId());
1014            }
1015        }
1016
1017        pw.print(prefix); pw.print("User data: ");
1018        if (mUserData == null) {
1019            pw.println("N/A");
1020        } else {
1021            pw.println();
1022            mUserData.dump(prefix2, pw);
1023        }
1024
1025        pw.print(prefix); pw.println("Field Classification strategy: ");
1026        mFieldClassificationStrategy.dump(prefix2, pw);
1027    }
1028
1029    @GuardedBy("mLock")
1030    void destroySessionsLocked() {
1031        if (mSessions.size() == 0) {
1032            mUi.destroyAll(null, null, false);
1033            return;
1034        }
1035        while (mSessions.size() > 0) {
1036            mSessions.valueAt(0).forceRemoveSelfLocked();
1037        }
1038    }
1039
1040    // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
1041    @GuardedBy("mLock")
1042    void destroyFinishedSessionsLocked() {
1043        final int sessionCount = mSessions.size();
1044        for (int i = sessionCount - 1; i >= 0; i--) {
1045            final Session session = mSessions.valueAt(i);
1046            if (session.isSavingLocked()) {
1047                if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
1048                session.forceRemoveSelfLocked();
1049            }
1050        }
1051    }
1052
1053    @GuardedBy("mLock")
1054    void listSessionsLocked(ArrayList<String> output) {
1055        final int numSessions = mSessions.size();
1056        for (int i = 0; i < numSessions; i++) {
1057            output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
1058                    : null) + ":" + mSessions.keyAt(i));
1059        }
1060    }
1061
1062    @GuardedBy("mLock")
1063    @Nullable ArrayMap<String, Long> getCompatibilityPackagesLocked() {
1064        if (mInfo != null) {
1065            return mInfo.getCompatibilityPackages();
1066        }
1067        return null;
1068    }
1069
1070    private void sendStateToClients(boolean resetClient) {
1071        final RemoteCallbackList<IAutoFillManagerClient> clients;
1072        final int userClientCount;
1073        synchronized (mLock) {
1074            if (mClients == null) {
1075                return;
1076            }
1077            clients = mClients;
1078            userClientCount = clients.beginBroadcast();
1079        }
1080        try {
1081            for (int i = 0; i < userClientCount; i++) {
1082                final IAutoFillManagerClient client = clients.getBroadcastItem(i);
1083                try {
1084                    final boolean resetSession;
1085                    final boolean isEnabled;
1086                    synchronized (mLock) {
1087                        resetSession = resetClient || isClientSessionDestroyedLocked(client);
1088                        isEnabled = isEnabledLocked();
1089                    }
1090                    int flags = 0;
1091                    if (isEnabled) {
1092                        flags |= AutofillManager.SET_STATE_FLAG_ENABLED;
1093                    }
1094                    if (resetSession) {
1095                        flags |= AutofillManager.SET_STATE_FLAG_RESET_SESSION;
1096                    }
1097                    if (resetClient) {
1098                        flags |= AutofillManager.SET_STATE_FLAG_RESET_CLIENT;
1099                    }
1100                    if (sDebug) {
1101                        flags |= AutofillManager.SET_STATE_FLAG_DEBUG;
1102                    }
1103                    if (sVerbose) {
1104                        flags |= AutofillManager.SET_STATE_FLAG_VERBOSE;
1105                    }
1106                    client.setState(flags);
1107                } catch (RemoteException re) {
1108                    /* ignore */
1109                }
1110            }
1111        } finally {
1112            clients.finishBroadcast();
1113        }
1114    }
1115
1116    @GuardedBy("mLock")
1117    private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
1118        final int sessionCount = mSessions.size();
1119        for (int i = 0; i < sessionCount; i++) {
1120            final Session session = mSessions.valueAt(i);
1121            if (session.getClient().equals(client)) {
1122                return session.isDestroyed();
1123            }
1124        }
1125        return true;
1126    }
1127
1128    @GuardedBy("mLock")
1129    boolean isEnabledLocked() {
1130        return mSetupComplete && mInfo != null && !mDisabled;
1131    }
1132
1133    /**
1134     * Called by {@link Session} when service asked to disable autofill for an app.
1135     */
1136    void disableAutofillForApp(@NonNull String packageName, long duration, boolean compatMode) {
1137        synchronized (mLock) {
1138            if (mDisabledApps == null) {
1139                mDisabledApps = new ArrayMap<>(1);
1140            }
1141            long expiration = SystemClock.elapsedRealtime() + duration;
1142            // Protect it against overflow
1143            if (expiration < 0) {
1144                expiration = Long.MAX_VALUE;
1145            }
1146            mDisabledApps.put(packageName, expiration);
1147            int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
1148            mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
1149                    packageName, getServicePackageName(), compatMode)
1150                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration));
1151        }
1152    }
1153
1154    /**
1155     * Called by {@link Session} when service asked to disable autofill an app.
1156     */
1157    void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
1158            boolean compatMode) {
1159        synchronized (mLock) {
1160            if (mDisabledActivities == null) {
1161                mDisabledActivities = new ArrayMap<>(1);
1162            }
1163            long expiration = SystemClock.elapsedRealtime() + duration;
1164            // Protect it against overflow
1165            if (expiration < 0) {
1166                expiration = Long.MAX_VALUE;
1167            }
1168            mDisabledActivities.put(componentName, expiration);
1169            final int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
1170            // NOTE: not using Helper.newLogMaker() because we're setting the componentName instead
1171            // of package name
1172            mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY)
1173                    .setComponentName(componentName)
1174                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName())
1175                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)
1176                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, compatMode ? 1 : 0));
1177        }
1178    }
1179
1180    /**
1181     * Checks if autofill is disabled by service to the given activity.
1182     */
1183    @GuardedBy("mLock")
1184    private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
1185        // Check activities first.
1186        long elapsedTime = 0;
1187        if (mDisabledActivities != null) {
1188            elapsedTime = SystemClock.elapsedRealtime();
1189            final Long expiration = mDisabledActivities.get(componentName);
1190            if (expiration != null) {
1191                if (expiration >= elapsedTime) return true;
1192                // Restriction expired - clean it up.
1193                if (sVerbose) {
1194                    Slog.v(TAG, "Removing " + componentName.toShortString()
1195                        + " from disabled list");
1196                }
1197                mDisabledActivities.remove(componentName);
1198            }
1199        }
1200
1201        // Then check apps.
1202        final String packageName = componentName.getPackageName();
1203        if (mDisabledApps == null) return false;
1204
1205        final Long expiration = mDisabledApps.get(packageName);
1206        if (expiration == null) return false;
1207
1208        if (elapsedTime == 0) {
1209            elapsedTime = SystemClock.elapsedRealtime();
1210        }
1211
1212        if (expiration >= elapsedTime) return true;
1213
1214        // Restriction expired - clean it up.
1215        if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
1216        mDisabledApps.remove(packageName);
1217        return false;
1218    }
1219
1220    // Called by AutofillManager, checks UID.
1221    boolean isFieldClassificationEnabled(int callingUid) {
1222        synchronized (mLock) {
1223            if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) {
1224                return false;
1225            }
1226            return isFieldClassificationEnabledLocked();
1227        }
1228    }
1229
1230    // Called by internally, no need to check UID.
1231    boolean isFieldClassificationEnabledLocked() {
1232        return Settings.Secure.getIntForUser(
1233                mContext.getContentResolver(),
1234                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
1235                mUserId) == 1;
1236    }
1237
1238    FieldClassificationStrategy getFieldClassificationStrategy() {
1239        return mFieldClassificationStrategy;
1240    }
1241
1242    String[] getAvailableFieldClassificationAlgorithms(int callingUid) {
1243        synchronized (mLock) {
1244            if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
1245                return null;
1246            }
1247        }
1248        return mFieldClassificationStrategy.getAvailableAlgorithms();
1249    }
1250
1251    String getDefaultFieldClassificationAlgorithm(int callingUid) {
1252        synchronized (mLock) {
1253            if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
1254                return null;
1255            }
1256        }
1257        return mFieldClassificationStrategy.getDefaultAlgorithm();
1258    }
1259
1260    @Override
1261    public String toString() {
1262        return "AutofillManagerServiceImpl: [userId=" + mUserId
1263                + ", component=" + (mInfo != null
1264                ? mInfo.getServiceInfo().getComponentName() : null) + "]";
1265    }
1266
1267    /** Task used to prune abandoned session */
1268    private class PruneTask extends AsyncTask<Void, Void, Void> {
1269        @Override
1270        protected Void doInBackground(Void... ignored) {
1271            int numSessionsToRemove;
1272
1273            SparseArray<IBinder> sessionsToRemove;
1274
1275            synchronized (mLock) {
1276                numSessionsToRemove = mSessions.size();
1277                sessionsToRemove = new SparseArray<>(numSessionsToRemove);
1278
1279                for (int i = 0; i < numSessionsToRemove; i++) {
1280                    Session session = mSessions.valueAt(i);
1281
1282                    sessionsToRemove.put(session.id, session.getActivityTokenLocked());
1283                }
1284            }
1285
1286            IActivityManager am = ActivityManager.getService();
1287
1288            // Only remove sessions which's activities are not known to the activity manager anymore
1289            for (int i = 0; i < numSessionsToRemove; i++) {
1290                try {
1291                    // The activity manager cannot resolve activities that have been removed
1292                    if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) {
1293                        sessionsToRemove.removeAt(i);
1294                        i--;
1295                        numSessionsToRemove--;
1296                    }
1297                } catch (RemoteException e) {
1298                    Slog.w(TAG, "Cannot figure out if activity is finished", e);
1299                }
1300            }
1301
1302            synchronized (mLock) {
1303                for (int i = 0; i < numSessionsToRemove; i++) {
1304                    Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i));
1305
1306                    if (sessionToRemove != null && sessionsToRemove.valueAt(i)
1307                            == sessionToRemove.getActivityTokenLocked()) {
1308                        if (sessionToRemove.isSavingLocked()) {
1309                            if (sVerbose) {
1310                                Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
1311                            }
1312                        } else {
1313                            if (sDebug) {
1314                                Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
1315                                    + sessionToRemove.getActivityTokenLocked() + ")");
1316                            }
1317                            sessionToRemove.removeSelfLocked();
1318                        }
1319                    }
1320                }
1321            }
1322
1323            return null;
1324        }
1325    }
1326}
1327