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