AutofillManagerService.java revision fe9a53bc45fd0124a876dc0a49680aaf86641d3e
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.Manifest;
27import android.annotation.NonNull;
28import android.app.ActivityManagerInternal;
29import android.content.BroadcastReceiver;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.pm.PackageManager;
35import android.content.pm.UserInfo;
36import android.database.ContentObserver;
37import android.graphics.Rect;
38import android.net.Uri;
39import android.os.Binder;
40import android.os.Bundle;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.RemoteException;
44import android.os.ResultReceiver;
45import android.os.ShellCallback;
46import android.os.UserHandle;
47import android.os.UserManager;
48import android.os.UserManagerInternal;
49import android.provider.Settings;
50import android.util.LocalLog;
51import android.util.Log;
52import android.util.Slog;
53import android.util.SparseArray;
54import android.util.SparseBooleanArray;
55import android.view.autofill.AutofillId;
56import android.view.autofill.AutofillValue;
57import android.view.autofill.IAutoFillManager;
58import android.view.autofill.IAutoFillManagerClient;
59
60import com.android.internal.annotations.GuardedBy;
61import com.android.internal.os.BackgroundThread;
62import com.android.internal.os.IResultReceiver;
63import com.android.internal.util.DumpUtils;
64import com.android.internal.util.Preconditions;
65import com.android.server.FgThread;
66import com.android.server.LocalServices;
67import com.android.server.SystemService;
68import com.android.server.autofill.ui.AutoFillUI;
69
70import java.io.FileDescriptor;
71import java.io.PrintWriter;
72import java.util.ArrayList;
73import java.util.List;
74
75/**
76 * Entry point service for autofill management.
77 *
78 * <p>This service provides the {@link IAutoFillManager} implementation and keeps a list of
79 * {@link AutofillManagerServiceImpl} per user; the real work is done by
80 * {@link AutofillManagerServiceImpl} itself.
81 */
82// TODO(b/33197203): Handle removing of packages
83public final class AutofillManagerService extends SystemService {
84
85    private static final String TAG = "AutofillManagerService";
86
87    static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
88
89    private final Context mContext;
90    private final AutoFillUI mUi;
91
92    private final Object mLock = new Object();
93
94    /**
95     * Cache of {@link AutofillManagerServiceImpl} per user id.
96     * <p>
97     * It has to be mapped by user id because the same current user could have simultaneous sessions
98     * associated to different user profiles (for example, in a multi-window environment or when
99     * device has work profiles).
100     */
101    @GuardedBy("mLock")
102    private SparseArray<AutofillManagerServiceImpl> mServicesCache = new SparseArray<>();
103
104    /**
105     * Users disabled due to {@link UserManager} restrictions.
106     */
107    @GuardedBy("mLock")
108    private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
109
110    // TODO(b/33197203): set a different max (or disable it) on low-memory devices.
111    private final LocalLog mRequestsHistory = new LocalLog(20);
112
113    // TODO(b/33197203): is this still needed?
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 onStopUser(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        AutofillManagerServiceImpl service = mServicesCache.get(userId);
200        if (service == null) {
201            service = new AutofillManagerServiceImpl(mContext, mLock,
202                    mRequestsHistory, userId, mUi, mDisabledUsers.get(userId));
203            mServicesCache.put(userId, service);
204        }
205        return service;
206    }
207
208    // Called by Shell command.
209    void requestSaveForUser(int userId) {
210        Slog.i(TAG, "requestSaveForUser(): " + userId);
211        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
212        final IBinder activityToken = getTopActivityForUser();
213        if (activityToken != null) {
214            synchronized (mLock) {
215                final AutofillManagerServiceImpl service = mServicesCache.get(userId);
216                if (service == null) {
217                    Log.w(TAG, "handleSaveForUser(): no cached service for userId " + userId);
218                    return;
219                }
220
221                service.requestSaveForUserLocked(activityToken);
222            }
223        }
224    }
225
226    // Called by Shell command.
227    void destroySessions(int userId, IResultReceiver receiver) {
228        Slog.i(TAG, "destroySessions() for userId " + userId);
229        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
230
231        synchronized (mLock) {
232            if (userId != UserHandle.USER_ALL) {
233                mServicesCache.get(userId).destroySessionsLocked();
234            } else {
235                final int size = mServicesCache.size();
236                for (int i = 0; i < size; i++) {
237                    mServicesCache.valueAt(i).destroySessionsLocked();
238                }
239            }
240        }
241
242        try {
243            receiver.send(0, new Bundle());
244        } catch (RemoteException e) {
245            // Just ignore it...
246        }
247    }
248
249    // Called by Shell command.
250    void listSessions(int userId, IResultReceiver receiver) {
251        Slog.i(TAG, "listSessions() for userId " + userId);
252        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
253        final Bundle resultData = new Bundle();
254        final ArrayList<String> sessions = new ArrayList<>();
255
256        synchronized (mLock) {
257            if (userId != UserHandle.USER_ALL) {
258                mServicesCache.get(userId).listSessionsLocked(sessions);
259            } else {
260                final int size = mServicesCache.size();
261                for (int i = 0; i < size; i++) {
262                    mServicesCache.valueAt(i).listSessionsLocked(sessions);
263                }
264            }
265        }
266
267        resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
268        try {
269            receiver.send(0, resultData);
270        } catch (RemoteException e) {
271            // Just ignore it...
272        }
273    }
274
275    // Called by Shell command.
276    void reset() {
277        Slog.i(TAG, "reset()");
278        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
279        synchronized (mLock) {
280            final int size = mServicesCache.size();
281            for (int i = 0; i < size; i++) {
282                mServicesCache.valueAt(i).destroyLocked();
283            }
284            mServicesCache.clear();
285        }
286    }
287
288    // Called by Shell command.
289    public void setSaveTimeout(int timeout) {
290        Slog.i(TAG, "setSaveTimeout("  + timeout + ")");
291        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
292        mUi.setSaveTimeout(timeout);
293    }
294
295    /**
296     * Removes a cached service for a given user.
297     */
298    private void removeCachedServiceLocked(int userId) {
299        final AutofillManagerServiceImpl service = mServicesCache.get(userId);
300        if (service != null) {
301            mServicesCache.delete(userId);
302            service.destroyLocked();
303        }
304    }
305
306    /**
307     * Updates a cached service for a given user.
308     */
309    private void updateCachedServiceLocked(int userId) {
310        updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
311    }
312
313    /**
314     * Updates a cached service for a given user.
315     */
316    private void updateCachedServiceLocked(int userId, boolean disabled) {
317        AutofillManagerServiceImpl service = mServicesCache.get(userId);
318        if (service != null) {
319            service.updateLocked(disabled);
320        }
321    }
322
323    private IBinder getTopActivityForUser() {
324        final List<IBinder> topActivities = LocalServices
325                .getService(ActivityManagerInternal.class).getTopVisibleActivities();
326        if (VERBOSE) {
327            Slog.v(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
328        }
329        if (topActivities.isEmpty()) {
330            Slog.w(TAG, "Could not get top activity");
331            return null;
332        }
333        return topActivities.get(0);
334    }
335
336    final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
337        @Override
338        public boolean addClient(IAutoFillManagerClient client, int userId) {
339            synchronized (mLock) {
340                return getServiceForUserLocked(userId).addClientLocked(client);
341            }
342        }
343
344        @Override
345        public void setAuthenticationResult(Bundle data, IBinder activityToken, int userId) {
346            synchronized (mLock) {
347                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
348                service.setAuthenticationResultLocked(data, activityToken);
349            }
350        }
351
352        @Override
353        public void setHasCallback(IBinder activityToken, int userId, boolean hasIt) {
354            synchronized (mLock) {
355                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
356                service.setHasCallback(activityToken, hasIt);
357            }
358        }
359
360        @Override
361        public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
362                AutofillId autofillId, Rect bounds, AutofillValue value, int userId,
363                boolean hasCallback, int flags, String packageName) {
364            // TODO(b/33197203): make sure it's called by resumed / focused activity
365
366            activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
367            appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
368            autofillId = Preconditions.checkNotNull(autofillId, "autoFillId");
369            packageName = Preconditions.checkNotNull(packageName, "packageName");
370
371            Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
372
373            try {
374                mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
375            } catch (PackageManager.NameNotFoundException e) {
376                throw new IllegalArgumentException(packageName + " is not a valid package", e);
377            }
378
379            synchronized (mLock) {
380                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
381                service.startSessionLocked(activityToken, windowToken, appCallback,
382                        autofillId, bounds, value, hasCallback, flags, packageName);
383            }
384        }
385
386        @Override
387        public void updateSession(IBinder activityToken, AutofillId id, Rect bounds,
388                AutofillValue value, int flags, int userId) {
389            synchronized (mLock) {
390                final AutofillManagerServiceImpl service = mServicesCache.get(
391                        UserHandle.getCallingUserId());
392                if (service != null) {
393                    service.updateSessionLocked(activityToken, id, bounds, value, flags);
394                }
395            }
396        }
397
398        @Override
399        public void finishSession(IBinder activityToken, int userId) {
400            synchronized (mLock) {
401                final AutofillManagerServiceImpl service = mServicesCache.get(
402                        UserHandle.getCallingUserId());
403                if (service != null) {
404                    service.finishSessionLocked(activityToken);
405                }
406            }
407        }
408
409        @Override
410        public void cancelSession(IBinder activityToken, int userId) {
411            synchronized (mLock) {
412                final AutofillManagerServiceImpl service = mServicesCache.get(
413                        UserHandle.getCallingUserId());
414                if (service != null) {
415                    service.cancelSessionLocked(activityToken);
416                }
417            }
418        }
419
420        @Override
421        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
422            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
423            synchronized (mLock) {
424                pw.print("Disabled users: "); pw.println(mDisabledUsers);
425                final int size = mServicesCache.size();
426                pw.print("Cached services: ");
427                if (size == 0) {
428                    pw.println("none");
429                } else {
430                    pw.println(size);
431                    for (int i = 0; i < size; i++) {
432                        pw.print("\nService at index "); pw.println(i);
433                        final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
434                        impl.dumpLocked("  ", pw);
435                    }
436                }
437                mUi.dump(pw);
438            }
439            pw.println("Requests history:");
440            mRequestsHistory.reverseDump(fd, pw, args);
441        }
442
443        @Override
444        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
445                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
446            (new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
447                    this, in, out, err, args, callback, resultReceiver);
448        }
449    }
450
451    private final class SettingsObserver extends ContentObserver {
452        SettingsObserver(Handler handler) {
453            super(handler);
454            ContentResolver resolver = mContext.getContentResolver();
455            resolver.registerContentObserver(Settings.Secure.getUriFor(
456                    Settings.Secure.AUTOFILL_SERVICE), false, this, UserHandle.USER_ALL);
457        }
458
459        @Override
460        public void onChange(boolean selfChange, Uri uri, int userId) {
461            synchronized (mLock) {
462                updateCachedServiceLocked(userId);
463            }
464        }
465    }
466}
467