AutofillManagerService.java revision bb81092a31649b4e1031e24d68958180f5d4024e
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.Manifest.permission.MANAGE_AUTO_FILL;
20import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
21
22import static com.android.server.autofill.Helper.DEBUG;
23import static com.android.server.autofill.Helper.VERBOSE;
24import static com.android.server.autofill.Helper.bundleToString;
25
26import android.annotation.NonNull;
27import android.annotation.Nullable;
28import android.app.ActivityManager;
29import android.app.ActivityManagerInternal;
30import android.content.BroadcastReceiver;
31import android.content.ContentResolver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.pm.PackageManager;
36import android.content.pm.UserInfo;
37import android.database.ContentObserver;
38import android.graphics.Rect;
39import android.net.Uri;
40import android.os.Binder;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.IBinder;
44import android.os.RemoteException;
45import android.os.ResultReceiver;
46import android.os.ShellCallback;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.os.UserManagerInternal;
50import android.provider.Settings;
51import android.service.autofill.FillEventHistory;
52import android.util.LocalLog;
53import android.util.Log;
54import android.util.Slog;
55import android.util.SparseArray;
56import android.util.SparseBooleanArray;
57import android.view.autofill.AutofillId;
58import android.view.autofill.AutofillValue;
59import android.view.autofill.IAutoFillManager;
60import android.view.autofill.IAutoFillManagerClient;
61
62import com.android.internal.annotations.GuardedBy;
63import com.android.internal.os.BackgroundThread;
64import com.android.internal.os.IResultReceiver;
65import com.android.internal.util.DumpUtils;
66import com.android.internal.util.Preconditions;
67import com.android.server.FgThread;
68import com.android.server.LocalServices;
69import com.android.server.SystemService;
70import com.android.server.autofill.ui.AutoFillUI;
71
72import java.io.FileDescriptor;
73import java.io.PrintWriter;
74import java.util.ArrayList;
75import java.util.List;
76import java.util.Objects;
77
78/**
79 * Entry point service for autofill management.
80 *
81 * <p>This service provides the {@link IAutoFillManager} implementation and keeps a list of
82 * {@link AutofillManagerServiceImpl} per user; the real work is done by
83 * {@link AutofillManagerServiceImpl} itself.
84 */
85public final class AutofillManagerService extends SystemService {
86
87    private static final String TAG = "AutofillManagerService";
88
89    static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
90
91    private final Context mContext;
92    private final AutoFillUI mUi;
93
94    private final Object mLock = new Object();
95
96    /**
97     * Cache of {@link AutofillManagerServiceImpl} per user id.
98     * <p>
99     * It has to be mapped by user id because the same current user could have simultaneous sessions
100     * associated to different user profiles (for example, in a multi-window environment or when
101     * device has work profiles).
102     */
103    @GuardedBy("mLock")
104    private SparseArray<AutofillManagerServiceImpl> mServicesCache = new SparseArray<>();
105
106    /**
107     * Users disabled due to {@link UserManager} restrictions.
108     */
109    @GuardedBy("mLock")
110    private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
111
112    private final LocalLog mRequestsHistory = new LocalLog(20);
113
114    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
115        @Override
116        public void onReceive(Context context, Intent intent) {
117            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
118                final String reason = intent.getStringExtra("reason");
119                if (VERBOSE) {
120                    Slog.v(TAG, "close system dialogs: " + reason);
121                }
122                mUi.hideAll();
123            }
124        }
125    };
126
127    public AutofillManagerService(Context context) {
128        super(context);
129        mContext = context;
130        mUi = new AutoFillUI(mContext);
131
132        final IntentFilter filter = new IntentFilter();
133        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
134        mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
135
136        // Hookup with UserManager to disable service when necessary.
137        final UserManager um = context.getSystemService(UserManager.class);
138        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
139        final List<UserInfo> users = um.getUsers();
140        for (int i = 0; i < users.size(); i++) {
141            final int userId = users.get(i).id;
142            final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
143            if (disabled) {
144                mDisabledUsers.put(userId, disabled);
145            }
146        }
147        umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
148            final boolean disabledNow =
149                    newRestrictions.getBoolean(UserManager.DISALLOW_AUTOFILL, false);
150            synchronized (mLock) {
151                final boolean disabledBefore = mDisabledUsers.get(userId);
152                if (disabledBefore == disabledNow) {
153                    // Nothing changed, do nothing.
154                    if (DEBUG) {
155                        Slog.d(TAG, "Restriction not changed for user " + userId + ": "
156                                + bundleToString(newRestrictions));
157                        return;
158                    }
159                }
160                mDisabledUsers.put(userId, disabledNow);
161                updateCachedServiceLocked(userId, disabledNow);
162            }
163        });
164    }
165
166    @Override
167    public void onStart() {
168        publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
169    }
170
171    @Override
172    public void onBootPhase(int phase) {
173        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
174            new SettingsObserver(BackgroundThread.getHandler());
175        }
176    }
177
178    @Override
179    public void onUnlockUser(int userId) {
180        synchronized (mLock) {
181            updateCachedServiceLocked(userId);
182        }
183    }
184
185    @Override
186    public void onCleanupUser(int userId) {
187        synchronized (mLock) {
188            removeCachedServiceLocked(userId);
189        }
190    }
191
192    /**
193     * Gets the service instance for an user.
194     *
195     * @return service instance.
196     */
197    @NonNull
198    AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
199        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
200                Binder.getCallingUid(), userId, false, false, null, null);
201        AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
202        if (service == null) {
203            service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
204                    resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
205            mServicesCache.put(userId, service);
206        }
207        return service;
208    }
209
210    /**
211     * Peeks the service instance for a user.
212     *
213     * @return service instance or {@code null} if not already present
214     */
215    @Nullable
216    AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
217        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
218                Binder.getCallingUid(), userId, false, false, null, null);
219        return mServicesCache.get(resolvedUserId);
220    }
221
222    // Called by Shell command.
223    void destroySessions(int userId, IResultReceiver receiver) {
224        Slog.i(TAG, "destroySessions() for userId " + userId);
225        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
226
227        synchronized (mLock) {
228            if (userId != UserHandle.USER_ALL) {
229                AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
230                if (service != null) {
231                    service.destroySessionsLocked();
232                }
233            } else {
234                final int size = mServicesCache.size();
235                for (int i = 0; i < size; i++) {
236                    mServicesCache.valueAt(i).destroySessionsLocked();
237                }
238            }
239        }
240
241        try {
242            receiver.send(0, new Bundle());
243        } catch (RemoteException e) {
244            // Just ignore it...
245        }
246    }
247
248    // Called by Shell command.
249    void listSessions(int userId, IResultReceiver receiver) {
250        Slog.i(TAG, "listSessions() for userId " + userId);
251        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
252        final Bundle resultData = new Bundle();
253        final ArrayList<String> sessions = new ArrayList<>();
254
255        synchronized (mLock) {
256            if (userId != UserHandle.USER_ALL) {
257                AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
258                if (service != null) {
259                    service.listSessionsLocked(sessions);
260                }
261            } else {
262                final int size = mServicesCache.size();
263                for (int i = 0; i < size; i++) {
264                    mServicesCache.valueAt(i).listSessionsLocked(sessions);
265                }
266            }
267        }
268
269        resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
270        try {
271            receiver.send(0, resultData);
272        } catch (RemoteException e) {
273            // Just ignore it...
274        }
275    }
276
277    // Called by Shell command.
278    void reset() {
279        Slog.i(TAG, "reset()");
280        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
281        synchronized (mLock) {
282            final int size = mServicesCache.size();
283            for (int i = 0; i < size; i++) {
284                mServicesCache.valueAt(i).destroyLocked();
285            }
286            mServicesCache.clear();
287        }
288    }
289
290    /**
291     * Removes a cached service for a given user.
292     */
293    private void removeCachedServiceLocked(int userId) {
294        final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
295        if (service != null) {
296            mServicesCache.delete(userId);
297            service.destroyLocked();
298        }
299    }
300
301    /**
302     * Updates a cached service for a given user.
303     */
304    private void updateCachedServiceLocked(int userId) {
305        updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
306    }
307
308    /**
309     * Updates a cached service for a given user.
310     */
311    private void updateCachedServiceLocked(int userId, boolean disabled) {
312        AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
313        if (service != null) {
314            service.updateLocked(disabled);
315        }
316    }
317
318    private IBinder getTopActivityForUser() {
319        final List<IBinder> topActivities = LocalServices
320                .getService(ActivityManagerInternal.class).getTopVisibleActivities();
321        if (VERBOSE) {
322            Slog.v(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
323        }
324        if (topActivities.isEmpty()) {
325            Slog.w(TAG, "Could not get top activity");
326            return null;
327        }
328        return topActivities.get(0);
329    }
330
331    final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
332        @Override
333        public boolean addClient(IAutoFillManagerClient client, int userId) {
334            synchronized (mLock) {
335                return getServiceForUserLocked(userId).addClientLocked(client);
336            }
337        }
338
339        @Override
340        public void setAuthenticationResult(Bundle data, int sessionId, int userId) {
341            synchronized (mLock) {
342                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
343                service.setAuthenticationResultLocked(data, sessionId, getCallingUid());
344            }
345        }
346
347        @Override
348        public void setHasCallback(int sessionId, int userId, boolean hasIt) {
349            synchronized (mLock) {
350                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
351                service.setHasCallback(sessionId, getCallingUid(), hasIt);
352            }
353        }
354
355        @Override
356        public int startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
357                AutofillId autofillId, Rect bounds, AutofillValue value, int userId,
358                boolean hasCallback, int flags, String packageName) {
359
360            activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
361            appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
362            autofillId = Preconditions.checkNotNull(autofillId, "autoFillId");
363            packageName = Preconditions.checkNotNull(packageName, "packageName");
364
365            Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
366
367            try {
368                mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
369            } catch (PackageManager.NameNotFoundException e) {
370                throw new IllegalArgumentException(packageName + " is not a valid package", e);
371            }
372
373            synchronized (mLock) {
374                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
375                return service.startSessionLocked(activityToken, getCallingUid(), windowToken,
376                        appCallback, autofillId, bounds, value, hasCallback, flags, packageName);
377            }
378        }
379
380        @Override
381        public FillEventHistory getFillEventHistory() throws RemoteException {
382            UserHandle user = getCallingUserHandle();
383            int uid = getCallingUid();
384
385            synchronized (mLock) {
386                AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
387                if (service != null) {
388                    return service.getFillEventHistory(uid);
389                }
390            }
391
392            return null;
393        }
394
395        @Override
396        public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
397                throws RemoteException {
398            activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
399            appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
400
401            synchronized (mLock) {
402                final AutofillManagerServiceImpl service = mServicesCache.get(
403                        UserHandle.getCallingUserId());
404                if (service != null) {
405                    return service.restoreSession(sessionId, getCallingUid(), activityToken,
406                            appCallback);
407                }
408            }
409
410            return false;
411        }
412
413        @Override
414        public void setWindow(int sessionId, IBinder windowToken) throws RemoteException {
415            windowToken = Preconditions.checkNotNull(windowToken, "windowToken");
416
417            synchronized (mLock) {
418                final AutofillManagerServiceImpl service = mServicesCache.get(
419                        UserHandle.getCallingUserId());
420                if (service != null) {
421                    service.setWindow(sessionId, getCallingUid(), windowToken);
422                }
423            }
424        }
425
426        @Override
427        public void updateSession(int sessionId, AutofillId id, Rect bounds,
428                AutofillValue value, int flags, int userId) {
429            synchronized (mLock) {
430                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
431                if (service != null) {
432                    service.updateSessionLocked(sessionId, getCallingUid(), id, bounds, value,
433                            flags);
434                }
435            }
436        }
437
438        @Override
439        public void finishSession(int sessionId, int userId) {
440            synchronized (mLock) {
441                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
442                if (service != null) {
443                    service.finishSessionLocked(sessionId, getCallingUid());
444                }
445            }
446        }
447
448        @Override
449        public void cancelSession(int sessionId, int userId) {
450            synchronized (mLock) {
451                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
452                if (service != null) {
453                    service.cancelSessionLocked(sessionId, getCallingUid());
454                }
455            }
456        }
457
458        @Override
459        public void disableOwnedAutofillServices(int userId) {
460            synchronized (mLock) {
461                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
462                if (service != null) {
463                    service.disableOwnedAutofillServicesLocked(Binder.getCallingUid());
464                }
465            }
466        }
467
468        @Override
469        public boolean isServiceSupported(int userId) {
470            synchronized (mLock) {
471                return !mDisabledUsers.get(userId);
472            }
473        }
474
475        @Override
476        public boolean isServiceEnabled(int userId, String packageName) {
477            synchronized (mLock) {
478                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
479                if (service == null) return false;
480                return Objects.equals(packageName, service.getPackageName());
481            }
482        }
483
484        @Override
485        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
486            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
487            synchronized (mLock) {
488                pw.print("Disabled users: "); pw.println(mDisabledUsers);
489                final int size = mServicesCache.size();
490                pw.print("Cached services: ");
491                if (size == 0) {
492                    pw.println("none");
493                } else {
494                    pw.println(size);
495                    for (int i = 0; i < size; i++) {
496                        pw.print("\nService at index "); pw.println(i);
497                        final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
498                        impl.dumpLocked("  ", pw);
499                    }
500                }
501                mUi.dump(pw);
502            }
503            pw.println("Requests history:");
504            mRequestsHistory.reverseDump(fd, pw, args);
505        }
506
507        @Override
508        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
509                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
510            (new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
511                    this, in, out, err, args, callback, resultReceiver);
512        }
513    }
514
515    private final class SettingsObserver extends ContentObserver {
516        SettingsObserver(Handler handler) {
517            super(handler);
518            ContentResolver resolver = mContext.getContentResolver();
519            resolver.registerContentObserver(Settings.Secure.getUriFor(
520                    Settings.Secure.AUTOFILL_SERVICE), false, this, UserHandle.USER_ALL);
521        }
522
523        @Override
524        public void onChange(boolean selfChange, Uri uri, int userId) {
525            synchronized (mLock) {
526                updateCachedServiceLocked(userId);
527            }
528        }
529    }
530}
531