blob: 6cae887d3ffc3adce4fd4b328e57520e0dee5283 [file] [log] [blame]
Felipe Lemeea95a6d2018-10-15 10:45:01 -07001/*
2 * Copyright (C) 2018 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 */
16package com.android.server;
17
18import android.annotation.NonNull;
19import android.annotation.Nullable;
20import android.annotation.UserIdInt;
21import android.app.ActivityManager;
22import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.UserInfo;
27import android.database.ContentObserver;
28import android.net.Uri;
29import android.os.Binder;
30import android.os.Handler;
31import android.os.UserHandle;
32import android.os.UserManager;
33import android.os.UserManagerInternal;
34import android.provider.Settings;
35import android.util.Slog;
36import android.util.SparseArray;
37import android.util.SparseBooleanArray;
38
39import com.android.internal.annotations.GuardedBy;
40import com.android.internal.content.PackageMonitor;
41import com.android.internal.os.BackgroundThread;
42
43import java.io.PrintWriter;
44import java.util.List;
45
46/**
47 * Base class for {@link SystemService SystemServices} that support multi user.
48 *
49 * <p>Subclasses of this service are just a facade for the service binder calls - the "real" work
50 * is done by the {@link AbstractPerUserSystemService} subclasses, which are automatically managed
51 * through an user -> service cache.
52 *
53 * <p>It also takes care of other plumbing tasks such as:
54 *
55 * <ul>
56 * <li>Disabling the service when {@link UserManager} restrictions change.
57 * <li>Refreshing the service when its underlying
58 * {@link #getServiceSettingsProperty() Settings property} changed.
59 * <li>Calling the service when other Settings properties changed.
60 * </ul>
61 *
62 * <p>See {@code com.android.server.autofill.AutofillManagerService} for a concrete
63 * (no pun intended) example of how to use it.
64 *
65 * @param <S> "real" service class.
66 *
67 * @hide
68 */
69// TODO(b/117779333): improve javadoc above instead of using Autofill as an example
70public abstract class AbstractMasterSystemService<S extends AbstractPerUserSystemService<S>>
71 extends SystemService {
72
73 /**
74 * Log tag
75 */
76 protected final String mTag = getClass().getSimpleName();
77
78 /**
79 * Lock used to synchronize access to internal state; should be acquired before calling a
80 * method whose name ends with {@code locked}.
81 */
82 protected final Object mLock = new Object();
83
84 /**
85 * Whether the service should log debug statements.
86 */
87 public boolean verbose = false;
88
89 /**
90 * Whether the service should log verbose statements.
91 */
92 public boolean debug = false;
93
94 /**
95 * Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
96 * be disabled through {@link UserManager}.
97 */
98 @GuardedBy("mLock")
99 @Nullable
100 private final SparseBooleanArray mDisabledUsers;
101
102 /**
103 * Cache of services per user id.
104 */
105 @GuardedBy("mLock")
106 private final SparseArray<S> mServicesCache = new SparseArray<>();
107
108 /**
109 * Default constructor.
110 *
111 * @param context system context.
112 * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
113 * disables the service.
114 */
115 protected AbstractMasterSystemService(@NonNull Context context,
116 @Nullable String disallowProperty) {
117 super(context);
118
119 if (disallowProperty == null) {
120 mDisabledUsers = null;
121 } else {
122 mDisabledUsers = new SparseBooleanArray();
123 // Hookup with UserManager to disable service when necessary.
124 final UserManager um = context.getSystemService(UserManager.class);
125 final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
126 final List<UserInfo> users = um.getUsers();
127 for (int i = 0; i < users.size(); i++) {
128 final int userId = users.get(i).id;
129 final boolean disabled = umi.getUserRestriction(userId, disallowProperty);
130 if (disabled) {
131 Slog.i(mTag, "Disabling for user " + userId);
132 mDisabledUsers.put(userId, disabled);
133 }
134 }
135 umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
136 final boolean disabledNow =
137 newRestrictions.getBoolean(disallowProperty, false);
138 synchronized (mLock) {
139 final boolean disabledBefore = mDisabledUsers.get(userId);
140 if (disabledBefore == disabledNow) {
141 // Nothing changed, do nothing.
142 if (debug) {
143 Slog.d(mTag, "Restriction did not change for user " + userId);
144 return;
145 }
146 }
147 Slog.i(mTag, "Updating for user " + userId + ": disabled=" + disabledNow);
148 mDisabledUsers.put(userId, disabledNow);
149 updateCachedServiceLocked(userId, disabledNow);
150 }
151 });
152 }
153 startTrackingPackageChanges();
154 }
155
156 @Override // from SystemService
157 public void onBootPhase(int phase) {
158 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
159 new SettingsObserver(BackgroundThread.getHandler());
160 }
161 }
162
163 @Override // from SystemService
164 public void onUnlockUser(int userId) {
165 synchronized (mLock) {
166 updateCachedServiceLocked(userId);
167 }
168 }
169
170 @Override // from SystemService
171 public void onCleanupUser(int userId) {
172 synchronized (mLock) {
173 removeCachedServiceLocked(userId);
174 }
175 }
176
177 /**
178 * Creates a new service that will be added to the cache.
179 *
180 * @param resolvedUserId the resolved user id for the service.
181 * @param disabled whether the service is currently disabled (due to {@link UserManager}
182 * restrictions).
183 *
184 * @return a new instance.
185 */
186 protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
187
188 /**
189 * Register the service for extra Settings changes (i.e., other than
190 * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
191 * {@link #getServiceSettingsProperty()}, which are automatically handled).
192 *
193 * <p> Example:
194 *
195 * <pre><code>
196 * resolver.registerContentObserver(Settings.Global.getUriFor(
197 * Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
198 * UserHandle.USER_ALL);
199 * </code></pre>
200 *
201 * <p><b>NOTE: </p>it doesn't need to register for
202 * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
203 * {@link #getServiceSettingsProperty()}.
204 *
205 */
206 @SuppressWarnings("unused")
207 protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
208 @NonNull ContentObserver observer) {
209 }
210
211 /**
212 * Callback for Settings changes that were registered though
213 * {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
214 *
215 * @param userId user associated with the change
216 * @param property Settings property changed.
217 */
218 protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
219 }
220
221 /**
222 * Gets the service instance for an user, creating an instance if not present in the cache.
223 */
224 @GuardedBy("mLock")
225 @NonNull
226 protected S getServiceForUserLocked(@UserIdInt int userId) {
227 final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
228 Binder.getCallingUid(), userId, false, false, null, null);
229 S service = mServicesCache.get(resolvedUserId);
230 if (service == null) {
231 final boolean disabled = isDisabledLocked(userId);
232 service = newServiceLocked(resolvedUserId, disabled);
233 if (!disabled) {
234 onServiceEnabledLocked(service, resolvedUserId);
235 }
236 mServicesCache.put(userId, service);
237 }
238 return service;
239 }
240
241 /**
242 * Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
243 * present in the cache.
244 */
245 @GuardedBy("mLock")
246 @Nullable
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800247 protected S peekServiceForUserLocked(@UserIdInt int userId) {
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700248 final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
249 Binder.getCallingUid(), userId, false, false, null, null);
250 return mServicesCache.get(resolvedUserId);
251 }
252
253 /**
254 * Updates a cached service for a given user.
255 */
256 @GuardedBy("mLock")
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800257 protected void updateCachedServiceLocked(@UserIdInt int userId) {
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700258 updateCachedServiceLocked(userId, isDisabledLocked(userId));
259 }
260
261 /**
262 * Checks whether the service is disabled (through {@link UserManager} restrictions) for the
263 * given user.
264 */
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800265 protected boolean isDisabledLocked(@UserIdInt int userId) {
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700266 return mDisabledUsers == null ? false : mDisabledUsers.get(userId);
267 }
268
269 /**
270 * Updates a cached service for a given user.
271 *
272 * @param userId user handle.
273 * @param disabled whether the user is disabled.
274 * @return service for the user.
275 */
276 @GuardedBy("mLock")
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800277 protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700278 final S service = getServiceForUserLocked(userId);
279 if (service != null) {
280 service.updateLocked(disabled);
281 if (!service.isEnabledLocked()) {
282 removeCachedServiceLocked(userId);
283 } else {
284 onServiceEnabledLocked(service, userId);
285 }
286 }
287 return service;
288 }
289
290 /**
291 * Gets the Settings property that defines the name of the component name used to bind this
292 * service to an external service, or {@code null} when the service is not defined by such
293 * property (for example, if it's a system service defined by framework resources).
294 */
295 @Nullable
296 protected String getServiceSettingsProperty() {
297 return null;
298 }
299
300 /**
301 * Callback called after a new service was added to the cache, or an existing service that was
302 * previously disabled gets enabled.
303 *
304 * <p>By default doesn't do anything, but can be overridden by subclasses.
305 */
306 @SuppressWarnings("unused")
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800307 protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700308 }
309
310 /**
311 * Removes a cached service for a given user.
312 *
313 * @return the removed service;
314 */
315 @GuardedBy("mLock")
316 @NonNull
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800317 private S removeCachedServiceLocked(@UserIdInt int userId) {
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700318 final S service = peekServiceForUserLocked(userId);
319 if (service != null) {
320 mServicesCache.delete(userId);
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800321 onServiceRemoved(service, userId);
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700322 }
323 return service;
324 }
325
326 /**
Felipe Leme7ed7b2c2018-11-14 17:47:15 -0800327 * Called after the service is removed from the cache.
328 */
329 @SuppressWarnings("unused")
330 protected void onServiceRemoved(@NonNull S service, @UserIdInt int userId) {
331 }
332
333 /**
Felipe Lemeea95a6d2018-10-15 10:45:01 -0700334 * Visits all services in the cache.
335 */
336 @GuardedBy("mLock")
337 protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
338 final int size = mServicesCache.size();
339 for (int i = 0; i < size; i++) {
340 visitor.visit(mServicesCache.valueAt(i));
341 }
342 }
343
344 /**
345 * Clear the cache by removing all services.
346 */
347 @GuardedBy("mLock")
348 protected void clearCacheLocked() {
349 mServicesCache.clear();
350 }
351
352 // TODO(b/117779333): support proto
353 protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
354 boolean realDebug = debug;
355 boolean realVerbose = verbose;
356
357 try {
358 // Temporarily turn on full logging;
359 debug = verbose = true;
360 final int size = mServicesCache.size();
361 pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
362 pw.print(" Verbose: "); pw.println(realVerbose);
363 pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
364 pw.print(prefix); pw.print("Settings property: "); pw.println(
365 getServiceSettingsProperty());
366 pw.print(prefix); pw.print("Cached services: ");
367 if (size == 0) {
368 pw.println("none");
369 } else {
370 pw.println(size);
371 final String prefix2 = " ";
372 for (int i = 0; i < size; i++) {
373 pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
374 final S service = mServicesCache.valueAt(i);
375 service.dumpLocked(prefix2, pw);
376 pw.println();
377 }
378 }
379 } finally {
380 debug = realDebug;
381 verbose = realVerbose;
382 }
383 }
384
385 private void startTrackingPackageChanges() {
386 PackageMonitor monitor = new PackageMonitor() {
387 @Override
388 public void onSomePackagesChanged() {
389 synchronized (mLock) {
390 updateCachedServiceLocked(getChangingUserId());
391 }
392 }
393
394 @Override
395 public void onPackageUpdateFinished(String packageName, int uid) {
396 synchronized (mLock) {
397 final String activePackageName = getActiveServicePackageName();
398 if (packageName.equals(activePackageName)) {
399 removeCachedServiceLocked(getChangingUserId());
400 } else {
401 handlePackageUpdateLocked(packageName);
402 }
403 }
404 }
405
406 @Override
407 public void onPackageRemoved(String packageName, int uid) {
408 synchronized (mLock) {
409 final int userId = getChangingUserId();
410 final S service = peekServiceForUserLocked(userId);
411 if (service != null) {
412 final ComponentName componentName = service.getServiceComponentName();
413 if (componentName != null) {
414 if (packageName.equals(componentName.getPackageName())) {
415 handleActiveServiceRemoved(userId);
416 }
417 }
418 }
419 }
420 }
421
422 @Override
423 public boolean onHandleForceStop(Intent intent, String[] packages,
424 int uid, boolean doit) {
425 synchronized (mLock) {
426 final String activePackageName = getActiveServicePackageName();
427 for (String pkg : packages) {
428 if (pkg.equals(activePackageName)) {
429 if (!doit) {
430 return true;
431 }
432 removeCachedServiceLocked(getChangingUserId());
433 } else {
434 handlePackageUpdateLocked(pkg);
435 }
436 }
437 }
438 return false;
439 }
440
441 private void handleActiveServiceRemoved(@UserIdInt int userId) {
442 removeCachedServiceLocked(userId);
443 final String serviceSettingsProperty = getServiceSettingsProperty();
444 if (serviceSettingsProperty != null) {
445 Settings.Secure.putStringForUser(getContext().getContentResolver(),
446 serviceSettingsProperty, null, userId);
447 }
448 }
449
450 private String getActiveServicePackageName() {
451 final int userId = getChangingUserId();
452 final S service = peekServiceForUserLocked(userId);
453 if (service == null) {
454 return null;
455 }
456 final ComponentName serviceComponent = service.getServiceComponentName();
457 if (serviceComponent == null) {
458 return null;
459 }
460 return serviceComponent.getPackageName();
461 }
462
463 @GuardedBy("mLock")
464 private void handlePackageUpdateLocked(String packageName) {
465 visitServicesLocked((s) -> s.handlePackageUpdateLocked(packageName));
466 }
467 };
468
469 // package changes
470 monitor.register(getContext(), null, UserHandle.ALL, true);
471 }
472
473 /**
474 * Visitor pattern.
475 *
476 * @param <S> visited class.
477 */
478 public interface Visitor<S> {
479 /**
480 * Visits a service.
481 *
482 * @param service the service to be visited.
483 */
484 void visit(@NonNull S service);
485 }
486
487 private final class SettingsObserver extends ContentObserver {
488 SettingsObserver(Handler handler) {
489 super(handler);
490 ContentResolver resolver = getContext().getContentResolver();
491 final String serviceProperty = getServiceSettingsProperty();
492 if (serviceProperty != null) {
493 resolver.registerContentObserver(Settings.Secure.getUriFor(
494 serviceProperty), false, this, UserHandle.USER_ALL);
495 }
496 resolver.registerContentObserver(Settings.Secure.getUriFor(
497 Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
498 registerForExtraSettingsChanges(resolver, this);
499 }
500
501 @Override
502 public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
503 if (verbose) Slog.v(mTag, "onChange(): uri=" + uri + ", userId=" + userId);
504 final String property = uri.getLastPathSegment();
505 if (property.equals(getServiceSettingsProperty())
506 || property.equals(Settings.Secure.USER_SETUP_COMPLETE)) {
507 synchronized (mLock) {
508 updateCachedServiceLocked(userId);
509 }
510 } else {
511 onSettingsChanged(userId, property);
512 }
513 }
514 }
515}