blob: 54bd9cb938da4c273ede66232374ded8b6bd3e26 [file] [log] [blame]
Mårten Kongstadeabc9e92015-12-15 16:40:23 +01001/*
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.om;
18
19import static android.app.AppGlobals.getPackageManager;
20import static android.content.Intent.ACTION_PACKAGE_ADDED;
21import static android.content.Intent.ACTION_PACKAGE_CHANGED;
22import static android.content.Intent.ACTION_PACKAGE_REMOVED;
Adam Lesinskiada8deb2017-05-12 13:50:42 -070023import static android.content.Intent.ACTION_USER_ADDED;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010024import static android.content.Intent.ACTION_USER_REMOVED;
25import static android.content.pm.PackageManager.SIGNATURE_MATCH;
Mårten Kongstadacfcdc72018-12-03 14:59:51 +010026import static android.os.Trace.TRACE_TAG_RRO;
27import static android.os.Trace.traceBegin;
28import static android.os.Trace.traceEnd;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010029
30import android.annotation.NonNull;
31import android.annotation.Nullable;
32import android.app.ActivityManager;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010033import android.app.IActivityManager;
34import android.content.BroadcastReceiver;
35import android.content.Context;
36import android.content.Intent;
37import android.content.IntentFilter;
38import android.content.om.IOverlayManager;
39import android.content.om.OverlayInfo;
40import android.content.pm.IPackageManager;
41import android.content.pm.PackageInfo;
42import android.content.pm.PackageManagerInternal;
43import android.content.pm.UserInfo;
44import android.net.Uri;
45import android.os.Binder;
46import android.os.Environment;
47import android.os.IBinder;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010048import android.os.RemoteException;
49import android.os.ResultReceiver;
50import android.os.ShellCallback;
Adam Lesinskia5ca6242017-03-01 15:45:12 -080051import android.os.SystemProperties;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010052import android.os.UserHandle;
Adam Lesinskiada8deb2017-05-12 13:50:42 -070053import android.os.UserManager;
Adam Lesinskia5ca6242017-03-01 15:45:12 -080054import android.text.TextUtils;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010055import android.util.ArrayMap;
Adam Lesinskia5ca6242017-03-01 15:45:12 -080056import android.util.ArraySet;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010057import android.util.AtomicFile;
58import android.util.Slog;
59import android.util.SparseArray;
60
61import com.android.server.FgThread;
62import com.android.server.IoThread;
63import com.android.server.LocalServices;
64import com.android.server.SystemService;
65import com.android.server.pm.Installer;
66import com.android.server.pm.UserManagerService;
67
Adam Lesinski7b031812018-02-22 13:32:53 -080068import libcore.util.EmptyArray;
69
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010070import org.xmlpull.v1.XmlPullParserException;
71
72import java.io.File;
73import java.io.FileDescriptor;
74import java.io.FileInputStream;
75import java.io.FileOutputStream;
76import java.io.IOException;
77import java.io.PrintWriter;
78import java.util.ArrayList;
Adam Lesinskid11c5512017-04-11 12:01:10 -070079import java.util.Arrays;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +010080import java.util.Collections;
81import java.util.HashMap;
82import java.util.List;
83import java.util.Map;
84import java.util.concurrent.atomic.AtomicBoolean;
85
86/**
87 * Service to manage asset overlays.
88 *
89 * <p>Asset overlays are additional resources that come from apks loaded
90 * alongside the system and app apks. This service, the OverlayManagerService
91 * (OMS), tracks which installed overlays to use and provides methods to change
92 * this. Changes propagate to running applications as part of the Activity
93 * lifecycle. This allows Activities to reread their resources at a well
94 * defined point.</p>
95 *
96 * <p>By itself, the OMS will not change what overlays should be active.
97 * Instead, it is only responsible for making sure that overlays *can* be used
98 * from a technical and security point of view and to activate overlays in
99 * response to external requests. The responsibility to toggle overlays on and
100 * off lies within components that implement different use-cases such as themes
101 * or dynamic customization.</p>
102 *
103 * <p>The OMS receives input from three sources:</p>
104 *
105 * <ul>
106 * <li>Callbacks from the SystemService class, specifically when the
107 * Android framework is booting and when the end user switches Android
108 * users.</li>
109 *
110 * <li>Intents from the PackageManagerService (PMS). Overlays are regular
111 * apks, and whenever a package is installed (or removed, or has a
112 * component enabled or disabled), the PMS broadcasts this as an intent.
113 * When the OMS receives one of these intents, it updates its internal
114 * representation of the available overlays and, if there was a visible
115 * change, triggers an asset refresh in the affected apps.</li>
116 *
117 * <li>External requests via the {@link IOverlayManager AIDL interface}.
118 * The interface allows clients to read information about the currently
119 * available overlays, change whether an overlay should be used or not, and
120 * change the relative order in which overlay packages are loaded.
121 * Read-access is granted if the request targets the same Android user as
122 * the caller runs as, or if the caller holds the
123 * INTERACT_ACROSS_USERS_FULL permission. Write-access is granted if the
124 * caller is granted read-access and additionaly holds the
125 * CHANGE_OVERLAY_PACKAGES permission.</li>
126 * </ul>
127 *
128 * <p>The AIDL interface works with String package names, int user IDs, and
129 * {@link OverlayInfo} objects. OverlayInfo instances are used to track a
130 * specific pair of target and overlay packages and include information such as
131 * the current state of the overlay. OverlayInfo objects are immutable.</p>
132 *
133 * <p>Internally, OverlayInfo objects are maintained by the
134 * OverlayManagerSettings class. The OMS and its helper classes are notified of
135 * changes to the settings by the OverlayManagerSettings.ChangeListener
136 * callback interface. The file /data/system/overlays.xml is used to persist
137 * the settings.</p>
138 *
139 * <p>Creation and deletion of idmap files are handled by the IdmapManager
140 * class.</p>
141 *
142 * <p>The following is an overview of OMS and its related classes. Note how box
143 * (2) does the heavy lifting, box (1) interacts with the Android framework,
144 * and box (3) replaces box (1) during unit testing.</p>
145 *
146 * <pre>
147 * Android framework
148 * | ^
149 * . . . | . . . . | . . . .
150 * . | | .
151 * . AIDL, broadcasts .
152 * . intents | .
153 * . | | . . . . . . . . . . . .
154 * . v | . .
155 * . OverlayManagerService . OverlayManagerTests .
156 * . \ . / .
157 * . (1) \ . / (3) .
158 * . . . . . . . . . . \ . . . / . . . . . . . . .
159 * . \ / .
160 * . (2) \ / .
161 * . OverlayManagerServiceImpl .
162 * . | | .
163 * . | | .
164 * . OverlayManagerSettings IdmapManager .
165 * . .
166 * . . . . . . . . . . . . . . . . . . . . . .
167 * </pre>
168 *
169 * <p>Finally, here is a list of keywords used in the OMS context.</p>
170 *
171 * <ul>
172 * <li><b>target [package]</b> -- A regular apk that may have its resource
173 * pool extended by zero or more overlay packages.</li>
174 *
175 * <li><b>overlay [package]</b> -- An apk that provides additional
176 * resources to another apk.</li>
177 *
178 * <li><b>OMS</b> -- The OverlayManagerService, i.e. this class.</li>
179 *
180 * <li><b>approved</b> -- An overlay is approved if the OMS has verified
181 * that it can be used technically speaking (its target package is
182 * installed, at least one resource name in both packages match, the
183 * idmap was created, etc) and that it is secure to do so. External
184 * clients can not change this state.</li>
185 *
186 * <li><b>not approved</b> -- The opposite of approved.</li>
187 *
188 * <li><b>enabled</b> -- An overlay currently in active use and thus part
189 * of resource lookups. This requires the overlay to be approved. Only
190 * external clients can change this state.</li>
191 *
192 * <li><b>disabled</b> -- The opposite of enabled.</li>
193 *
194 * <li><b>idmap</b> -- A mapping of resource IDs between target and overlay
195 * used during resource lookup. Also the name of the binary that creates
196 * the mapping.</li>
197 * </ul>
198 */
199public final class OverlayManagerService extends SystemService {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100200 static final String TAG = "OverlayManager";
201
202 static final boolean DEBUG = false;
203
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800204 /**
205 * The system property that specifies the default overlays to apply.
206 * This is a semicolon separated list of package names.
207 *
208 * Ex: com.android.vendor.overlay_one;com.android.vendor.overlay_two
209 */
210 private static final String DEFAULT_OVERLAYS_PROP = "ro.boot.vendor.overlay.theme";
211
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100212 private final Object mLock = new Object();
213
214 private final AtomicFile mSettingsFile;
215
216 private final PackageManagerHelper mPackageManager;
217
218 private final UserManagerService mUserManager;
219
220 private final OverlayManagerSettings mSettings;
221
222 private final OverlayManagerServiceImpl mImpl;
223
224 private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
225
226 public OverlayManagerService(@NonNull final Context context,
227 @NonNull final Installer installer) {
228 super(context);
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100229 try {
230 traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService");
231 mSettingsFile = new AtomicFile(
232 new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
233 mPackageManager = new PackageManagerHelper();
234 mUserManager = UserManagerService.getInstance();
Winsone0818bf2019-03-01 12:10:09 -0800235 IdmapManager im = new IdmapManager(installer, mPackageManager);
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100236 mSettings = new OverlayManagerSettings();
237 mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
238 getDefaultOverlayPackages(), new OverlayChangeListener());
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100239
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100240 final IntentFilter packageFilter = new IntentFilter();
241 packageFilter.addAction(ACTION_PACKAGE_ADDED);
242 packageFilter.addAction(ACTION_PACKAGE_CHANGED);
243 packageFilter.addAction(ACTION_PACKAGE_REMOVED);
244 packageFilter.addDataScheme("package");
245 getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
246 packageFilter, null, null);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100247
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100248 final IntentFilter userFilter = new IntentFilter();
249 userFilter.addAction(ACTION_USER_ADDED);
250 userFilter.addAction(ACTION_USER_REMOVED);
251 getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
252 userFilter, null, null);
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700253
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100254 restoreSettings();
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100255
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100256 initIfNeeded();
257 onSwitchUser(UserHandle.USER_SYSTEM);
Mårten Kongstadcd78e0f2018-05-23 10:41:35 +0200258
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100259 publishBinderService(Context.OVERLAY_SERVICE, mService);
260 publishLocalService(OverlayManagerService.class, this);
261 } finally {
262 traceEnd(TRACE_TAG_RRO);
263 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100264 }
265
266 @Override
267 public void onStart() {
268 // Intentionally left empty.
269 }
270
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700271 private void initIfNeeded() {
272 final UserManager um = getContext().getSystemService(UserManager.class);
273 final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
274 synchronized (mLock) {
275 final int userCount = users.size();
276 for (int i = 0; i < userCount; i++) {
277 final UserInfo userInfo = users.get(i);
278 if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
279 // Initialize any users that can't be switched to, as there state would
280 // never be setup in onSwitchUser(). We will switch to the system user right
281 // after this, and its state will be setup there.
282 final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
283 updateOverlayPaths(users.get(i).id, targets);
284 }
285 }
286 }
287 }
288
Fyodor Kupolov94ab1a62017-03-06 14:28:20 -0800289 @Override
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100290 public void onSwitchUser(final int newUserId) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100291 try {
292 traceBegin(TRACE_TAG_RRO, "OMS#onSwitchUser " + newUserId);
293 // ensure overlays in the settings are up-to-date, and propagate
294 // any asset changes to the rest of the system
295 synchronized (mLock) {
296 final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
297 updateAssets(newUserId, targets);
298 }
299 schedulePersistSettings();
300 } finally {
301 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100302 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100303 }
304
Adam Lesinski7b031812018-02-22 13:32:53 -0800305 private static String[] getDefaultOverlayPackages() {
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800306 final String str = SystemProperties.get(DEFAULT_OVERLAYS_PROP);
307 if (TextUtils.isEmpty(str)) {
Adam Lesinski7b031812018-02-22 13:32:53 -0800308 return EmptyArray.STRING;
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800309 }
310
311 final ArraySet<String> defaultPackages = new ArraySet<>();
312 for (String packageName : str.split(";")) {
313 if (!TextUtils.isEmpty(packageName)) {
314 defaultPackages.add(packageName);
315 }
316 }
Adam Lesinski7b031812018-02-22 13:32:53 -0800317 return defaultPackages.toArray(new String[defaultPackages.size()]);
Adam Lesinskia5ca6242017-03-01 15:45:12 -0800318 }
319
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100320 private final class PackageReceiver extends BroadcastReceiver {
321 @Override
322 public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
Youngha Park14d8d682018-08-02 15:43:38 +0900323 final String action = intent.getAction();
324 if (action == null) {
325 Slog.e(TAG, "Cannot handle package broadcast with null action");
326 return;
327 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100328 final Uri data = intent.getData();
329 if (data == null) {
330 Slog.e(TAG, "Cannot handle package broadcast with null data");
331 return;
332 }
333 final String packageName = data.getSchemeSpecificPart();
334
335 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
336
337 final int[] userIds;
338 final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
339 if (extraUid == UserHandle.USER_NULL) {
340 userIds = mUserManager.getUserIds();
341 } else {
342 userIds = new int[] { UserHandle.getUserId(extraUid) };
343 }
344
Youngha Park14d8d682018-08-02 15:43:38 +0900345 switch (action) {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100346 case ACTION_PACKAGE_ADDED:
347 if (replacing) {
348 onPackageUpgraded(packageName, userIds);
349 } else {
350 onPackageAdded(packageName, userIds);
351 }
352 break;
353 case ACTION_PACKAGE_CHANGED:
354 onPackageChanged(packageName, userIds);
355 break;
356 case ACTION_PACKAGE_REMOVED:
357 if (replacing) {
358 onPackageUpgrading(packageName, userIds);
359 } else {
360 onPackageRemoved(packageName, userIds);
361 }
362 break;
363 default:
364 // do nothing
365 break;
366 }
367 }
368
369 private void onPackageAdded(@NonNull final String packageName,
370 @NonNull final int[] userIds) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100371 try {
372 traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
373 for (final int userId : userIds) {
374 synchronized (mLock) {
375 final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
376 false);
Mårten Kongstadad32ba92019-03-26 16:05:23 +0100377 if (pi != null && !pi.applicationInfo.isInstantApp()) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100378 mPackageManager.cachePackageInfo(packageName, userId, pi);
379 if (pi.isOverlayPackage()) {
380 mImpl.onOverlayPackageAdded(packageName, userId);
381 } else {
382 mImpl.onTargetPackageAdded(packageName, userId);
383 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100384 }
385 }
386 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100387 } finally {
388 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100389 }
390 }
391
392 private void onPackageChanged(@NonNull final String packageName,
393 @NonNull final int[] userIds) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100394 try {
395 traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
396 for (int userId : userIds) {
397 synchronized (mLock) {
398 final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
399 false);
Mårten Kongstadad32ba92019-03-26 16:05:23 +0100400 if (pi != null && pi.applicationInfo.isInstantApp()) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100401 mPackageManager.cachePackageInfo(packageName, userId, pi);
402 if (pi.isOverlayPackage()) {
403 mImpl.onOverlayPackageChanged(packageName, userId);
404 } else {
405 mImpl.onTargetPackageChanged(packageName, userId);
406 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100407 }
408 }
409 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100410 } finally {
411 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100412 }
413 }
414
415 private void onPackageUpgrading(@NonNull final String packageName,
416 @NonNull final int[] userIds) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100417 try {
418 traceBegin(TRACE_TAG_RRO, "OMS#onPackageUpgrading " + packageName);
419 for (int userId : userIds) {
420 synchronized (mLock) {
421 mPackageManager.forgetPackageInfo(packageName, userId);
422 final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
423 if (oi != null) {
424 mImpl.onOverlayPackageUpgrading(packageName, userId);
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100425 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100426 }
427 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100428 } finally {
429 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100430 }
431 }
432
433 private void onPackageUpgraded(@NonNull final String packageName,
434 @NonNull final int[] userIds) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100435 try {
436 traceBegin(TRACE_TAG_RRO, "OMS#onPackageUpgraded " + packageName);
437 for (int userId : userIds) {
438 synchronized (mLock) {
439 final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
440 false);
Mårten Kongstadad32ba92019-03-26 16:05:23 +0100441 if (pi != null && !pi.applicationInfo.isInstantApp()) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100442 mPackageManager.cachePackageInfo(packageName, userId, pi);
443 if (pi.isOverlayPackage()) {
444 mImpl.onOverlayPackageUpgraded(packageName, userId);
445 } else {
446 mImpl.onTargetPackageUpgraded(packageName, userId);
447 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100448 }
449 }
450 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100451 } finally {
452 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100453 }
454 }
455
456 private void onPackageRemoved(@NonNull final String packageName,
457 @NonNull final int[] userIds) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100458 try {
459 traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
460 for (int userId : userIds) {
461 synchronized (mLock) {
462 mPackageManager.forgetPackageInfo(packageName, userId);
463 final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
464 if (oi != null) {
465 mImpl.onOverlayPackageRemoved(packageName, userId);
466 } else {
467 mImpl.onTargetPackageRemoved(packageName, userId);
468 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100469 }
470 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100471 } finally {
472 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100473 }
474 }
475 }
476
477 private final class UserReceiver extends BroadcastReceiver {
478 @Override
479 public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700480 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100481 switch (intent.getAction()) {
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700482 case ACTION_USER_ADDED:
483 if (userId != UserHandle.USER_NULL) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100484 try {
485 traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED");
486 final ArrayList<String> targets;
487 synchronized (mLock) {
488 targets = mImpl.updateOverlaysForUser(userId);
489 }
490 updateOverlayPaths(userId, targets);
491 } finally {
492 traceEnd(TRACE_TAG_RRO);
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700493 }
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700494 }
495 break;
496
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100497 case ACTION_USER_REMOVED:
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100498 if (userId != UserHandle.USER_NULL) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100499 try {
500 traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_REMOVED");
501 synchronized (mLock) {
502 mImpl.onUserRemoved(userId);
503 mPackageManager.forgetAllPackageInfos(userId);
504 }
505 } finally {
506 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100507 }
508 }
509 break;
510 default:
511 // do nothing
512 break;
513 }
514 }
515 }
516
517 private final IBinder mService = new IOverlayManager.Stub() {
518 @Override
Adam Lesinskic745f422017-04-05 16:31:30 -0700519 public Map<String, List<OverlayInfo>> getAllOverlays(int userId) throws RemoteException {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100520 try {
521 traceBegin(TRACE_TAG_RRO, "OMS#getAllOverlays " + userId);
522 userId = handleIncomingUser(userId, "getAllOverlays");
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100523
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100524 synchronized (mLock) {
525 return mImpl.getOverlaysForUser(userId);
526 }
527 } finally {
528 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100529 }
530 }
531
532 @Override
533 public List<OverlayInfo> getOverlayInfosForTarget(@Nullable final String targetPackageName,
534 int userId) throws RemoteException {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100535 try {
536 traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfosForTarget " + targetPackageName);
537 userId = handleIncomingUser(userId, "getOverlayInfosForTarget");
538 if (targetPackageName == null) {
539 return Collections.emptyList();
540 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100541
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100542 synchronized (mLock) {
543 return mImpl.getOverlayInfosForTarget(targetPackageName, userId);
544 }
545 } finally {
546 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100547 }
548 }
549
550 @Override
551 public OverlayInfo getOverlayInfo(@Nullable final String packageName,
552 int userId) throws RemoteException {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100553 try {
554 traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName);
555 userId = handleIncomingUser(userId, "getOverlayInfo");
556 if (packageName == null) {
557 return null;
558 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100559
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100560 synchronized (mLock) {
561 return mImpl.getOverlayInfo(packageName, userId);
562 }
563 } finally {
564 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100565 }
566 }
567
568 @Override
569 public boolean setEnabled(@Nullable final String packageName, final boolean enable,
570 int userId) throws RemoteException {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100571 try {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100572 traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable);
573 enforceChangeOverlayPackagesPermission("setEnabled");
574 userId = handleIncomingUser(userId, "setEnabled");
575 if (packageName == null) {
576 return false;
577 }
578
579 final long ident = Binder.clearCallingIdentity();
580 try {
581 synchronized (mLock) {
582 return mImpl.setEnabled(packageName, enable, userId);
583 }
584 } finally {
585 Binder.restoreCallingIdentity(ident);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100586 }
587 } finally {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100588 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100589 }
590 }
591
592 @Override
Jason Monk929ed8d2017-03-07 16:01:20 -0500593 public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable,
594 int userId) throws RemoteException {
Jason Monk929ed8d2017-03-07 16:01:20 -0500595 try {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100596 traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable);
597 enforceChangeOverlayPackagesPermission("setEnabledExclusive");
598 userId = handleIncomingUser(userId, "setEnabledExclusive");
599 if (packageName == null || !enable) {
600 return false;
601 }
602
603 final long ident = Binder.clearCallingIdentity();
604 try {
605 synchronized (mLock) {
606 return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
607 userId);
608 }
609 } finally {
610 Binder.restoreCallingIdentity(ident);
Adrian Roosc84df772018-01-19 21:20:22 +0100611 }
612 } finally {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100613 traceEnd(TRACE_TAG_RRO);
Adrian Roosc84df772018-01-19 21:20:22 +0100614 }
615 }
616
617 @Override
618 public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId)
619 throws RemoteException {
Adrian Roosc84df772018-01-19 21:20:22 +0100620 try {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100621 traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName);
622 enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory");
623 userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory");
624 if (packageName == null) {
625 return false;
626 }
627
628 final long ident = Binder.clearCallingIdentity();
629 try {
630 synchronized (mLock) {
631 return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
632 userId);
633 }
634 } finally {
635 Binder.restoreCallingIdentity(ident);
Jason Monk929ed8d2017-03-07 16:01:20 -0500636 }
637 } finally {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100638 traceEnd(TRACE_TAG_RRO);
Jason Monk929ed8d2017-03-07 16:01:20 -0500639 }
640 }
641
642 @Override
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100643 public boolean setPriority(@Nullable final String packageName,
644 @Nullable final String parentPackageName, int userId) throws RemoteException {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100645 try {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100646 traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " "
647 + parentPackageName);
648 enforceChangeOverlayPackagesPermission("setPriority");
649 userId = handleIncomingUser(userId, "setPriority");
650 if (packageName == null || parentPackageName == null) {
651 return false;
652 }
653
654 final long ident = Binder.clearCallingIdentity();
655 try {
656 synchronized (mLock) {
657 return mImpl.setPriority(packageName, parentPackageName, userId);
658 }
659 } finally {
660 Binder.restoreCallingIdentity(ident);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100661 }
662 } finally {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100663 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100664 }
665 }
666
667 @Override
668 public boolean setHighestPriority(@Nullable final String packageName, int userId)
669 throws RemoteException {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100670 try {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100671 traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName);
672 enforceChangeOverlayPackagesPermission("setHighestPriority");
673 userId = handleIncomingUser(userId, "setHighestPriority");
674 if (packageName == null) {
675 return false;
676 }
677
678 final long ident = Binder.clearCallingIdentity();
679 try {
680 synchronized (mLock) {
681 return mImpl.setHighestPriority(packageName, userId);
682 }
683 } finally {
684 Binder.restoreCallingIdentity(ident);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100685 }
686 } finally {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100687 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100688 }
689 }
690
691 @Override
692 public boolean setLowestPriority(@Nullable final String packageName, int userId)
693 throws RemoteException {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100694 try {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100695 traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName);
696 enforceChangeOverlayPackagesPermission("setLowestPriority");
697 userId = handleIncomingUser(userId, "setLowestPriority");
698 if (packageName == null) {
699 return false;
700 }
701
702 final long ident = Binder.clearCallingIdentity();
703 try {
704 synchronized (mLock) {
705 return mImpl.setLowestPriority(packageName, userId);
706 }
707 } finally {
708 Binder.restoreCallingIdentity(ident);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100709 }
710 } finally {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100711 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100712 }
713 }
714
715 @Override
716 public void onShellCommand(@NonNull final FileDescriptor in,
717 @NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
718 @NonNull final String[] args, @NonNull final ShellCallback callback,
719 @NonNull final ResultReceiver resultReceiver) {
720 (new OverlayManagerShellCommand(this)).exec(
721 this, in, out, err, args, callback, resultReceiver);
722 }
723
724 @Override
725 protected void dump(@NonNull final FileDescriptor fd, @NonNull final PrintWriter pw,
726 @NonNull final String[] argv) {
727 enforceDumpPermission("dump");
728
729 final boolean verbose = argv.length > 0 && "--verbose".equals(argv[0]);
730
731 synchronized (mLock) {
732 mImpl.onDump(pw);
733 mPackageManager.dump(pw, verbose);
734 }
735 }
736
737 /**
738 * Ensure that the caller has permission to interact with the given userId.
739 * If the calling user is not the same as the provided user, the caller needs
740 * to hold the INTERACT_ACROSS_USERS_FULL permission (or be system uid or
741 * root).
742 *
743 * @param userId the user to interact with
744 * @param message message for any SecurityException
745 */
746 private int handleIncomingUser(final int userId, @NonNull final String message) {
747 return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
748 Binder.getCallingUid(), userId, false, true, message, null);
749 }
750
751 /**
752 * Enforce that the caller holds the CHANGE_OVERLAY_PACKAGES permission (or is
753 * system or root).
754 *
755 * @param message used as message if SecurityException is thrown
756 * @throws SecurityException if the permission check fails
757 */
758 private void enforceChangeOverlayPackagesPermission(@NonNull final String message) {
Winson Chungddaf3512019-03-18 13:02:44 -0700759 getContext().enforceCallingOrSelfPermission(
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100760 android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, message);
761 }
762
763 /**
764 * Enforce that the caller holds the DUMP permission (or is system or root).
765 *
766 * @param message used as message if SecurityException is thrown
767 * @throws SecurityException if the permission check fails
768 */
769 private void enforceDumpPermission(@NonNull final String message) {
Winson Chungddaf3512019-03-18 13:02:44 -0700770 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100771 }
772 };
773
Adam Lesinskic745f422017-04-05 16:31:30 -0700774 private final class OverlayChangeListener
775 implements OverlayManagerServiceImpl.OverlayChangeListener {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100776 @Override
Mårten Kongstad497945c2018-04-27 09:56:13 +0200777 public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100778 schedulePersistSettings();
Adam Lesinskic745f422017-04-05 16:31:30 -0700779 FgThread.getHandler().post(() -> {
Mårten Kongstad497945c2018-04-27 09:56:13 +0200780 updateAssets(userId, targetPackageName);
781
782 final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
783 Uri.fromParts("package", targetPackageName, null));
784 intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
785
786 if (DEBUG) {
787 Slog.d(TAG, "send broadcast " + intent);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100788 }
Adam Lesinskic745f422017-04-05 16:31:30 -0700789
Mårten Kongstad497945c2018-04-27 09:56:13 +0200790 try {
791 ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
792 null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
793 userId);
794 } catch (RemoteException e) {
795 // Intentionally left empty.
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100796 }
Adam Lesinskic745f422017-04-05 16:31:30 -0700797 });
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100798 }
799 }
800
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700801 /**
802 * Updates the target packages' set of enabled overlays in PackageManager.
803 */
804 private void updateOverlayPaths(int userId, List<String> targetPackageNames) {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100805 try {
806 traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
807 if (DEBUG) {
808 Slog.d(TAG, "Updating overlay assets");
809 }
810 final PackageManagerInternal pm =
811 LocalServices.getService(PackageManagerInternal.class);
812 final boolean updateFrameworkRes = targetPackageNames.contains("android");
813 if (updateFrameworkRes) {
814 targetPackageNames = pm.getTargetPackageNames(userId);
815 }
Mårten Kongstad2e0d0f32016-06-02 09:35:31 +0200816
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100817 final Map<String, List<String>> pendingChanges =
818 new ArrayMap<>(targetPackageNames.size());
819 synchronized (mLock) {
820 final List<String> frameworkOverlays =
821 mImpl.getEnabledOverlayPackageNames("android", userId);
822 final int n = targetPackageNames.size();
823 for (int i = 0; i < n; i++) {
824 final String targetPackageName = targetPackageNames.get(i);
825 List<String> list = new ArrayList<>();
826 if (!"android".equals(targetPackageName)) {
827 list.addAll(frameworkOverlays);
828 }
829 list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
830 pendingChanges.put(targetPackageName, list);
831 }
832 }
833
Mårten Kongstad38988342018-12-04 10:28:01 +0100834 final int n = targetPackageNames.size();
835 for (int i = 0; i < n; i++) {
Mårten Kongstad2e0d0f32016-06-02 09:35:31 +0200836 final String targetPackageName = targetPackageNames.get(i);
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100837 if (DEBUG) {
838 Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
839 + TextUtils.join(",", pendingChanges.get(targetPackageName))
840 + "] userId=" + userId);
Todd Kennedy560830c2017-06-16 13:55:13 -0700841 }
Mårten Kongstad2e0d0f32016-06-02 09:35:31 +0200842
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100843 if (!pm.setEnabledOverlayPackages(
844 userId, targetPackageName, pendingChanges.get(targetPackageName))) {
845 Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
846 targetPackageName, userId));
847 }
Adam Lesinskic745f422017-04-05 16:31:30 -0700848 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100849 } finally {
850 traceEnd(TRACE_TAG_RRO);
Mårten Kongstad2e0d0f32016-06-02 09:35:31 +0200851 }
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700852 }
Mårten Kongstad2e0d0f32016-06-02 09:35:31 +0200853
Adam Lesinskiada8deb2017-05-12 13:50:42 -0700854 private void updateAssets(final int userId, final String targetPackageName) {
855 updateAssets(userId, Collections.singletonList(targetPackageName));
856 }
857
858 private void updateAssets(final int userId, List<String> targetPackageNames) {
859 updateOverlayPaths(userId, targetPackageNames);
Mårten Kongstad2e0d0f32016-06-02 09:35:31 +0200860 final IActivityManager am = ActivityManager.getService();
861 try {
862 am.scheduleApplicationInfoChanged(targetPackageNames, userId);
863 } catch (RemoteException e) {
864 // Intentionally left empty.
865 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100866 }
867
868 private void schedulePersistSettings() {
869 if (mPersistSettingsScheduled.getAndSet(true)) {
870 return;
871 }
Adam Lesinskic745f422017-04-05 16:31:30 -0700872 IoThread.getHandler().post(() -> {
873 mPersistSettingsScheduled.set(false);
874 if (DEBUG) {
875 Slog.d(TAG, "Writing overlay settings");
876 }
877 synchronized (mLock) {
878 FileOutputStream stream = null;
879 try {
880 stream = mSettingsFile.startWrite();
881 mSettings.persist(stream);
882 mSettingsFile.finishWrite(stream);
883 } catch (IOException | XmlPullParserException e) {
884 mSettingsFile.failWrite(stream);
885 Slog.e(TAG, "failed to persist overlay state", e);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100886 }
887 }
888 });
889 }
890
891 private void restoreSettings() {
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100892 try {
893 traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
894 synchronized (mLock) {
895 if (!mSettingsFile.getBaseFile().exists()) {
896 return;
Adam Lesinskid11c5512017-04-11 12:01:10 -0700897 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100898 try (FileInputStream stream = mSettingsFile.openRead()) {
899 mSettings.restore(stream);
Adam Lesinskid11c5512017-04-11 12:01:10 -0700900
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100901 // We might have data for dying users if the device was
902 // restarted before we received USER_REMOVED. Remove data for
903 // users that will not exist after the system is ready.
904
905 final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
906 final int[] liveUserIds = new int[liveUsers.size()];
907 for (int i = 0; i < liveUsers.size(); i++) {
908 liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
Adam Lesinskid11c5512017-04-11 12:01:10 -0700909 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100910 Arrays.sort(liveUserIds);
911
912 for (int userId : mSettings.getUsers()) {
913 if (Arrays.binarySearch(liveUserIds, userId) < 0) {
914 mSettings.removeUser(userId);
915 }
916 }
917 } catch (IOException | XmlPullParserException e) {
918 Slog.e(TAG, "failed to restore overlay state", e);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100919 }
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100920 }
Mårten Kongstadacfcdc72018-12-03 14:59:51 +0100921 } finally {
922 traceEnd(TRACE_TAG_RRO);
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100923 }
924 }
925
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100926 private static final class PackageManagerHelper implements
927 OverlayManagerServiceImpl.PackageManagerHelper {
928
929 private final IPackageManager mPackageManager;
930 private final PackageManagerInternal mPackageManagerInternal;
931
932 // Use a cache for performance and for consistency within OMS: because
933 // additional PACKAGE_* intents may be delivered while we process an
934 // intent, querying the PackageManagerService for the actual current
935 // state may lead to contradictions within OMS. Better then to lag
936 // behind until all pending intents have been processed.
937 private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
938
939 PackageManagerHelper() {
940 mPackageManager = getPackageManager();
941 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
942 }
943
944 public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
945 final boolean useCache) {
946 if (useCache) {
947 final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId);
948 if (cachedPi != null) {
949 return cachedPi;
950 }
951 }
952 try {
953 final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId);
954 if (useCache && pi != null) {
955 cachePackageInfo(packageName, userId, pi);
956 }
957 return pi;
958 } catch (RemoteException e) {
959 // Intentionally left empty.
960 }
961 return null;
962 }
963
964 @Override
965 public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
966 return getPackageInfo(packageName, userId, true);
967 }
968
969 @Override
970 public boolean signaturesMatching(@NonNull final String packageName1,
971 @NonNull final String packageName2, final int userId) {
972 // The package manager does not support different versions of packages
973 // to be installed for different users: ignore userId for now.
974 try {
Adam Lesinskic745f422017-04-05 16:31:30 -0700975 return mPackageManager.checkSignatures(
976 packageName1, packageName2) == SIGNATURE_MATCH;
Mårten Kongstadeabc9e92015-12-15 16:40:23 +0100977 } catch (RemoteException e) {
978 // Intentionally left blank
979 }
980 return false;
981 }
982
983 @Override
984 public List<PackageInfo> getOverlayPackages(final int userId) {
985 return mPackageManagerInternal.getOverlayPackages(userId);
986 }
987
988 public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
989 final int userId) {
990 final HashMap<String, PackageInfo> map = mCache.get(userId);
991 return map == null ? null : map.get(packageName);
992 }
993
994 public void cachePackageInfo(@NonNull final String packageName, final int userId,
995 @NonNull final PackageInfo pi) {
996 HashMap<String, PackageInfo> map = mCache.get(userId);
997 if (map == null) {
998 map = new HashMap<>();
999 mCache.put(userId, map);
1000 }
1001 map.put(packageName, pi);
1002 }
1003
1004 public void forgetPackageInfo(@NonNull final String packageName, final int userId) {
1005 final HashMap<String, PackageInfo> map = mCache.get(userId);
1006 if (map == null) {
1007 return;
1008 }
1009 map.remove(packageName);
1010 if (map.isEmpty()) {
1011 mCache.delete(userId);
1012 }
1013 }
1014
1015 public void forgetAllPackageInfos(final int userId) {
1016 mCache.delete(userId);
1017 }
1018
1019 private static final String TAB1 = " ";
1020 private static final String TAB2 = TAB1 + TAB1;
1021
1022 public void dump(@NonNull final PrintWriter pw, final boolean verbose) {
1023 pw.println("PackageInfo cache");
1024
1025 if (!verbose) {
1026 int count = 0;
Mårten Kongstad38988342018-12-04 10:28:01 +01001027 final int n = mCache.size();
1028 for (int i = 0; i < n; i++) {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +01001029 final int userId = mCache.keyAt(i);
1030 count += mCache.get(userId).size();
1031 }
1032 pw.println(TAB1 + count + " package(s)");
1033 return;
1034 }
1035
1036 if (mCache.size() == 0) {
1037 pw.println(TAB1 + "<empty>");
1038 return;
1039 }
1040
Mårten Kongstad38988342018-12-04 10:28:01 +01001041 final int n = mCache.size();
1042 for (int i = 0; i < n; i++) {
Mårten Kongstadeabc9e92015-12-15 16:40:23 +01001043 final int userId = mCache.keyAt(i);
1044 pw.println(TAB1 + "User " + userId);
1045 final HashMap<String, PackageInfo> map = mCache.get(userId);
1046 for (Map.Entry<String, PackageInfo> entry : map.entrySet()) {
1047 pw.println(TAB2 + entry.getKey() + ": " + entry.getValue());
1048 }
1049 }
1050 }
1051 }
1052}