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