AutofillManagerServiceImpl.java revision 0aa4c5065d1495ec0b2c6fe15324569f31dcbdb1
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.view.autofill.AutofillManager.ACTION_START_SESSION;
20import static android.view.autofill.AutofillManager.NO_SESSION;
21
22import static com.android.server.autofill.Helper.DEBUG;
23import static com.android.server.autofill.Helper.VERBOSE;
24
25import android.annotation.NonNull;
26import android.annotation.Nullable;
27import android.app.AppGlobals;
28import android.content.ComponentName;
29import android.content.Context;
30import android.content.pm.ApplicationInfo;
31import android.content.pm.PackageManager;
32import android.content.pm.ServiceInfo;
33import android.graphics.Rect;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.IBinder;
37import android.os.Looper;
38import android.os.RemoteCallbackList;
39import android.os.RemoteException;
40import android.os.UserHandle;
41import android.os.UserManager;
42import android.provider.Settings;
43import android.service.autofill.AutofillService;
44import android.service.autofill.AutofillServiceInfo;
45import android.service.autofill.FillEventHistory;
46import android.service.autofill.FillEventHistory.Event;
47import android.service.autofill.FillResponse;
48import android.service.autofill.IAutoFillService;
49import android.text.TextUtils;
50import android.util.LocalLog;
51import android.util.Log;
52import android.util.PrintWriterPrinter;
53import android.util.Slog;
54import android.util.SparseArray;
55import android.view.autofill.AutofillId;
56import android.view.autofill.AutofillManager;
57import android.view.autofill.AutofillValue;
58import android.view.autofill.IAutoFillManagerClient;
59
60import com.android.internal.R;
61import com.android.internal.annotations.GuardedBy;
62import com.android.internal.os.HandlerCaller;
63import com.android.server.autofill.ui.AutoFillUI;
64
65import java.io.PrintWriter;
66import java.util.ArrayList;
67import java.util.Random;
68
69/**
70 * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
71 * app's {@link IAutoFillService} implementation.
72 *
73 */
74final class AutofillManagerServiceImpl {
75
76    private static final String TAG = "AutofillManagerServiceImpl";
77    private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
78
79    static final int MSG_SERVICE_SAVE = 1;
80
81    private final int mUserId;
82    private final Context mContext;
83    private final Object mLock;
84    private final AutoFillUI mUi;
85
86    private RemoteCallbackList<IAutoFillManagerClient> mClients;
87    private AutofillServiceInfo mInfo;
88
89    private static final Random sRandom = new Random();
90
91    private final LocalLog mRequestsHistory;
92    /**
93     * Whether service was disabled for user due to {@link UserManager} restrictions.
94     */
95    private boolean mDisabled;
96
97    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
98        switch (msg.what) {
99            case MSG_SERVICE_SAVE:
100                handleSessionSave(msg.arg1);
101                break;
102            default:
103                Slog.w(TAG, "invalid msg on handler: " + msg);
104        }
105    };
106
107    private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
108            mHandlerCallback, true);
109
110    /**
111     * Cache of pending {@link Session}s, keyed by {@code activityToken}.
112     *
113     * <p>They're kept until the {@link AutofillService} finished handling a request, an error
114     * occurs, or the session times out.
115     */
116    @GuardedBy("mLock")
117    private final SparseArray<Session> mSessions = new SparseArray<>();
118
119    /** The last selection */
120    @GuardedBy("mLock")
121    private FillEventHistory mEventHistory;
122
123    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
124            int userId, AutoFillUI ui, boolean disabled) {
125        mContext = context;
126        mLock = lock;
127        mRequestsHistory = requestsHistory;
128        mUserId = userId;
129        mUi = ui;
130        updateLocked(disabled);
131    }
132
133    CharSequence getServiceName() {
134        final String packageName = getPackageName();
135        if (packageName == null) {
136            return null;
137        }
138
139        try {
140            final PackageManager pm = mContext.getPackageManager();
141            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
142            return pm.getApplicationLabel(info);
143        } catch (Exception e) {
144            Slog.e(TAG, "Could not get label for " + packageName + ": " + e);
145            return packageName;
146        }
147    }
148
149    String getPackageName() {
150        final ComponentName serviceComponent = getServiceComponentName();
151        if (serviceComponent != null) {
152            return serviceComponent.getPackageName();
153        }
154        return null;
155    }
156
157    ComponentName getServiceComponentName() {
158        synchronized (mLock) {
159            if (mInfo == null) {
160                return null;
161            }
162            return mInfo.getServiceInfo().getComponentName();
163        }
164    }
165
166    private String getComponentNameFromSettings() {
167        return Settings.Secure.getStringForUser(
168                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
169    }
170
171    void updateLocked(boolean disabled) {
172        final boolean wasEnabled = isEnabled();
173        mDisabled = disabled;
174        ComponentName serviceComponent = null;
175        ServiceInfo serviceInfo = null;
176        final String componentName = getComponentNameFromSettings();
177        if (!TextUtils.isEmpty(componentName)) {
178            try {
179                serviceComponent = ComponentName.unflattenFromString(componentName);
180                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
181                        0, mUserId);
182            } catch (RuntimeException | RemoteException e) {
183                Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
184                return;
185            }
186        }
187        try {
188            if (serviceInfo != null) {
189                mInfo = new AutofillServiceInfo(mContext.getPackageManager(),
190                        serviceComponent, mUserId);
191            } else {
192                mInfo = null;
193            }
194            if (wasEnabled != isEnabled()) {
195                if (!isEnabled()) {
196                    final int sessionCount = mSessions.size();
197                    for (int i = sessionCount - 1; i >= 0; i--) {
198                        final Session session = mSessions.valueAt(i);
199                        session.removeSelfLocked();
200                    }
201                }
202                sendStateToClients(false);
203            }
204        } catch (Exception e) {
205            Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e);
206        }
207    }
208
209    /**
210     * Used by {@link AutofillManagerServiceShellCommand} to request save for the current top app.
211     */
212    void requestSaveForUserLocked(IBinder activityToken) {
213        if (!isEnabled()) {
214            return;
215        }
216
217        final int numSessions = mSessions.size();
218        for (int i = 0; i < numSessions; i++) {
219            final Session session = mSessions.valueAt(i);
220            if (session.getActivityTokenLocked().equals(activityToken)) {
221                session.callSaveLocked();
222                return;
223            }
224        }
225
226        Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
227    }
228
229    boolean addClientLocked(IAutoFillManagerClient client) {
230        if (mClients == null) {
231            mClients = new RemoteCallbackList<>();
232        }
233        mClients.register(client);
234        return isEnabled();
235    }
236
237    void setAuthenticationResultLocked(Bundle data, int sessionId, int uid) {
238        if (!isEnabled()) {
239            return;
240        }
241        final Session session = mSessions.get(sessionId);
242        if (session != null && uid == session.uid) {
243            session.setAuthenticationResultLocked(data);
244        }
245    }
246
247    void setHasCallback(int sessionId, int uid, boolean hasIt) {
248        if (!isEnabled()) {
249            return;
250        }
251        final Session session = mSessions.get(sessionId);
252        if (session != null && uid == session.uid) {
253            synchronized (mLock) {
254                session.setHasCallbackLocked(hasIt);
255            }
256        }
257    }
258
259    int startSessionLocked(@NonNull IBinder activityToken, int uid, @Nullable IBinder windowToken,
260            @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
261            @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
262            int flags, @NonNull String packageName) {
263        if (!isEnabled()) {
264            return 0;
265        }
266
267        final Session newSession = createSessionByTokenLocked(activityToken, uid, windowToken,
268                appCallbackToken, hasCallback, flags, packageName);
269        if (newSession == null) {
270            return NO_SESSION;
271        }
272
273        final String historyItem =
274                "id=" + newSession.id + " uid=" + uid + " s=" + mInfo.getServiceInfo().packageName
275                        + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds + " hc=" +
276                        hasCallback + " f=" + flags;
277        mRequestsHistory.log(historyItem);
278
279        newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
280
281        return newSession.id;
282    }
283
284    void finishSessionLocked(int sessionId, int uid) {
285        if (!isEnabled()) {
286            return;
287        }
288
289        final Session session = mSessions.get(sessionId);
290        if (session == null || uid != session.uid) {
291            Slog.w(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")");
292            return;
293        }
294
295        final boolean finished = session.showSaveLocked();
296        if (DEBUG) {
297            Log.d(TAG, "finishSessionLocked(): session finished on save? " + finished);
298        }
299        if (finished) {
300            session.removeSelfLocked();
301        }
302    }
303
304    void cancelSessionLocked(int sessionId, int uid) {
305        if (!isEnabled()) {
306            return;
307        }
308
309        final Session session = mSessions.get(sessionId);
310        if (session == null || uid != session.uid) {
311            Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")");
312            return;
313        }
314        session.removeSelfLocked();
315    }
316
317    void disableOwnedAutofillServicesLocked(int uid) {
318        if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid
319                != UserHandle.getAppId(uid)) {
320            return;
321        }
322        final long identity = Binder.clearCallingIdentity();
323        try {
324            final String autoFillService = getComponentNameFromSettings();
325            if (mInfo.getServiceInfo().getComponentName().equals(
326                    ComponentName.unflattenFromString(autoFillService))) {
327                Settings.Secure.putStringForUser(mContext.getContentResolver(),
328                        Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
329                destroySessionsLocked();
330            }
331        } finally {
332            Binder.restoreCallingIdentity(identity);
333        }
334    }
335
336    private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
337            @Nullable IBinder windowToken, @NonNull IBinder appCallbackToken, boolean hasCallback,
338            int flags, @NonNull String packageName) {
339        // use random ids so that one app cannot know that another app creates sessions
340        int sessionId;
341        int tries = 0;
342        do {
343            tries++;
344            if (tries > MAX_SESSION_ID_CREATE_TRIES) {
345                Log.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries");
346                return null;
347            }
348
349            sessionId = sRandom.nextInt();
350        } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
351
352        final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
353                sessionId, uid, activityToken, windowToken, appCallbackToken, hasCallback,
354                mInfo.getServiceInfo().getComponentName(), packageName);
355        mSessions.put(newSession.id, newSession);
356
357        return newSession;
358    }
359
360    /**
361     * Restores a session after an activity was temporarily destroyed.
362     *
363     * @param sessionId The id of the session to restore
364     * @param uid UID of the process that tries to restore the session
365     * @param activityToken The new instance of the activity
366     * @param appCallback The callbacks to the activity
367     */
368    boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken,
369            @NonNull IBinder appCallback) {
370        final Session session = mSessions.get(sessionId);
371
372        if (session == null || uid != session.uid) {
373            return false;
374        } else {
375            session.switchActivity(activityToken, appCallback);
376            return true;
377        }
378    }
379
380    /**
381     * Set the window the UI should get attached to
382     *
383     * @param sessionId The id of the session to restore
384     * @param uid UID of the process that tries to restore the session
385     * @param windowToken The window the activity is now in
386     */
387    boolean setWindow(int sessionId, int uid, @NonNull IBinder windowToken) {
388        final Session session = mSessions.get(sessionId);
389
390        if (session == null || uid != session.uid) {
391            return false;
392        } else {
393            session.switchWindow(windowToken);
394            return true;
395        }
396    }
397
398    void updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
399            AutofillValue value, int action, int flags) {
400        final Session session = mSessions.get(sessionId);
401        if (session == null || session.uid != uid) {
402            if (VERBOSE) {
403                Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId + "(" + uid
404                        + ")");
405            }
406            return;
407        }
408
409        session.updateLocked(autofillId, virtualBounds, value, action, flags);
410    }
411
412    void removeSessionLocked(int sessionId) {
413        mSessions.remove(sessionId);
414    }
415
416    private void handleSessionSave(int sessionId) {
417        synchronized (mLock) {
418            final Session session = mSessions.get(sessionId);
419            if (session == null) {
420                Slog.w(TAG, "handleSessionSave(): already gone: " + sessionId);
421
422                return;
423            }
424            session.callSaveLocked();
425        }
426    }
427
428    void destroyLocked() {
429        if (VERBOSE) {
430            Slog.v(TAG, "destroyLocked()");
431        }
432
433        final int numSessions = mSessions.size();
434        for (int i = 0; i < numSessions; i++) {
435            mSessions.valueAt(i).destroyLocked();
436        }
437        mSessions.clear();
438
439        sendStateToClients(true);
440    }
441
442    CharSequence getServiceLabel() {
443        return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager());
444    }
445
446    /**
447     * Initializes the last fill selection after an autofill service returned a new
448     * {@link FillResponse}.
449     */
450    void setLastResponse(int serviceUid, @NonNull FillResponse response) {
451        synchronized (mLock) {
452            mEventHistory = new FillEventHistory(serviceUid, response.getClientState());
453        }
454    }
455
456    /**
457     * Updates the last fill selection when an authentication was selected.
458     */
459    void setAuthenticationSelected() {
460        synchronized (mLock) {
461            mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null));
462        }
463    }
464
465    /**
466     * Updates the last fill selection when an dataset authentication was selected.
467     */
468    void setDatasetAuthenticationSelected(@Nullable String selectedDataset) {
469        synchronized (mLock) {
470            mEventHistory.addEvent(
471                    new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
472        }
473    }
474
475    /**
476     * Updates the last fill selection when an save Ui is shown.
477     */
478    void setSaveShown() {
479        synchronized (mLock) {
480            mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null));
481        }
482    }
483
484    /**
485     * Updates the last fill response when a dataset was selected.
486     */
487    void setDatasetSelected(@Nullable String selectedDataset) {
488        synchronized (mLock) {
489            mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset));
490        }
491    }
492
493    /**
494     * Gets the fill event history.
495     *
496     * @param callingUid The calling uid
497     *
498     * @return The history or {@code null} if there is none.
499     */
500    FillEventHistory getFillEventHistory(int callingUid) {
501        synchronized (mLock) {
502            if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) {
503                return mEventHistory;
504            }
505        }
506
507        return null;
508    }
509
510    void dumpLocked(String prefix, PrintWriter pw) {
511        final String prefix2 = prefix + "  ";
512
513        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
514        pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null
515                ? mInfo.getServiceInfo().getComponentName() : null);
516        pw.print(prefix); pw.print("Component from settings: ");
517            pw.println(getComponentNameFromSettings());
518        pw.print(prefix); pw.print("Default component: ");
519            pw.println(mContext.getString(R.string.config_defaultAutofillService));
520        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
521
522        if (VERBOSE && mInfo != null) {
523            // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
524            pw.print(prefix); pw.println("ServiceInfo:");
525            mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix);
526        }
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