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