AutofillManagerServiceImpl.java revision a9379d0b44ca1f68a0036d2b65218e17fa348514
13659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu/*
23659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * Copyright (C) 2016 The Android Open Source Project
33659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu *
43659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * Licensed under the Apache License, Version 2.0 (the "License");
53659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * you may not use this file except in compliance with the License.
63659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * You may obtain a copy of the License at
73659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu *
83659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu *      http://www.apache.org/licenses/LICENSE-2.0
93659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu *
103659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * Unless required by applicable law or agreed to in writing, software
113659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * distributed under the License is distributed on an "AS IS" BASIS,
123659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu * See the License for the specific language governing permissions and
14ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * limitations under the License.
153659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu */
16ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas
1726d09ce0e088ef4c69538ead9e7ee79905a047feDake Gupackage com.android.server.autofill;
183659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu
193659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport static android.view.autofill.AutofillManager.ACTION_START_SESSION;
2026d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport static android.view.autofill.AutofillManager.NO_SESSION;
213659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu
223659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport static com.android.server.autofill.Helper.sDebug;
238619e0ef7062b6a714f22af993e4b440fae7ef08Aurimas Liutikasimport static com.android.server.autofill.Helper.sVerbose;
248619e0ef7062b6a714f22af993e4b440fae7ef08Aurimas Liutikas
2526d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.annotation.NonNull;
2626d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.annotation.Nullable;
2726d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.app.ActivityManager;
2826d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.app.AppGlobals;
2926d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.app.IActivityManager;
3026d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.content.ComponentName;
3126d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.content.Context;
3226d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.content.pm.ApplicationInfo;
3326d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.content.pm.PackageManager;
3426d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.content.pm.ServiceInfo;
3526d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.graphics.Rect;
3626d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.os.AsyncTask;
373659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.os.Binder;
38ceb7ab2ddd6e157cd4ade0f14a382c39428163c4Dake Guimport android.os.Bundle;
393659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.os.IBinder;
403659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.os.Looper;
413659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.os.RemoteCallbackList;
42ceb7ab2ddd6e157cd4ade0f14a382c39428163c4Dake Guimport android.os.RemoteException;
433659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.os.UserHandle;
443659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.os.UserManager;
453659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.provider.Settings;
46ceb7ab2ddd6e157cd4ade0f14a382c39428163c4Dake Guimport android.service.autofill.AutofillService;
473659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.service.autofill.AutofillServiceInfo;
483659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.service.autofill.FillEventHistory;
493659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.service.autofill.FillEventHistory.Event;
503659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.service.autofill.FillResponse;
51a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stoutimport android.service.autofill.IAutoFillService;
523659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.text.TextUtils;
533659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.util.ArrayMap;
543659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.util.LocalLog;
553659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.util.Slog;
563659dc62f9e55b1043edb4105c311c8ef997f2aeDake Guimport android.util.SparseArray;
5726d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.view.autofill.AutofillId;
5826d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.view.autofill.AutofillValue;
5926d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport android.view.autofill.IAutoFillManagerClient;
6026d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
6126d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport com.android.internal.R;
6226d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport com.android.internal.annotations.GuardedBy;
6326d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport com.android.internal.os.HandlerCaller;
6426d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport com.android.server.autofill.ui.AutoFillUI;
6526d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
6626d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport java.io.PrintWriter;
6726d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport java.util.ArrayList;
6826d09ce0e088ef4c69538ead9e7ee79905a047feDake Guimport java.util.Random;
6926d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
7026d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu/**
7126d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
7226d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu * app's {@link IAutoFillService} implementation.
7326d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu *
7426d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu */
7526d09ce0e088ef4c69538ead9e7ee79905a047feDake Gufinal class AutofillManagerServiceImpl {
7626d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
7726d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private static final String TAG = "AutofillManagerServiceImpl";
7826d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
7926d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
8026d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    /** Minimum interval to prune abandoned sessions */
8126d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
8226d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
8326d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    static final int MSG_SERVICE_SAVE = 1;
8426d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
8526d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private final int mUserId;
8626d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private final Context mContext;
8726d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private final Object mLock;
8826d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private final AutoFillUI mUi;
8926d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
9026d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private RemoteCallbackList<IAutoFillManagerClient> mClients;
9126d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private AutofillServiceInfo mInfo;
9226d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
9326d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private static final Random sRandom = new Random();
9426d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
9526d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private final LocalLog mRequestsHistory;
9626d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    /**
9726d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu     * Whether service was disabled for user due to {@link UserManager} restrictions.
9826d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu     */
9926d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private boolean mDisabled;
10026d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
10126d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
10226d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu        switch (msg.what) {
10326d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu            case MSG_SERVICE_SAVE:
10426d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu                handleSessionSave(msg.arg1);
10526d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu                break;
10626d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu            default:
10726d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu                Slog.w(TAG, "invalid msg on handler: " + msg);
10826d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu        }
10926d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    };
11026d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu
11126d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu    private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
11226d09ce0e088ef4c69538ead9e7ee79905a047feDake Gu            mHandlerCallback, true);
1133659dc62f9e55b1043edb4105c311c8ef997f2aeDake Gu
114    /**
115     * Cache of pending {@link Session}s, keyed by sessionId.
116     *
117     * <p>They're kept until the {@link AutofillService} finished handling a request, an error
118     * occurs, or the session is abandoned.
119     */
120    @GuardedBy("mLock")
121    private final SparseArray<Session> mSessions = new SparseArray<>();
122
123    /** The last selection */
124    @GuardedBy("mLock")
125    private FillEventHistory mEventHistory;
126
127    /** When was {@link PruneTask} last executed? */
128    private long mLastPrune = 0;
129
130    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
131            int userId, AutoFillUI ui, boolean disabled) {
132        mContext = context;
133        mLock = lock;
134        mRequestsHistory = requestsHistory;
135        mUserId = userId;
136        mUi = ui;
137        updateLocked(disabled);
138    }
139
140    CharSequence getServiceName() {
141        final String packageName = getPackageName();
142        if (packageName == null) {
143            return null;
144        }
145
146        try {
147            final PackageManager pm = mContext.getPackageManager();
148            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
149            return pm.getApplicationLabel(info);
150        } catch (Exception e) {
151            Slog.e(TAG, "Could not get label for " + packageName + ": " + e);
152            return packageName;
153        }
154    }
155
156    String getPackageName() {
157        final ComponentName serviceComponent = getServiceComponentName();
158        if (serviceComponent != null) {
159            return serviceComponent.getPackageName();
160        }
161        return null;
162    }
163
164    ComponentName getServiceComponentName() {
165        synchronized (mLock) {
166            if (mInfo == null) {
167                return null;
168            }
169            return mInfo.getServiceInfo().getComponentName();
170        }
171    }
172
173    private String getComponentNameFromSettings() {
174        return Settings.Secure.getStringForUser(
175                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
176    }
177
178    void updateLocked(boolean disabled) {
179        final boolean wasEnabled = isEnabled();
180        mDisabled = disabled;
181        ComponentName serviceComponent = null;
182        ServiceInfo serviceInfo = null;
183        final String componentName = getComponentNameFromSettings();
184        if (!TextUtils.isEmpty(componentName)) {
185            try {
186                serviceComponent = ComponentName.unflattenFromString(componentName);
187                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
188                        0, mUserId);
189            } catch (RuntimeException | RemoteException e) {
190                Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
191                return;
192            }
193        }
194        try {
195            if (serviceInfo != null) {
196                mInfo = new AutofillServiceInfo(mContext.getPackageManager(),
197                        serviceComponent, mUserId);
198            } else {
199                mInfo = null;
200            }
201            if (wasEnabled != isEnabled()) {
202                if (!isEnabled()) {
203                    final int sessionCount = mSessions.size();
204                    for (int i = sessionCount - 1; i >= 0; i--) {
205                        final Session session = mSessions.valueAt(i);
206                        session.removeSelfLocked();
207                    }
208                }
209                sendStateToClients(false);
210            }
211        } catch (Exception e) {
212            Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e);
213        }
214    }
215
216    /**
217     * Used by {@link AutofillManagerServiceShellCommand} to request save for the current top app.
218     */
219    void requestSaveForUserLocked(IBinder activityToken) {
220        if (!isEnabled()) {
221            return;
222        }
223
224        final int numSessions = mSessions.size();
225        for (int i = 0; i < numSessions; i++) {
226            final Session session = mSessions.valueAt(i);
227            if (session.getActivityTokenLocked().equals(activityToken)) {
228                session.callSaveLocked();
229                return;
230            }
231        }
232
233        Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
234    }
235
236    boolean addClientLocked(IAutoFillManagerClient client) {
237        if (mClients == null) {
238            mClients = new RemoteCallbackList<>();
239        }
240        mClients.register(client);
241        return isEnabled();
242    }
243
244    void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
245        if (!isEnabled()) {
246            return;
247        }
248        final Session session = mSessions.get(sessionId);
249        if (session != null && uid == session.uid) {
250            session.setAuthenticationResultLocked(data, authenticationId);
251        }
252    }
253
254    void setHasCallback(int sessionId, int uid, boolean hasIt) {
255        if (!isEnabled()) {
256            return;
257        }
258        final Session session = mSessions.get(sessionId);
259        if (session != null && uid == session.uid) {
260            synchronized (mLock) {
261                session.setHasCallbackLocked(hasIt);
262            }
263        }
264    }
265
266    int startSessionLocked(@NonNull IBinder activityToken, int uid,
267            @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
268            @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
269            int flags, @NonNull String packageName) {
270        if (!isEnabled()) {
271            return 0;
272        }
273
274        // Occasionally clean up abandoned sessions
275        pruneAbandonedSessionsLocked();
276
277        final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken,
278                hasCallback, flags, packageName);
279        if (newSession == null) {
280            return NO_SESSION;
281        }
282
283        final String historyItem =
284                "id=" + newSession.id + " uid=" + uid + " s=" + mInfo.getServiceInfo().packageName
285                        + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds + " hc=" +
286                        hasCallback + " f=" + flags;
287        mRequestsHistory.log(historyItem);
288
289        newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
290
291        return newSession.id;
292    }
293
294    /**
295     * Remove abandoned sessions if needed.
296     */
297    private void pruneAbandonedSessionsLocked() {
298        long now = System.currentTimeMillis();
299        if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
300            mLastPrune = now;
301
302            if (mSessions.size() > 0) {
303                (new PruneTask()).execute();
304            }
305        }
306    }
307
308    void finishSessionLocked(int sessionId, int uid) {
309        if (!isEnabled()) {
310            return;
311        }
312
313        final Session session = mSessions.get(sessionId);
314        if (session == null || uid != session.uid) {
315            if (sVerbose) {
316                Slog.v(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")");
317            }
318            return;
319        }
320
321        final boolean finished = session.showSaveLocked();
322        if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
323
324        if (finished) {
325            session.removeSelfLocked();
326        }
327    }
328
329    void cancelSessionLocked(int sessionId, int uid) {
330        if (!isEnabled()) {
331            return;
332        }
333
334        final Session session = mSessions.get(sessionId);
335        if (session == null || uid != session.uid) {
336            Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")");
337            return;
338        }
339        session.removeSelfLocked();
340    }
341
342    void disableOwnedAutofillServicesLocked(int uid) {
343        if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid
344                != UserHandle.getAppId(uid)) {
345            return;
346        }
347        final long identity = Binder.clearCallingIdentity();
348        try {
349            final String autoFillService = getComponentNameFromSettings();
350            if (mInfo.getServiceInfo().getComponentName().equals(
351                    ComponentName.unflattenFromString(autoFillService))) {
352                Settings.Secure.putStringForUser(mContext.getContentResolver(),
353                        Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
354                destroySessionsLocked();
355            }
356        } finally {
357            Binder.restoreCallingIdentity(identity);
358        }
359    }
360
361    private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
362            @NonNull IBinder appCallbackToken, boolean hasCallback, int flags,
363            @NonNull String packageName) {
364        // use random ids so that one app cannot know that another app creates sessions
365        int sessionId;
366        int tries = 0;
367        do {
368            tries++;
369            if (tries > MAX_SESSION_ID_CREATE_TRIES) {
370                Slog.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries");
371                return null;
372            }
373
374            sessionId = sRandom.nextInt();
375        } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
376
377        final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
378                sessionId, uid, activityToken, appCallbackToken, hasCallback,
379                mInfo.getServiceInfo().getComponentName(), packageName);
380        mSessions.put(newSession.id, newSession);
381
382        return newSession;
383    }
384
385    /**
386     * Restores a session after an activity was temporarily destroyed.
387     *
388     * @param sessionId The id of the session to restore
389     * @param uid UID of the process that tries to restore the session
390     * @param activityToken The new instance of the activity
391     * @param appCallback The callbacks to the activity
392     */
393    boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken,
394            @NonNull IBinder appCallback) {
395        final Session session = mSessions.get(sessionId);
396
397        if (session == null || uid != session.uid) {
398            return false;
399        } else {
400            session.switchActivity(activityToken, appCallback);
401            return true;
402        }
403    }
404
405    void updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
406            AutofillValue value, int action, int flags) {
407        final Session session = mSessions.get(sessionId);
408        if (session == null || session.uid != uid) {
409            if (sVerbose) {
410                Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId + "(" + uid
411                        + ")");
412            }
413            return;
414        }
415
416        session.updateLocked(autofillId, virtualBounds, value, action, flags);
417    }
418
419    void removeSessionLocked(int sessionId) {
420        mSessions.remove(sessionId);
421    }
422
423    private void handleSessionSave(int sessionId) {
424        synchronized (mLock) {
425            final Session session = mSessions.get(sessionId);
426            if (session == null) {
427                Slog.w(TAG, "handleSessionSave(): already gone: " + sessionId);
428
429                return;
430            }
431            session.callSaveLocked();
432        }
433    }
434
435    void destroyLocked() {
436        if (sVerbose) Slog.v(TAG, "destroyLocked()");
437
438        final int numSessions = mSessions.size();
439        for (int i = 0; i < numSessions; i++) {
440            mSessions.valueAt(i).destroyLocked();
441        }
442        mSessions.clear();
443
444        sendStateToClients(true);
445    }
446
447    CharSequence getServiceLabel() {
448        return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager());
449    }
450
451    /**
452     * Initializes the last fill selection after an autofill service returned a new
453     * {@link FillResponse}.
454     */
455    void setLastResponse(int serviceUid, @NonNull FillResponse response) {
456        synchronized (mLock) {
457            mEventHistory = new FillEventHistory(serviceUid, response.getClientState());
458        }
459    }
460
461    /**
462     * Updates the last fill selection when an authentication was selected.
463     */
464    void setAuthenticationSelected() {
465        synchronized (mLock) {
466            mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null));
467        }
468    }
469
470    /**
471     * Updates the last fill selection when an dataset authentication was selected.
472     */
473    void setDatasetAuthenticationSelected(@Nullable String selectedDataset) {
474        synchronized (mLock) {
475            mEventHistory.addEvent(
476                    new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
477        }
478    }
479
480    /**
481     * Updates the last fill selection when an save Ui is shown.
482     */
483    void setSaveShown() {
484        synchronized (mLock) {
485            mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null));
486        }
487    }
488
489    /**
490     * Updates the last fill response when a dataset was selected.
491     */
492    void setDatasetSelected(@Nullable String selectedDataset) {
493        synchronized (mLock) {
494            mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset));
495        }
496    }
497
498    /**
499     * Gets the fill event history.
500     *
501     * @param callingUid The calling uid
502     *
503     * @return The history or {@code null} if there is none.
504     */
505    FillEventHistory getFillEventHistory(int callingUid) {
506        synchronized (mLock) {
507            if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) {
508                return mEventHistory;
509            }
510        }
511
512        return null;
513    }
514
515    void dumpLocked(String prefix, PrintWriter pw) {
516        final String prefix2 = prefix + "  ";
517
518        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
519        pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null
520                ? mInfo.getServiceInfo().getComponentName() : null);
521        pw.print(prefix); pw.print("Component from settings: ");
522            pw.println(getComponentNameFromSettings());
523        pw.print(prefix); pw.print("Default component: ");
524            pw.println(mContext.getString(R.string.config_defaultAutofillService));
525        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
526        pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
527
528        final int size = mSessions.size();
529        if (size == 0) {
530            pw.print(prefix); pw.println("No sessions");
531        } else {
532            pw.print(prefix); pw.print(size); pw.println(" sessions:");
533            for (int i = 0; i < size; i++) {
534                pw.print(prefix); pw.print("#"); pw.println(i + 1);
535                mSessions.valueAt(i).dumpLocked(prefix2, pw);
536            }
537        }
538
539        if (mEventHistory == null || mEventHistory.getEvents() == null
540                || mEventHistory.getEvents().size() == 0) {
541            pw.print(prefix); pw.println("No event on last fill response");
542        } else {
543            pw.print(prefix); pw.println("Events of last fill response:");
544            pw.print(prefix);
545
546            int numEvents = mEventHistory.getEvents().size();
547            for (int i = 0; i < numEvents; i++) {
548                final Event event = mEventHistory.getEvents().get(i);
549                pw.println("  " + i + ": eventType=" + event.getType() + " datasetId="
550                        + event.getDatasetId());
551            }
552        }
553    }
554
555    void destroySessionsLocked() {
556        while (mSessions.size() > 0) {
557            mSessions.valueAt(0).removeSelfLocked();
558        }
559    }
560
561    void listSessionsLocked(ArrayList<String> output) {
562        final int numSessions = mSessions.size();
563        for (int i = 0; i < numSessions; i++) {
564            output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
565                    : null) + ":" + mSessions.keyAt(i));
566        }
567    }
568
569    private void sendStateToClients(boolean resetClient) {
570        final RemoteCallbackList<IAutoFillManagerClient> clients;
571        final int userClientCount;
572        synchronized (mLock) {
573            if (mClients == null) {
574                return;
575            }
576            clients = mClients;
577            userClientCount = clients.beginBroadcast();
578        }
579        try {
580            for (int i = 0; i < userClientCount; i++) {
581                final IAutoFillManagerClient client = clients.getBroadcastItem(i);
582                try {
583                    final boolean resetSession;
584                    synchronized (mLock) {
585                        resetSession = resetClient || isClientSessionDestroyedLocked(client);
586                    }
587                    client.setState(isEnabled(), resetSession, resetClient);
588                } catch (RemoteException re) {
589                    /* ignore */
590                }
591            }
592        } finally {
593            clients.finishBroadcast();
594        }
595    }
596
597    private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
598        final int sessionCount = mSessions.size();
599        for (int i = 0; i < sessionCount; i++) {
600            final Session session = mSessions.valueAt(i);
601            if (session.getClient().equals(client)) {
602                return session.isDestroyed();
603            }
604        }
605        return true;
606    }
607
608    boolean isEnabled() {
609        return mInfo != null && !mDisabled;
610    }
611
612    @Override
613    public String toString() {
614        return "AutofillManagerServiceImpl: [userId=" + mUserId
615                + ", component=" + (mInfo != null
616                ? mInfo.getServiceInfo().getComponentName() : null) + "]";
617    }
618
619    /** Task used to prune abandoned session */
620    private class PruneTask extends AsyncTask<Void, Void, Void> {
621        @Override
622        protected Void doInBackground(Void... ignored) {
623            int numSessionsToRemove;
624            ArrayMap<IBinder, Integer> sessionsToRemove;
625
626            synchronized (mLock) {
627                numSessionsToRemove = mSessions.size();
628                sessionsToRemove = new ArrayMap<>(numSessionsToRemove);
629
630                for (int i = 0; i < numSessionsToRemove; i++) {
631                    Session session = mSessions.valueAt(i);
632
633                    sessionsToRemove.put(session.getActivityTokenLocked(), session.id);
634                }
635            }
636
637            IActivityManager am = ActivityManager.getService();
638
639            // Only remove sessions which's activities are not known to the activity manager anymore
640            for (int i = 0; i < numSessionsToRemove; i++) {
641                try {
642                    // The activity manager cannot resolve activities that have been removed
643                    if (am.getActivityClassForToken(sessionsToRemove.keyAt(i)) != null) {
644                        sessionsToRemove.removeAt(i);
645                        i--;
646                        numSessionsToRemove--;
647                    }
648                } catch (RemoteException e) {
649                    Slog.w(TAG, "Cannot figure out if activity is finished", e);
650                }
651            }
652
653            synchronized (mLock) {
654                for (int i = 0; i < numSessionsToRemove; i++) {
655                    Session sessionToRemove = mSessions.get(sessionsToRemove.valueAt(i));
656
657                    if (sessionToRemove != null) {
658                        if (sessionToRemove.isSavingLocked()) {
659                            if (sVerbose) {
660                                Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
661                            }
662                        } else {
663                            if (sDebug) {
664                                Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
665                                    + sessionToRemove.getActivityTokenLocked() + ")");
666                            }
667                            sessionToRemove.removeSelfLocked();
668                        }
669                    }
670                }
671            }
672
673            return null;
674        }
675    }
676}
677