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