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