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