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