blob: 8b3775657da0a79fe0090a58e29097ad550dfa8b [file] [log] [blame]
Felipe Leme5381aa42016-10-13 09:02:32 -07001/*
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.AUTO_FILL_MANAGER_SERVICE;
21
22import android.Manifest;
23import android.app.ActivityManager;
24import android.app.AppGlobals;
25import android.content.ComponentName;
26import android.content.ContentResolver;
27import android.content.Context;
28import android.content.pm.PackageManager;
29import android.content.pm.ServiceInfo;
30import android.database.ContentObserver;
31import android.net.Uri;
32import android.os.Binder;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Parcel;
37import android.os.RemoteException;
38import android.os.ResultReceiver;
39import android.os.ShellCallback;
40import android.os.UserHandle;
41import android.provider.Settings;
42import android.service.autofill.IAutoFillManagerService;
43import android.text.TextUtils;
44import android.util.Slog;
45import android.util.SparseArray;
46
47import com.android.internal.annotations.GuardedBy;
48import com.android.internal.os.BackgroundThread;
49import com.android.server.FgThread;
50import com.android.server.SystemService;
51
52import java.io.FileDescriptor;
53import java.io.PrintWriter;
54
55/**
56 * Entry point service for auto-fill management.
57 *
58 * <p>This service provides the {@link IAutoFillManagerService} implementation and keeps a list of
59 * {@link AutoFillManagerServiceImpl} per user; the real work is done by
60 * {@link AutoFillManagerServiceImpl} itself.
61 */
62public final class AutoFillManagerService extends SystemService {
63
64 private static final String TAG = "AutoFillManagerService";
65 private static final boolean DEBUG = true; // TODO: change to false once stable
66
67 private final AutoFillManagerServiceStub mServiceStub;
68 private final Context mContext;
69 private final ContentResolver mResolver;
70
71 private final Object mLock = new Object();
72
73 @GuardedBy("mLock")
74 private boolean mSafeMode;
75
76 /**
77 * Map of {@link AutoFillManagerServiceImpl} per user id.
78 * <p>
79 * It has to be mapped by user id because the same current user could have simultaneous sessions
80 * associated to different user profiles (for example, in a multi-window environment).
81 * <p>
82 * This map is filled on demand in the following scenarios:
83 * <ol>
84 * <li>On start, it sets the value for the default user.
85 * <li>When an auto-fill service app is removed, its entries are removed.
86 * <li>When the current user changes.
87 * <li>When the {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} changes.
88 * </ol>
89 */
90 // TODO: make sure all cases listed above are handled
91 // TODO: should entries be removed when there is no section and have not be used for a while?
92 @GuardedBy("mLock")
93 private SparseArray<AutoFillManagerServiceImpl> mImplByUser = new SparseArray<>();
94
95 // TODO: should disable it on low-memory devices? if not, this attribute should be removed...
96 private final boolean mEnableService = true;
97
98 public AutoFillManagerService(Context context) {
99 super(context);
100
101 mContext = context;
102 mResolver = context.getContentResolver();
103 mServiceStub = new AutoFillManagerServiceStub();
104 }
105
106 @Override
107 public void onStart() {
108 if (DEBUG)
109 Slog.d(TAG, "onStart(): binding as " + AUTO_FILL_MANAGER_SERVICE);
110 publishBinderService(AUTO_FILL_MANAGER_SERVICE, mServiceStub);
111 }
112
113 // TODO: refactor so it's bound on demand, in which case it can use isSafeMode() from PM.
114 @Override
115 public void onBootPhase(int phase) {
116 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
117 systemRunning(isSafeMode());
118 }
119 }
120
121 // TODO: refactor so it's bound on demand, in which case it can use isSafeMode() from PM.
122 @Override
123 public void onStartUser(int userHandle) {
124 if (DEBUG) Slog.d(TAG, "onStartUser(): userHandle=" + userHandle);
125
126 updateImplementationIfNeeded(userHandle, false);
127 }
128
129 @Override
130 public void onUnlockUser(int userHandle) {
131 if (DEBUG) Slog.d(TAG, "onUnlockUser(): userHandle=" + userHandle);
132
133 updateImplementationIfNeeded(userHandle, false);
134 }
135
136 @Override
137 public void onSwitchUser(int userHandle) {
138 if (DEBUG) Slog.d(TAG, "onSwitchUser(): userHandle=" + userHandle);
139
140 updateImplementationIfNeeded(userHandle, false);
141 }
142
143 private void systemRunning(boolean safeMode) {
144 if (DEBUG) Slog.d(TAG, "systemRunning(): safeMode=" + safeMode);
145
146 // TODO: register a PackageMonitor
147 new SettingsObserver(BackgroundThread.getHandler());
148
149 synchronized (mLock) {
150 mSafeMode = safeMode;
151 updateImplementationIfNeededLocked(ActivityManager.getCurrentUser(), false);
152 }
153 }
154
155 private void updateImplementationIfNeeded(int user, boolean force) {
156 synchronized (mLock) {
157 updateImplementationIfNeededLocked(user, force);
158 }
159 }
160
161 private void updateImplementationIfNeededLocked(int user, boolean force) {
162 if (DEBUG)
163 Slog.d(TAG, "updateImplementationIfNeededLocked(" + user + ", " + force + ")");
164
165 if (mSafeMode) {
166 if (DEBUG) Slog.d(TAG, "skipping on safe mode");
167 return;
168 }
169
170 final String curService = Settings.Secure.getStringForUser(
171 mResolver, Settings.Secure.AUTO_FILL_SERVICE, user);
172 if (DEBUG)
173 Slog.d(TAG, "Current service settings for user " + user + ": " + curService);
174 ComponentName serviceComponent = null;
175 ServiceInfo serviceInfo = null;
176 if (!TextUtils.isEmpty(curService)) {
177 try {
178 serviceComponent = ComponentName.unflattenFromString(curService);
179 serviceInfo =
180 AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, user);
181 } catch (RuntimeException | RemoteException e) {
182 Slog.wtf(TAG, "Bad auto-fill service name " + curService, e);
183 serviceComponent = null;
184 serviceInfo = null;
185 }
186 }
187
188 final AutoFillManagerServiceImpl impl = mImplByUser.get(user);
189 if (DEBUG) Slog.d(TAG, "Current impl: " + impl + " component: " + serviceComponent
190 + " info: " + serviceInfo);
191
192 if (force || impl == null || !impl.mComponent.equals(serviceComponent)) {
193 if (impl != null) {
194 impl.shutdownLocked();
195 }
196 if (serviceInfo != null) {
197 final AutoFillManagerServiceImpl newImpl = new AutoFillManagerServiceImpl(mContext,
198 mLock, mServiceStub, FgThread.getHandler(), user, serviceComponent);
199 if (DEBUG) Slog.d(TAG, "Setting impl for user " + user + " as: " + newImpl);
200 mImplByUser.put(user, newImpl);
201 newImpl.startLocked();
202 } else {
203 if (DEBUG) Slog.d(TAG, "Removing impl for user " + user + ": " + impl);
204 mImplByUser.remove(user);
205 }
206 }
207 }
208
209 // TODO: might need to return null instead of throw exception
210 private AutoFillManagerServiceImpl getImplOrThrowLocked(int userId) {
211 final AutoFillManagerServiceImpl impl = mImplByUser.get(userId);
212 if (impl == null) {
213 throw new IllegalStateException("no auto-fill service for user " + userId);
214 }
215 return impl;
216 }
217
218 final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
219
220 @Override
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700221 public boolean requestAutoFill(int userId, IBinder activityToken) {
Felipe Leme5381aa42016-10-13 09:02:32 -0700222 mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
223
224 synchronized (mLock) {
Felipe Leme29a5b0d2016-10-25 14:57:11 -0700225 return getImplOrThrowLocked(userId).requestAutoFill(activityToken);
Felipe Leme5381aa42016-10-13 09:02:32 -0700226 }
227 }
228
229 @Override
230 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
231 if (mContext.checkCallingPermission(
232 Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
233 pw.println("Permission Denial: can't dump autofill from from pid="
234 + Binder.getCallingPid()
235 + ", uid=" + Binder.getCallingUid());
236 return;
237 }
Felipe Leme5381aa42016-10-13 09:02:32 -0700238 synchronized (mLock) {
239 pw.print("mEnableService: "); pw.println(mEnableService);
240 pw.print("mSafeMode: "); pw.println(mSafeMode);
241 final int size = mImplByUser.size();
242 pw.print("Number of implementations: ");
243 if (size == 0) {
244 pw.println("none");
245 } else {
246 pw.println(size);
247 for (int i = 0; i < size; i++) {
248 pw.print("\nImplementation at index "); pw.println(i);
249 final AutoFillManagerServiceImpl impl = mImplByUser.valueAt(i);
250 impl.dumpLocked(" ", pw);
251 }
252 }
253 }
254 }
255
Felipe Leme5381aa42016-10-13 09:02:32 -0700256 @Override
257 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
258 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
259 (new AutoFillManagerServiceShellCommand(this)).exec(
260 this, in, out, err, args, callback, resultReceiver);
261 }
262
263 }
264
265 private final class SettingsObserver extends ContentObserver {
266 SettingsObserver(Handler handler) {
267 super(handler);
268 ContentResolver resolver = mContext.getContentResolver();
269 resolver.registerContentObserver(Settings.Secure.getUriFor(
270 Settings.Secure.AUTO_FILL_SERVICE), false, this,
271 UserHandle.USER_ALL);
272 }
273
274 @Override
275 public void onChange(boolean selfChange, Uri uri, int userId) {
276 synchronized (mLock) {
277 updateImplementationIfNeededLocked(userId, false);
278 }
279 }
280 }
281}