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
541            // NOTE: not using Helper.newLogMaker() because we don't have the session id
542            final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT)
543                    .setPackageName(callingPackage)
544                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName())
545                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
546                            componentName == null ? "null" : componentName.flattenToShortString());
547            if (compatMode) {
548                log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
549            }
550            mMetricsLogger.write(log);
551
552            throw new SecurityException("Invalid component: " + componentName);
553        }
554    }
555
556    /**
557     * Restores a session after an activity was temporarily destroyed.
558     *
559     * @param sessionId The id of the session to restore
560     * @param uid UID of the process that tries to restore the session
561     * @param activityToken The new instance of the activity
562     * @param appCallback The callbacks to the activity
563     */
564    boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken,
565            @NonNull IBinder appCallback) {
566        final Session session = mSessions.get(sessionId);
567
568        if (session == null || uid != session.uid) {
569            return false;
570        } else {
571            session.switchActivity(activityToken, appCallback);
572            return true;
573        }
574    }
575
576    /**
577     * Updates a session and returns whether it should be restarted.
578     */
579    @GuardedBy("mLock")
580    boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
581            AutofillValue value, int action, int flags) {
582        final Session session = mSessions.get(sessionId);
583        if (session == null || session.uid != uid) {
584            if ((flags & FLAG_MANUAL_REQUEST) != 0) {
585                if (sDebug) {
586                    Slog.d(TAG, "restarting session " + sessionId + " due to manual request on "
587                            + autofillId);
588                }
589                return true;
590            }
591            if (sVerbose) {
592                Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId
593                        + "(" + uid + ")");
594            }
595            return false;
596        }
597
598        session.updateLocked(autofillId, virtualBounds, value, action, flags);
599        return false;
600    }
601
602    @GuardedBy("mLock")
603    void removeSessionLocked(int sessionId) {
604        mSessions.remove(sessionId);
605    }
606
607    void handleSessionSave(Session session) {
608        synchronized (mLock) {
609            if (mSessions.get(session.id) == null) {
610                Slog.w(TAG, "handleSessionSave(): already gone: " + session.id);
611
612                return;
613            }
614            session.callSaveLocked();
615        }
616    }
617
618    void onPendingSaveUi(int operation, @NonNull IBinder token) {
619        if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
620        synchronized (mLock) {
621            final int sessionCount = mSessions.size();
622            for (int i = sessionCount - 1; i >= 0; i--) {
623                final Session session = mSessions.valueAt(i);
624                if (session.isSaveUiPendingForTokenLocked(token)) {
625                    session.onPendingSaveUi(operation, token);
626                    return;
627                }
628            }
629        }
630        if (sDebug) {
631            Slog.d(TAG, "No pending Save UI for token " + token + " and operation "
632                    + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_",
633                            operation));
634        }
635    }
636
637    @GuardedBy("mLock")
638    void handlePackageUpdateLocked(String packageName) {
639        final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
640        if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
641            resetExtServiceLocked();
642        }
643    }
644
645    @GuardedBy("mLock")
646    void resetExtServiceLocked() {
647        if (sVerbose) Slog.v(TAG, "reset autofill service.");
648        mFieldClassificationStrategy.reset();
649    }
650
651    @GuardedBy("mLock")
652    void destroyLocked() {
653        if (sVerbose) Slog.v(TAG, "destroyLocked()");
654
655        resetExtServiceLocked();
656
657        final int numSessions = mSessions.size();
658        final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions);
659        for (int i = 0; i < numSessions; i++) {
660            final RemoteFillService remoteFillService = mSessions.valueAt(i).destroyLocked();
661            if (remoteFillService != null) {
662                remoteFillServices.add(remoteFillService);
663            }
664        }
665        mSessions.clear();
666        for (int i = 0; i < remoteFillServices.size(); i++) {
667            remoteFillServices.valueAt(i).destroy();
668        }
669
670        sendStateToClients(true);
671        if (mClients != null) {
672            mClients.kill();
673            mClients = null;
674        }
675    }
676
677    @NonNull
678    CharSequence getServiceLabel() {
679        final CharSequence label = mInfo.getServiceInfo().loadSafeLabel(
680                mContext.getPackageManager(), 0 /* do not ellipsize */,
681                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
682        return label;
683    }
684
685    @NonNull
686    Drawable getServiceIcon() {
687        return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
688    }
689
690    /**
691     * Initializes the last fill selection after an autofill service returned a new
692     * {@link FillResponse}.
693     */
694    void setLastResponse(int sessionId, @NonNull FillResponse response) {
695        synchronized (mLock) {
696            mEventHistory = new FillEventHistory(sessionId, response.getClientState());
697        }
698    }
699
700    /**
701     * Resets the last fill selection.
702     */
703    void resetLastResponse() {
704        synchronized (mLock) {
705            mEventHistory = null;
706        }
707    }
708
709    @GuardedBy("mLock")
710    private boolean isValidEventLocked(String method, int sessionId) {
711        if (mEventHistory == null) {
712            Slog.w(TAG, method + ": not logging event because history is null");
713            return false;
714        }
715        if (sessionId != mEventHistory.getSessionId()) {
716            if (sDebug) {
717                Slog.d(TAG, method + ": not logging event for session " + sessionId
718                        + " because tracked session is " + mEventHistory.getSessionId());
719            }
720            return false;
721        }
722        return true;
723    }
724
725    /**
726     * Updates the last fill selection when an authentication was selected.
727     */
728    void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) {
729        synchronized (mLock) {
730            if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
731                mEventHistory.addEvent(
732                        new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
733                                null, null, null, null, null, null));
734            }
735        }
736    }
737
738    /**
739     * Updates the last fill selection when an dataset authentication was selected.
740     */
741    void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
742            @Nullable Bundle clientState) {
743        synchronized (mLock) {
744            if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
745                mEventHistory.addEvent(
746                        new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
747                                clientState, null, null, null, null, null, null, null, null));
748            }
749        }
750    }
751
752    /**
753     * Updates the last fill selection when an save Ui is shown.
754     */
755    void logSaveShown(int sessionId, @Nullable Bundle clientState) {
756        synchronized (mLock) {
757            if (isValidEventLocked("logSaveShown()", sessionId)) {
758                mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
759                        null, null, null, null, null, null, null));
760            }
761        }
762    }
763
764    /**
765     * Updates the last fill response when a dataset was selected.
766     */
767    void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
768            @Nullable Bundle clientState) {
769        synchronized (mLock) {
770            if (isValidEventLocked("logDatasetSelected()", sessionId)) {
771                mEventHistory.addEvent(
772                        new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
773                                null, null, null, null, null, null, null));
774            }
775        }
776    }
777
778    /**
779     * Updates the last fill response when an autofill context is committed.
780     */
781    @GuardedBy("mLock")
782    void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
783            @Nullable ArrayList<String> selectedDatasets,
784            @Nullable ArraySet<String> ignoredDatasets,
785            @Nullable ArrayList<AutofillId> changedFieldIds,
786            @Nullable ArrayList<String> changedDatasetIds,
787            @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
788            @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
789            @NonNull ComponentName appComponentName, boolean compatMode) {
790        logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
791                changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
792                manuallyFilledDatasetIds, null, null, appComponentName, compatMode);
793    }
794
795    @GuardedBy("mLock")
796    void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
797            @Nullable ArrayList<String> selectedDatasets,
798            @Nullable ArraySet<String> ignoredDatasets,
799            @Nullable ArrayList<AutofillId> changedFieldIds,
800            @Nullable ArrayList<String> changedDatasetIds,
801            @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
802            @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
803            @Nullable ArrayList<AutofillId> detectedFieldIdsList,
804            @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
805            @NonNull ComponentName appComponentName, boolean compatMode) {
806        if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
807            if (sVerbose) {
808                Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
809                        + ", selectedDatasets=" + selectedDatasets
810                        + ", ignoredDatasetIds=" + ignoredDatasets
811                        + ", changedAutofillIds=" + changedFieldIds
812                        + ", changedDatasetIds=" + changedDatasetIds
813                        + ", manuallyFilledFieldIds=" + manuallyFilledFieldIds
814                        + ", detectedFieldIds=" + detectedFieldIdsList
815                        + ", detectedFieldClassifications=" + detectedFieldClassificationsList
816                        + ", appComponentName=" + appComponentName.toShortString()
817                        + ", compatMode=" + compatMode);
818            }
819            AutofillId[] detectedFieldsIds = null;
820            FieldClassification[] detectedFieldClassifications = null;
821            if (detectedFieldIdsList != null) {
822                detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
823                detectedFieldIdsList.toArray(detectedFieldsIds);
824                detectedFieldClassifications =
825                        new FieldClassification[detectedFieldClassificationsList.size()];
826                detectedFieldClassificationsList.toArray(detectedFieldClassifications);
827
828                final int numberFields = detectedFieldsIds.length;
829                int totalSize = 0;
830                float totalScore = 0;
831                for (int i = 0; i < numberFields; i++) {
832                    final FieldClassification fc = detectedFieldClassifications[i];
833                    final List<Match> matches = fc.getMatches();
834                    final int size = matches.size();
835                    totalSize += size;
836                    for (int j = 0; j < size; j++) {
837                        totalScore += matches.get(j).getScore();
838                    }
839                }
840
841                final int averageScore = (int) ((totalScore * 100) / totalSize);
842                mMetricsLogger.write(Helper
843                        .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
844                                appComponentName, getServicePackageName(), sessionId, compatMode)
845                        .setCounterValue(numberFields)
846                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
847                                averageScore));
848            }
849            mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
850                    clientState, selectedDatasets, ignoredDatasets,
851                    changedFieldIds, changedDatasetIds,
852                    manuallyFilledFieldIds, manuallyFilledDatasetIds,
853                    detectedFieldsIds, detectedFieldClassifications));
854        }
855    }
856
857    /**
858     * Gets the fill event history.
859     *
860     * @param callingUid The calling uid
861     *
862     * @return The history or {@code null} if there is none.
863     */
864    FillEventHistory getFillEventHistory(int callingUid) {
865        synchronized (mLock) {
866            if (mEventHistory != null
867                    && isCalledByServiceLocked("getFillEventHistory", callingUid)) {
868                return mEventHistory;
869            }
870        }
871        return null;
872    }
873
874    // Called by Session - does not need to check uid
875    UserData getUserData() {
876        synchronized (mLock) {
877            return mUserData;
878        }
879    }
880
881    // Called by AutofillManager
882    UserData getUserData(int callingUid) {
883        synchronized (mLock) {
884            if (isCalledByServiceLocked("getUserData", callingUid)) {
885                return mUserData;
886            }
887        }
888        return null;
889    }
890
891    // Called by AutofillManager
892    void setUserData(int callingUid, UserData userData) {
893        synchronized (mLock) {
894            if (!isCalledByServiceLocked("setUserData", callingUid)) {
895                return;
896            }
897            mUserData = userData;
898            // Log it
899            final int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length;
900            // NOTE: contrary to most metrics, the service name is logged as the main package name
901            // here, not as MetricsEvent.FIELD_AUTOFILL_SERVICE
902            mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED)
903                    .setPackageName(getServicePackageName())
904                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, numberFields));
905        }
906    }
907
908    @GuardedBy("mLock")
909    private boolean isCalledByServiceLocked(String methodName, int callingUid) {
910        if (getServiceUidLocked() != callingUid) {
911            Slog.w(TAG, methodName + "() called by UID " + callingUid
912                    + ", but service UID is " + getServiceUidLocked());
913            return false;
914        }
915        return true;
916    }
917
918    @GuardedBy("mLock")
919    void dumpLocked(String prefix, PrintWriter pw) {
920        final String prefix2 = prefix + "  ";
921
922        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
923        pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
924        pw.print(prefix); pw.print("Autofill Service Info: ");
925        if (mInfo == null) {
926            pw.println("N/A");
927        } else {
928            pw.println();
929            mInfo.dump(prefix2, pw);
930            pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel());
931        }
932        pw.print(prefix); pw.print("Component from settings: ");
933            pw.println(getComponentNameFromSettings());
934        pw.print(prefix); pw.print("Default component: ");
935            pw.println(mContext.getString(R.string.config_defaultAutofillService));
936        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
937        pw.print(prefix); pw.print("Field classification enabled: ");
938            pw.println(isFieldClassificationEnabledLocked());
939        pw.print(prefix); pw.print("Compat pkgs: ");
940        final ArrayMap<String, Long> compatPkgs = getCompatibilityPackagesLocked();
941        if (compatPkgs == null) {
942            pw.println("N/A");
943        } else {
944            pw.println(compatPkgs);
945        }
946        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
947        pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
948
949        pw.print(prefix); pw.print("Disabled apps: ");
950
951        if (mDisabledApps == null) {
952            pw.println("N/A");
953        } else {
954            final int size = mDisabledApps.size();
955            pw.println(size);
956            final StringBuilder builder = new StringBuilder();
957            final long now = SystemClock.elapsedRealtime();
958            for (int i = 0; i < size; i++) {
959                final String packageName = mDisabledApps.keyAt(i);
960                final long expiration = mDisabledApps.valueAt(i);
961                 builder.append(prefix).append(prefix)
962                     .append(i).append(". ").append(packageName).append(": ");
963                 TimeUtils.formatDuration((expiration - now), builder);
964                 builder.append('\n');
965             }
966             pw.println(builder);
967        }
968
969        pw.print(prefix); pw.print("Disabled activities: ");
970
971        if (mDisabledActivities == null) {
972            pw.println("N/A");
973        } else {
974            final int size = mDisabledActivities.size();
975            pw.println(size);
976            final StringBuilder builder = new StringBuilder();
977            final long now = SystemClock.elapsedRealtime();
978            for (int i = 0; i < size; i++) {
979                final ComponentName component = mDisabledActivities.keyAt(i);
980                final long expiration = mDisabledActivities.valueAt(i);
981                 builder.append(prefix).append(prefix)
982                     .append(i).append(". ").append(component).append(": ");
983                 TimeUtils.formatDuration((expiration - now), builder);
984                 builder.append('\n');
985             }
986             pw.println(builder);
987        }
988
989        final int size = mSessions.size();
990        if (size == 0) {
991            pw.print(prefix); pw.println("No sessions");
992        } else {
993            pw.print(prefix); pw.print(size); pw.println(" sessions:");
994            for (int i = 0; i < size; i++) {
995                pw.print(prefix); pw.print("#"); pw.println(i + 1);
996                mSessions.valueAt(i).dumpLocked(prefix2, pw);
997            }
998        }
999
1000        pw.print(prefix); pw.print("Clients: ");
1001        if (mClients == null) {
1002            pw.println("N/A");
1003        } else {
1004            pw.println();
1005            mClients.dump(pw, prefix2);
1006        }
1007
1008        if (mEventHistory == null || mEventHistory.getEvents() == null
1009                || mEventHistory.getEvents().size() == 0) {
1010            pw.print(prefix); pw.println("No event on last fill response");
1011        } else {
1012            pw.print(prefix); pw.println("Events of last fill response:");
1013            pw.print(prefix);
1014
1015            int numEvents = mEventHistory.getEvents().size();
1016            for (int i = 0; i < numEvents; i++) {
1017                final Event event = mEventHistory.getEvents().get(i);
1018                pw.println("  " + i + ": eventType=" + event.getType() + " datasetId="
1019                        + event.getDatasetId());
1020            }
1021        }
1022
1023        pw.print(prefix); pw.print("User data: ");
1024        if (mUserData == null) {
1025            pw.println("N/A");
1026        } else {
1027            pw.println();
1028            mUserData.dump(prefix2, pw);
1029        }
1030
1031        pw.print(prefix); pw.println("Field Classification strategy: ");
1032        mFieldClassificationStrategy.dump(prefix2, pw);
1033    }
1034
1035    @GuardedBy("mLock")
1036    void destroySessionsLocked() {
1037        if (mSessions.size() == 0) {
1038            mUi.destroyAll(null, null, false);
1039            return;
1040        }
1041        while (mSessions.size() > 0) {
1042            mSessions.valueAt(0).forceRemoveSelfLocked();
1043        }
1044    }
1045
1046    // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
1047    @GuardedBy("mLock")
1048    void destroyFinishedSessionsLocked() {
1049        final int sessionCount = mSessions.size();
1050        for (int i = sessionCount - 1; i >= 0; i--) {
1051            final Session session = mSessions.valueAt(i);
1052            if (session.isSavingLocked()) {
1053                if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
1054                session.forceRemoveSelfLocked();
1055            }
1056        }
1057    }
1058
1059    @GuardedBy("mLock")
1060    void listSessionsLocked(ArrayList<String> output) {
1061        final int numSessions = mSessions.size();
1062        for (int i = 0; i < numSessions; i++) {
1063            output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
1064                    : null) + ":" + mSessions.keyAt(i));
1065        }
1066    }
1067
1068    @GuardedBy("mLock")
1069    @Nullable ArrayMap<String, Long> getCompatibilityPackagesLocked() {
1070        if (mInfo != null) {
1071            return mInfo.getCompatibilityPackages();
1072        }
1073        return null;
1074    }
1075
1076    private void sendStateToClients(boolean resetClient) {
1077        final RemoteCallbackList<IAutoFillManagerClient> clients;
1078        final int userClientCount;
1079        synchronized (mLock) {
1080            if (mClients == null) {
1081                return;
1082            }
1083            clients = mClients;
1084            userClientCount = clients.beginBroadcast();
1085        }
1086        try {
1087            for (int i = 0; i < userClientCount; i++) {
1088                final IAutoFillManagerClient client = clients.getBroadcastItem(i);
1089                try {
1090                    final boolean resetSession;
1091                    final boolean isEnabled;
1092                    synchronized (mLock) {
1093                        resetSession = resetClient || isClientSessionDestroyedLocked(client);
1094                        isEnabled = isEnabledLocked();
1095                    }
1096                    int flags = 0;
1097                    if (isEnabled) {
1098                        flags |= AutofillManager.SET_STATE_FLAG_ENABLED;
1099                    }
1100                    if (resetSession) {
1101                        flags |= AutofillManager.SET_STATE_FLAG_RESET_SESSION;
1102                    }
1103                    if (resetClient) {
1104                        flags |= AutofillManager.SET_STATE_FLAG_RESET_CLIENT;
1105                    }
1106                    if (sDebug) {
1107                        flags |= AutofillManager.SET_STATE_FLAG_DEBUG;
1108                    }
1109                    if (sVerbose) {
1110                        flags |= AutofillManager.SET_STATE_FLAG_VERBOSE;
1111                    }
1112                    client.setState(flags);
1113                } catch (RemoteException re) {
1114                    /* ignore */
1115                }
1116            }
1117        } finally {
1118            clients.finishBroadcast();
1119        }
1120    }
1121
1122    @GuardedBy("mLock")
1123    private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
1124        final int sessionCount = mSessions.size();
1125        for (int i = 0; i < sessionCount; i++) {
1126            final Session session = mSessions.valueAt(i);
1127            if (session.getClient().equals(client)) {
1128                return session.isDestroyed();
1129            }
1130        }
1131        return true;
1132    }
1133
1134    @GuardedBy("mLock")
1135    boolean isEnabledLocked() {
1136        return mSetupComplete && mInfo != null && !mDisabled;
1137    }
1138
1139    /**
1140     * Called by {@link Session} when service asked to disable autofill for an app.
1141     */
1142    void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId,
1143            boolean compatMode) {
1144        synchronized (mLock) {
1145            if (mDisabledApps == null) {
1146                mDisabledApps = new ArrayMap<>(1);
1147            }
1148            long expiration = SystemClock.elapsedRealtime() + duration;
1149            // Protect it against overflow
1150            if (expiration < 0) {
1151                expiration = Long.MAX_VALUE;
1152            }
1153            mDisabledApps.put(packageName, expiration);
1154            int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
1155            mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
1156                    packageName, getServicePackageName(), sessionId, compatMode)
1157                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration));
1158        }
1159    }
1160
1161    /**
1162     * Called by {@link Session} when service asked to disable autofill an app.
1163     */
1164    void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
1165            int sessionId, boolean compatMode) {
1166        synchronized (mLock) {
1167            if (mDisabledActivities == null) {
1168                mDisabledActivities = new ArrayMap<>(1);
1169            }
1170            long expiration = SystemClock.elapsedRealtime() + duration;
1171            // Protect it against overflow
1172            if (expiration < 0) {
1173                expiration = Long.MAX_VALUE;
1174            }
1175            mDisabledActivities.put(componentName, expiration);
1176            final int intDuration = duration > Integer.MAX_VALUE
1177                    ? Integer.MAX_VALUE
1178                    : (int) duration;
1179            // NOTE: not using Helper.newLogMaker() because we're setting the componentName instead
1180            // of package name
1181            final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY)
1182                    .setComponentName(componentName)
1183                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName())
1184                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)
1185                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, sessionId);
1186            if (compatMode) {
1187                log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
1188            }
1189            mMetricsLogger.write(log);
1190        }
1191    }
1192
1193    /**
1194     * Checks if autofill is disabled by service to the given activity.
1195     */
1196    @GuardedBy("mLock")
1197    private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
1198        // Check activities first.
1199        long elapsedTime = 0;
1200        if (mDisabledActivities != null) {
1201            elapsedTime = SystemClock.elapsedRealtime();
1202            final Long expiration = mDisabledActivities.get(componentName);
1203            if (expiration != null) {
1204                if (expiration >= elapsedTime) return true;
1205                // Restriction expired - clean it up.
1206                if (sVerbose) {
1207                    Slog.v(TAG, "Removing " + componentName.toShortString()
1208                        + " from disabled list");
1209                }
1210                mDisabledActivities.remove(componentName);
1211            }
1212        }
1213
1214        // Then check apps.
1215        final String packageName = componentName.getPackageName();
1216        if (mDisabledApps == null) return false;
1217
1218        final Long expiration = mDisabledApps.get(packageName);
1219        if (expiration == null) return false;
1220
1221        if (elapsedTime == 0) {
1222            elapsedTime = SystemClock.elapsedRealtime();
1223        }
1224
1225        if (expiration >= elapsedTime) return true;
1226
1227        // Restriction expired - clean it up.
1228        if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
1229        mDisabledApps.remove(packageName);
1230        return false;
1231    }
1232
1233    // Called by AutofillManager, checks UID.
1234    boolean isFieldClassificationEnabled(int callingUid) {
1235        synchronized (mLock) {
1236            if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) {
1237                return false;
1238            }
1239            return isFieldClassificationEnabledLocked();
1240        }
1241    }
1242
1243    // Called by internally, no need to check UID.
1244    boolean isFieldClassificationEnabledLocked() {
1245        return Settings.Secure.getIntForUser(
1246                mContext.getContentResolver(),
1247                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
1248                mUserId) == 1;
1249    }
1250
1251    FieldClassificationStrategy getFieldClassificationStrategy() {
1252        return mFieldClassificationStrategy;
1253    }
1254
1255    String[] getAvailableFieldClassificationAlgorithms(int callingUid) {
1256        synchronized (mLock) {
1257            if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
1258                return null;
1259            }
1260        }
1261        return mFieldClassificationStrategy.getAvailableAlgorithms();
1262    }
1263
1264    String getDefaultFieldClassificationAlgorithm(int callingUid) {
1265        synchronized (mLock) {
1266            if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
1267                return null;
1268            }
1269        }
1270        return mFieldClassificationStrategy.getDefaultAlgorithm();
1271    }
1272
1273    @Override
1274    public String toString() {
1275        return "AutofillManagerServiceImpl: [userId=" + mUserId
1276                + ", component=" + (mInfo != null
1277                ? mInfo.getServiceInfo().getComponentName() : null) + "]";
1278    }
1279
1280    /** Task used to prune abandoned session */
1281    private class PruneTask extends AsyncTask<Void, Void, Void> {
1282        @Override
1283        protected Void doInBackground(Void... ignored) {
1284            int numSessionsToRemove;
1285
1286            SparseArray<IBinder> sessionsToRemove;
1287
1288            synchronized (mLock) {
1289                numSessionsToRemove = mSessions.size();
1290                sessionsToRemove = new SparseArray<>(numSessionsToRemove);
1291
1292                for (int i = 0; i < numSessionsToRemove; i++) {
1293                    Session session = mSessions.valueAt(i);
1294
1295                    sessionsToRemove.put(session.id, session.getActivityTokenLocked());
1296                }
1297            }
1298
1299            IActivityManager am = ActivityManager.getService();
1300
1301            // Only remove sessions which's activities are not known to the activity manager anymore
1302            for (int i = 0; i < numSessionsToRemove; i++) {
1303                try {
1304                    // The activity manager cannot resolve activities that have been removed
1305                    if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) {
1306                        sessionsToRemove.removeAt(i);
1307                        i--;
1308                        numSessionsToRemove--;
1309                    }
1310                } catch (RemoteException e) {
1311                    Slog.w(TAG, "Cannot figure out if activity is finished", e);
1312                }
1313            }
1314
1315            synchronized (mLock) {
1316                for (int i = 0; i < numSessionsToRemove; i++) {
1317                    Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i));
1318
1319                    if (sessionToRemove != null && sessionsToRemove.valueAt(i)
1320                            == sessionToRemove.getActivityTokenLocked()) {
1321                        if (sessionToRemove.isSavingLocked()) {
1322                            if (sVerbose) {
1323                                Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
1324                            }
1325                        } else {
1326                            if (sDebug) {
1327                                Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
1328                                    + sessionToRemove.getActivityTokenLocked() + ")");
1329                            }
1330                            sessionToRemove.removeSelfLocked();
1331                        }
1332                    }
1333                }
1334            }
1335
1336            return null;
1337        }
1338    }
1339}
1340