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