AutofillManagerService.java revision 5f97880714e0ee2c503bca0d7681e896c53386b0
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.bundleToString;
23import static com.android.server.autofill.Helper.sDebug;
24import static com.android.server.autofill.Helper.sPartitionMaxCount;
25import static com.android.server.autofill.Helper.sVerbose;
26
27import android.annotation.NonNull;
28import android.annotation.Nullable;
29import android.app.ActivityManager;
30import android.app.ActivityThread;
31import android.content.BroadcastReceiver;
32import android.content.ComponentName;
33import android.content.ContentResolver;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.pm.PackageManager;
38import android.content.pm.UserInfo;
39import android.database.ContentObserver;
40import android.graphics.Rect;
41import android.net.Uri;
42import android.os.Binder;
43import android.os.Build;
44import android.os.Bundle;
45import android.os.Handler;
46import android.os.IBinder;
47import android.os.RemoteException;
48import android.os.ResultReceiver;
49import android.os.ShellCallback;
50import android.os.UserHandle;
51import android.os.UserManager;
52import android.os.UserManagerInternal;
53import android.provider.Settings;
54import android.service.autofill.FillEventHistory;
55import android.util.LocalLog;
56import android.util.Slog;
57import android.util.SparseArray;
58import android.util.SparseBooleanArray;
59import android.view.autofill.AutofillId;
60import android.view.autofill.AutofillManager;
61import android.view.autofill.AutofillManagerInternal;
62import android.view.autofill.AutofillValue;
63import android.view.autofill.IAutoFillManager;
64import android.view.autofill.IAutoFillManagerClient;
65
66import com.android.internal.annotations.GuardedBy;
67import com.android.internal.content.PackageMonitor;
68import com.android.internal.os.BackgroundThread;
69import com.android.internal.os.IResultReceiver;
70import com.android.internal.util.DumpUtils;
71import com.android.internal.util.Preconditions;
72import com.android.server.FgThread;
73import com.android.server.LocalServices;
74import com.android.server.SystemService;
75import com.android.server.autofill.ui.AutoFillUI;
76
77import java.io.FileDescriptor;
78import java.io.PrintWriter;
79import java.util.ArrayList;
80import java.util.List;
81import java.util.Objects;
82
83/**
84 * Entry point service for autofill management.
85 *
86 * <p>This service provides the {@link IAutoFillManager} implementation and keeps a list of
87 * {@link AutofillManagerServiceImpl} per user; the real work is done by
88 * {@link AutofillManagerServiceImpl} itself.
89 */
90public final class AutofillManagerService extends SystemService {
91
92    private static final String TAG = "AutofillManagerService";
93
94    static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
95
96    private final Context mContext;
97    private final AutoFillUI mUi;
98
99    private final Object mLock = new Object();
100
101    /**
102     * Cache of {@link AutofillManagerServiceImpl} per user id.
103     * <p>
104     * It has to be mapped by user id because the same current user could have simultaneous sessions
105     * associated to different user profiles (for example, in a multi-window environment or when
106     * device has work profiles).
107     */
108    @GuardedBy("mLock")
109    private SparseArray<AutofillManagerServiceImpl> mServicesCache = new SparseArray<>();
110
111    /**
112     * Users disabled due to {@link UserManager} restrictions.
113     */
114    @GuardedBy("mLock")
115    private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
116
117    private final LocalLog mRequestsHistory = new LocalLog(20);
118
119    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
120        @Override
121        public void onReceive(Context context, Intent intent) {
122            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
123                mUi.hideAll(null);
124            }
125        }
126    };
127
128    public AutofillManagerService(Context context) {
129        super(context);
130        mContext = context;
131        mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
132
133        final boolean debug = Build.IS_DEBUGGABLE;
134        Slog.i(TAG, "Setting debug to " + debug);
135        setDebugLocked(debug);
136
137        final IntentFilter filter = new IntentFilter();
138        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
139        mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
140
141        // Hookup with UserManager to disable service when necessary.
142        final UserManager um = context.getSystemService(UserManager.class);
143        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
144        final List<UserInfo> users = um.getUsers();
145        for (int i = 0; i < users.size(); i++) {
146            final int userId = users.get(i).id;
147            final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
148            if (disabled) {
149                if (disabled) {
150                    Slog.i(TAG, "Disabling Autofill for user " + userId);
151                }
152                mDisabledUsers.put(userId, disabled);
153            }
154        }
155        umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
156            final boolean disabledNow =
157                    newRestrictions.getBoolean(UserManager.DISALLOW_AUTOFILL, false);
158            synchronized (mLock) {
159                final boolean disabledBefore = mDisabledUsers.get(userId);
160                if (disabledBefore == disabledNow) {
161                    // Nothing changed, do nothing.
162                    if (sDebug) {
163                        Slog.d(TAG, "Autofill restriction did not change for user " + userId + ": "
164                                + bundleToString(newRestrictions));
165                        return;
166                    }
167                }
168                Slog.i(TAG, "Updating Autofill for user " + userId + ": disabled=" + disabledNow);
169                mDisabledUsers.put(userId, disabledNow);
170                updateCachedServiceLocked(userId, disabledNow);
171            }
172        });
173        startTrackingPackageChanges();
174    }
175
176    private void startTrackingPackageChanges() {
177        PackageMonitor monitor = new PackageMonitor() {
178            @Override
179            public void onSomePackagesChanged() {
180                synchronized (mLock) {
181                    updateCachedServiceLocked(getChangingUserId());
182                }
183            }
184
185            @Override
186            public void onPackageUpdateFinished(String packageName, int uid) {
187                synchronized (mLock) {
188                    final String activePackageName = getActiveAutofillServicePackageName();
189                    if (packageName.equals(activePackageName)) {
190                        removeCachedServiceLocked(getChangingUserId());
191                    }
192                }
193            }
194
195            @Override
196            public void onPackageRemoved(String packageName, int uid) {
197                synchronized (mLock) {
198                    final int userId = getChangingUserId();
199                    final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
200                    if (userState != null) {
201                        final ComponentName componentName = userState.getServiceComponentName();
202                        if (componentName != null) {
203                            if (packageName.equals(componentName.getPackageName())) {
204                                handleActiveAutofillServiceRemoved(userId);
205                            }
206                        }
207                    }
208                }
209            }
210
211            @Override
212            public boolean onHandleForceStop(Intent intent, String[] packages,
213                    int uid, boolean doit) {
214                synchronized (mLock) {
215                    final String activePackageName = getActiveAutofillServicePackageName();
216                    for (String pkg : packages) {
217                        if (pkg.equals(activePackageName)) {
218                            if (!doit) {
219                                return true;
220                            }
221                            removeCachedServiceLocked(getChangingUserId());
222                        }
223                    }
224                }
225                return false;
226            }
227
228            private void handleActiveAutofillServiceRemoved(int userId) {
229                removeCachedServiceLocked(userId);
230                Settings.Secure.putStringForUser(mContext.getContentResolver(),
231                        Settings.Secure.AUTOFILL_SERVICE, null, userId);
232            }
233
234            private String getActiveAutofillServicePackageName() {
235                final int userId = getChangingUserId();
236                final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
237                if (userState == null) {
238                    return null;
239                }
240                final ComponentName serviceComponent = userState.getServiceComponentName();
241                if (serviceComponent == null) {
242                    return null;
243                }
244                return serviceComponent.getPackageName();
245            }
246        };
247
248        // package changes
249        monitor.register(mContext, null,  UserHandle.ALL, true);
250    }
251
252    @Override
253    public void onStart() {
254        publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
255        publishLocalService(AutofillManagerInternal.class, new LocalService());
256    }
257
258    @Override
259    public void onBootPhase(int phase) {
260        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
261            new SettingsObserver(BackgroundThread.getHandler());
262        }
263    }
264
265    @Override
266    public void onUnlockUser(int userId) {
267        synchronized (mLock) {
268            updateCachedServiceLocked(userId);
269        }
270    }
271
272    @Override
273    public void onCleanupUser(int userId) {
274        synchronized (mLock) {
275            removeCachedServiceLocked(userId);
276        }
277    }
278
279    /**
280     * Gets the service instance for an user.
281     *
282     * @return service instance.
283     */
284    @NonNull
285    AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
286        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
287                Binder.getCallingUid(), userId, false, false, null, null);
288        AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
289        if (service == null) {
290            service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
291                    resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
292            mServicesCache.put(userId, service);
293        }
294        return service;
295    }
296
297    /**
298     * Peeks the service instance for a user.
299     *
300     * @return service instance or {@code null} if not already present
301     */
302    @Nullable
303    AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
304        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
305                Binder.getCallingUid(), userId, false, false, null, null);
306        return mServicesCache.get(resolvedUserId);
307    }
308
309    // Called by Shell command.
310    void destroySessions(int userId, IResultReceiver receiver) {
311        Slog.i(TAG, "destroySessions() for userId " + userId);
312        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
313
314        synchronized (mLock) {
315            if (userId != UserHandle.USER_ALL) {
316                AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
317                if (service != null) {
318                    service.destroySessionsLocked();
319                }
320            } else {
321                final int size = mServicesCache.size();
322                for (int i = 0; i < size; i++) {
323                    mServicesCache.valueAt(i).destroySessionsLocked();
324                }
325            }
326        }
327
328        try {
329            receiver.send(0, new Bundle());
330        } catch (RemoteException e) {
331            // Just ignore it...
332        }
333    }
334
335    // Called by Shell command.
336    void listSessions(int userId, IResultReceiver receiver) {
337        Slog.i(TAG, "listSessions() for userId " + userId);
338        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
339
340        final Bundle resultData = new Bundle();
341        final ArrayList<String> sessions = new ArrayList<>();
342
343        synchronized (mLock) {
344            if (userId != UserHandle.USER_ALL) {
345                AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
346                if (service != null) {
347                    service.listSessionsLocked(sessions);
348                }
349            } else {
350                final int size = mServicesCache.size();
351                for (int i = 0; i < size; i++) {
352                    mServicesCache.valueAt(i).listSessionsLocked(sessions);
353                }
354            }
355        }
356
357        resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
358        try {
359            receiver.send(0, resultData);
360        } catch (RemoteException e) {
361            // Just ignore it...
362        }
363    }
364
365    // Called by Shell command.
366    void reset() {
367        Slog.i(TAG, "reset()");
368        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
369
370        synchronized (mLock) {
371            final int size = mServicesCache.size();
372            for (int i = 0; i < size; i++) {
373                mServicesCache.valueAt(i).destroyLocked();
374            }
375            mServicesCache.clear();
376        }
377    }
378
379    // Called by Shell command.
380    void setLogLevel(int level) {
381        Slog.i(TAG, "setLogLevel(): " + level);
382        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
383
384        boolean debug = false;
385        boolean verbose = false;
386        if (level == AutofillManager.FLAG_ADD_CLIENT_VERBOSE) {
387            debug = verbose = true;
388        } else if (level == AutofillManager.FLAG_ADD_CLIENT_DEBUG) {
389            debug = true;
390        }
391        synchronized (mLock) {
392            setDebugLocked(debug);
393            setVerboseLocked(verbose);
394        }
395    }
396
397    // Called by Shell command.
398    int getLogLevel() {
399        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
400
401        synchronized (mLock) {
402            if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
403            if (sDebug) return AutofillManager.FLAG_ADD_CLIENT_DEBUG;
404            return 0;
405        }
406    }
407
408    // Called by Shell command.
409    public int getMaxPartitions() {
410        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
411
412        synchronized (mLock) {
413            return sPartitionMaxCount;
414        }
415    }
416
417    // Called by Shell command.
418    public void setMaxPartitions(int max) {
419        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
420        Slog.i(TAG, "setMaxPartitions(): " + max);
421        synchronized (mLock) {
422            sPartitionMaxCount = max;
423        }
424    }
425
426    private void setDebugLocked(boolean debug) {
427        com.android.server.autofill.Helper.sDebug = debug;
428        android.view.autofill.Helper.sDebug = debug;
429    }
430
431
432    private void setVerboseLocked(boolean verbose) {
433        com.android.server.autofill.Helper.sVerbose = verbose;
434        android.view.autofill.Helper.sVerbose = verbose;
435    }
436
437    /**
438     * Removes a cached service for a given user.
439     */
440    private void removeCachedServiceLocked(int userId) {
441        final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
442        if (service != null) {
443            mServicesCache.delete(userId);
444            service.destroyLocked();
445        }
446    }
447
448    /**
449     * Updates a cached service for a given user.
450     */
451    private void updateCachedServiceLocked(int userId) {
452        updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
453    }
454
455    /**
456     * Updates a cached service for a given user.
457     */
458    private void updateCachedServiceLocked(int userId, boolean disabled) {
459        AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
460        if (service != null) {
461            service.updateLocked(disabled);
462            if (!service.isEnabled()) {
463                removeCachedServiceLocked(userId);
464            }
465        }
466    }
467
468    private final class LocalService extends AutofillManagerInternal {
469
470        @Override
471        public void onBackKeyPressed() {
472            if (sDebug) Slog.d(TAG, "onBackKeyPressed()");
473            mUi.hideAll(null);
474        }
475    }
476
477    final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
478        @Override
479        public int addClient(IAutoFillManagerClient client, int userId) {
480            synchronized (mLock) {
481                int flags = 0;
482                if (getServiceForUserLocked(userId).addClientLocked(client)) {
483                    flags |= AutofillManager.FLAG_ADD_CLIENT_ENABLED;
484                }
485                if (sDebug) {
486                    flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG;
487                }
488                if (sVerbose) {
489                    flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
490                }
491                return flags;
492            }
493        }
494
495        @Override
496        public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId,
497                int userId) {
498            synchronized (mLock) {
499                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
500                service.setAuthenticationResultLocked(data, sessionId, authenticationId,
501                        getCallingUid());
502            }
503        }
504
505        @Override
506        public void setHasCallback(int sessionId, int userId, boolean hasIt) {
507            synchronized (mLock) {
508                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
509                service.setHasCallback(sessionId, getCallingUid(), hasIt);
510            }
511        }
512
513        @Override
514        public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
515                Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags,
516                String packageName) {
517
518            activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
519            appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
520            autofillId = Preconditions.checkNotNull(autofillId, "autoFillId");
521            packageName = Preconditions.checkNotNull(packageName, "packageName");
522
523            Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
524
525            try {
526                mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
527            } catch (PackageManager.NameNotFoundException e) {
528                throw new IllegalArgumentException(packageName + " is not a valid package", e);
529            }
530
531            synchronized (mLock) {
532                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
533                return service.startSessionLocked(activityToken, getCallingUid(), appCallback,
534                        autofillId, bounds, value, hasCallback, flags, packageName);
535            }
536        }
537
538        @Override
539        public FillEventHistory getFillEventHistory() throws RemoteException {
540            UserHandle user = getCallingUserHandle();
541            int uid = getCallingUid();
542
543            synchronized (mLock) {
544                AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
545                if (service != null) {
546                    return service.getFillEventHistory(uid);
547                }
548            }
549
550            return null;
551        }
552
553        @Override
554        public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
555                throws RemoteException {
556            activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
557            appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
558
559            synchronized (mLock) {
560                final AutofillManagerServiceImpl service = mServicesCache.get(
561                        UserHandle.getCallingUserId());
562                if (service != null) {
563                    return service.restoreSession(sessionId, getCallingUid(), activityToken,
564                            appCallback);
565                }
566            }
567
568            return false;
569        }
570
571        @Override
572        public void updateSession(int sessionId, AutofillId autoFillId, Rect bounds,
573                AutofillValue value, int action, int flags, int userId) {
574            synchronized (mLock) {
575                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
576                if (service != null) {
577                    service.updateSessionLocked(sessionId, getCallingUid(), autoFillId, bounds,
578                            value, action, flags);
579                }
580            }
581        }
582
583        @Override
584        public int updateOrRestartSession(IBinder activityToken, IBinder appCallback,
585                AutofillId autoFillId, Rect bounds, AutofillValue value, int userId,
586                boolean hasCallback, int flags, String packageName, int sessionId, int action) {
587            boolean restart = false;
588            synchronized (mLock) {
589                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
590                if (service != null) {
591                    restart = service.updateSessionLocked(sessionId, getCallingUid(), autoFillId,
592                            bounds, value, action, flags);
593                }
594            }
595            if (restart) {
596                return startSession(activityToken, appCallback, autoFillId, bounds, value, userId,
597                        hasCallback, flags, packageName);
598            }
599
600            // Nothing changed...
601            return sessionId;
602        }
603
604        @Override
605        public void finishSession(int sessionId, int userId) {
606            synchronized (mLock) {
607                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
608                if (service != null) {
609                    service.finishSessionLocked(sessionId, getCallingUid());
610                }
611            }
612        }
613
614        @Override
615        public void cancelSession(int sessionId, int userId) {
616            synchronized (mLock) {
617                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
618                if (service != null) {
619                    service.cancelSessionLocked(sessionId, getCallingUid());
620                }
621            }
622        }
623
624        @Override
625        public void disableOwnedAutofillServices(int userId) {
626            synchronized (mLock) {
627                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
628                if (service != null) {
629                    service.disableOwnedAutofillServicesLocked(Binder.getCallingUid());
630                }
631            }
632        }
633
634        @Override
635        public boolean isServiceSupported(int userId) {
636            synchronized (mLock) {
637                return !mDisabledUsers.get(userId);
638            }
639        }
640
641        @Override
642        public boolean isServiceEnabled(int userId, String packageName) {
643            synchronized (mLock) {
644                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
645                if (service == null) return false;
646                return Objects.equals(packageName, service.getPackageName());
647            }
648        }
649
650        @Override
651        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
652            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
653
654            boolean showHistory = true;
655            boolean uiOnly = false;
656            if (args != null) {
657                for (String arg : args) {
658                    switch(arg) {
659                        case "--no-history":
660                            showHistory = false;
661                            break;
662                        case "--ui-only":
663                            uiOnly = true;
664                            break;
665                        case "--help":
666                            pw.println("Usage: dumpsys autofill [--ui-only|--no-history]");
667                            return;
668                        default:
669                            Slog.w(TAG, "Ignoring invalid dump arg: " + arg);
670                    }
671                }
672            }
673
674            if (uiOnly) {
675                mUi.dump(pw);
676                return;
677            }
678
679            boolean oldDebug = sDebug;
680            try {
681                synchronized (mLock) {
682                    oldDebug = sDebug;
683                    setDebugLocked(true);
684                    pw.print("Debug mode: "); pw.println(oldDebug);
685                    pw.print("Verbose mode: "); pw.println(sVerbose);
686                    pw.print("Disabled users: "); pw.println(mDisabledUsers);
687                    pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
688                    final int size = mServicesCache.size();
689                    pw.print("Cached services: ");
690                    if (size == 0) {
691                        pw.println("none");
692                    } else {
693                        pw.println(size);
694                        for (int i = 0; i < size; i++) {
695                            pw.print("\nService at index "); pw.println(i);
696                            final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
697                            impl.dumpLocked("  ", pw);
698                        }
699                    }
700                    mUi.dump(pw);
701                }
702                if (showHistory) {
703                    pw.println("Requests history:");
704                    mRequestsHistory.reverseDump(fd, pw, args);
705                }
706            } finally {
707                setDebugLocked(oldDebug);
708            }
709        }
710
711        @Override
712        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
713                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
714            (new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
715                    this, in, out, err, args, callback, resultReceiver);
716        }
717    }
718
719    private final class SettingsObserver extends ContentObserver {
720        SettingsObserver(Handler handler) {
721            super(handler);
722            ContentResolver resolver = mContext.getContentResolver();
723            resolver.registerContentObserver(Settings.Secure.getUriFor(
724                    Settings.Secure.AUTOFILL_SERVICE), false, this, UserHandle.USER_ALL);
725            resolver.registerContentObserver(Settings.Secure.getUriFor(
726                    Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
727        }
728
729        @Override
730        public void onChange(boolean selfChange, Uri uri, int userId) {
731            if (sVerbose) Slog.v(TAG, "onChange(): uri=" + uri + ", userId=" + userId);
732            synchronized (mLock) {
733                updateCachedServiceLocked(userId);
734            }
735        }
736    }
737}
738