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