• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.om;
18 
19 import static android.app.AppGlobals.getPackageManager;
20 import static android.content.Intent.ACTION_PACKAGE_ADDED;
21 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
22 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
23 import static android.content.Intent.ACTION_USER_ADDED;
24 import static android.content.Intent.ACTION_USER_REMOVED;
25 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
26 import static android.os.Trace.TRACE_TAG_RRO;
27 import static android.os.Trace.traceBegin;
28 import static android.os.Trace.traceEnd;
29 
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.app.ActivityManager;
33 import android.app.IActivityManager;
34 import android.content.BroadcastReceiver;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.content.om.IOverlayManager;
39 import android.content.om.OverlayInfo;
40 import android.content.pm.IPackageManager;
41 import android.content.pm.PackageInfo;
42 import android.content.pm.PackageManagerInternal;
43 import android.content.pm.UserInfo;
44 import android.net.Uri;
45 import android.os.Binder;
46 import android.os.Environment;
47 import android.os.IBinder;
48 import android.os.RemoteException;
49 import android.os.ResultReceiver;
50 import android.os.ShellCallback;
51 import android.os.SystemProperties;
52 import android.os.UserHandle;
53 import android.os.UserManager;
54 import android.text.TextUtils;
55 import android.util.ArrayMap;
56 import android.util.ArraySet;
57 import android.util.AtomicFile;
58 import android.util.Slog;
59 import android.util.SparseArray;
60 
61 import com.android.server.FgThread;
62 import com.android.server.IoThread;
63 import com.android.server.LocalServices;
64 import com.android.server.SystemService;
65 import com.android.server.pm.Installer;
66 import com.android.server.pm.UserManagerService;
67 
68 import libcore.util.EmptyArray;
69 
70 import org.xmlpull.v1.XmlPullParserException;
71 
72 import java.io.File;
73 import java.io.FileDescriptor;
74 import java.io.FileInputStream;
75 import java.io.FileOutputStream;
76 import java.io.IOException;
77 import java.io.PrintWriter;
78 import java.util.ArrayList;
79 import java.util.Arrays;
80 import java.util.Collections;
81 import java.util.HashMap;
82 import java.util.List;
83 import java.util.Map;
84 import 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>To test the OMS, execute:
170  * <code>
171  * atest FrameworksServicesTests:com.android.server.om  # internal tests
172  * atest OverlayDeviceTests OverlayHostTests            # public API tests
173  * </code>
174  * </p>
175  *
176  * <p>Finally, here is a list of keywords used in the OMS context.</p>
177  *
178  * <ul>
179  *     <li><b>target [package]</b> -- A regular apk that may have its resource
180  *     pool extended  by zero or more overlay packages.</li>
181  *
182  *     <li><b>overlay [package]</b> -- An apk that provides additional
183  *     resources to another apk.</li>
184  *
185  *     <li><b>OMS</b> -- The OverlayManagerService, i.e. this class.</li>
186  *
187  *     <li><b>approved</b> -- An overlay is approved if the OMS has verified
188  *     that it can be used technically speaking (its target package is
189  *     installed, at least one resource name in both packages match, the
190  *     idmap was created, etc) and that it is secure to do so. External
191  *     clients can not change this state.</li>
192  *
193  *     <li><b>not approved</b> -- The opposite of approved.</li>
194  *
195  *     <li><b>enabled</b> -- An overlay currently in active use and thus part
196  *     of resource lookups. This requires the overlay to be approved. Only
197  *     external clients can change this state.</li>
198  *
199  *     <li><b>disabled</b> -- The opposite of enabled.</li>
200  *
201  *     <li><b>idmap</b> -- A mapping of resource IDs between target and overlay
202  *     used during resource lookup. Also the name of the binary that creates
203  *     the mapping.</li>
204  * </ul>
205  */
206 public final class OverlayManagerService extends SystemService {
207     static final String TAG = "OverlayManager";
208 
209     static final boolean DEBUG = false;
210 
211     /**
212      * The system property that specifies the default overlays to apply.
213      * This is a semicolon separated list of package names.
214      *
215      * Ex: com.android.vendor.overlay_one;com.android.vendor.overlay_two
216      */
217     private static final String DEFAULT_OVERLAYS_PROP = "ro.boot.vendor.overlay.theme";
218 
219     private final Object mLock = new Object();
220 
221     private final AtomicFile mSettingsFile;
222 
223     private final PackageManagerHelper mPackageManager;
224 
225     private final UserManagerService mUserManager;
226 
227     private final OverlayManagerSettings mSettings;
228 
229     private final OverlayManagerServiceImpl mImpl;
230 
231     private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
232 
OverlayManagerService(@onNull final Context context, @NonNull final Installer installer)233     public OverlayManagerService(@NonNull final Context context,
234             @NonNull final Installer installer) {
235         super(context);
236         try {
237             traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService");
238             mSettingsFile = new AtomicFile(
239                     new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
240             mPackageManager = new PackageManagerHelper();
241             mUserManager = UserManagerService.getInstance();
242             IdmapManager im = new IdmapManager(installer, mPackageManager);
243             mSettings = new OverlayManagerSettings();
244             mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
245                     getDefaultOverlayPackages(), new OverlayChangeListener());
246 
247             final IntentFilter packageFilter = new IntentFilter();
248             packageFilter.addAction(ACTION_PACKAGE_ADDED);
249             packageFilter.addAction(ACTION_PACKAGE_CHANGED);
250             packageFilter.addAction(ACTION_PACKAGE_REMOVED);
251             packageFilter.addDataScheme("package");
252             getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
253                     packageFilter, null, null);
254 
255             final IntentFilter userFilter = new IntentFilter();
256             userFilter.addAction(ACTION_USER_ADDED);
257             userFilter.addAction(ACTION_USER_REMOVED);
258             getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
259                     userFilter, null, null);
260 
261             restoreSettings();
262 
263             initIfNeeded();
264             onSwitchUser(UserHandle.USER_SYSTEM);
265 
266             publishBinderService(Context.OVERLAY_SERVICE, mService);
267             publishLocalService(OverlayManagerService.class, this);
268         } finally {
269             traceEnd(TRACE_TAG_RRO);
270         }
271     }
272 
273     @Override
onStart()274     public void onStart() {
275         // Intentionally left empty.
276     }
277 
initIfNeeded()278     private void initIfNeeded() {
279         final UserManager um = getContext().getSystemService(UserManager.class);
280         final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
281         synchronized (mLock) {
282             final int userCount = users.size();
283             for (int i = 0; i < userCount; i++) {
284                 final UserInfo userInfo = users.get(i);
285                 if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
286                     // Initialize any users that can't be switched to, as there state would
287                     // never be setup in onSwitchUser(). We will switch to the system user right
288                     // after this, and its state will be setup there.
289                     final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
290                     updateOverlayPaths(users.get(i).id, targets);
291                 }
292             }
293         }
294     }
295 
296     @Override
onSwitchUser(final int newUserId)297     public void onSwitchUser(final int newUserId) {
298         try {
299             traceBegin(TRACE_TAG_RRO, "OMS#onSwitchUser " + newUserId);
300             // ensure overlays in the settings are up-to-date, and propagate
301             // any asset changes to the rest of the system
302             synchronized (mLock) {
303                 final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
304                 updateAssets(newUserId, targets);
305             }
306             schedulePersistSettings();
307         } finally {
308             traceEnd(TRACE_TAG_RRO);
309         }
310     }
311 
getDefaultOverlayPackages()312     private static String[] getDefaultOverlayPackages() {
313         final String str = SystemProperties.get(DEFAULT_OVERLAYS_PROP);
314         if (TextUtils.isEmpty(str)) {
315             return EmptyArray.STRING;
316         }
317 
318         final ArraySet<String> defaultPackages = new ArraySet<>();
319         for (String packageName : str.split(";")) {
320             if (!TextUtils.isEmpty(packageName)) {
321                 defaultPackages.add(packageName);
322             }
323         }
324         return defaultPackages.toArray(new String[defaultPackages.size()]);
325     }
326 
327     private final class PackageReceiver extends BroadcastReceiver {
328         @Override
onReceive(@onNull final Context context, @NonNull final Intent intent)329         public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
330             final String action = intent.getAction();
331             if (action == null) {
332                 Slog.e(TAG, "Cannot handle package broadcast with null action");
333                 return;
334             }
335             final Uri data = intent.getData();
336             if (data == null) {
337                 Slog.e(TAG, "Cannot handle package broadcast with null data");
338                 return;
339             }
340             final String packageName = data.getSchemeSpecificPart();
341 
342             final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
343 
344             final int[] userIds;
345             final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
346             if (extraUid == UserHandle.USER_NULL) {
347                 userIds = mUserManager.getUserIds();
348             } else {
349                 userIds = new int[] { UserHandle.getUserId(extraUid) };
350             }
351 
352             switch (action) {
353                 case ACTION_PACKAGE_ADDED:
354                     if (replacing) {
355                         onPackageReplaced(packageName, userIds);
356                     } else {
357                         onPackageAdded(packageName, userIds);
358                     }
359                     break;
360                 case ACTION_PACKAGE_CHANGED:
361                     onPackageChanged(packageName, userIds);
362                     break;
363                 case ACTION_PACKAGE_REMOVED:
364                     if (replacing) {
365                         onPackageReplacing(packageName, userIds);
366                     } else {
367                         onPackageRemoved(packageName, userIds);
368                     }
369                     break;
370                 default:
371                     // do nothing
372                     break;
373             }
374         }
375 
onPackageAdded(@onNull final String packageName, @NonNull final int[] userIds)376         private void onPackageAdded(@NonNull final String packageName,
377                 @NonNull final int[] userIds) {
378             try {
379                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
380                 for (final int userId : userIds) {
381                     synchronized (mLock) {
382                         final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
383                                 false);
384                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
385                             mPackageManager.cachePackageInfo(packageName, userId, pi);
386                             if (pi.isOverlayPackage()) {
387                                 mImpl.onOverlayPackageAdded(packageName, userId);
388                             } else {
389                                 mImpl.onTargetPackageAdded(packageName, userId);
390                             }
391                         }
392                     }
393                 }
394             } finally {
395                 traceEnd(TRACE_TAG_RRO);
396             }
397         }
398 
onPackageChanged(@onNull final String packageName, @NonNull final int[] userIds)399         private void onPackageChanged(@NonNull final String packageName,
400                 @NonNull final int[] userIds) {
401             try {
402                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
403                 for (int userId : userIds) {
404                     synchronized (mLock) {
405                         final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
406                                 false);
407                         if (pi != null && pi.applicationInfo.isInstantApp()) {
408                             mPackageManager.cachePackageInfo(packageName, userId, pi);
409                             if (pi.isOverlayPackage()) {
410                                 mImpl.onOverlayPackageChanged(packageName, userId);
411                             }  else {
412                                 mImpl.onTargetPackageChanged(packageName, userId);
413                             }
414                         }
415                     }
416                 }
417             } finally {
418                 traceEnd(TRACE_TAG_RRO);
419             }
420         }
421 
onPackageReplacing(@onNull final String packageName, @NonNull final int[] userIds)422         private void onPackageReplacing(@NonNull final String packageName,
423                 @NonNull final int[] userIds) {
424             try {
425                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
426                 for (int userId : userIds) {
427                     synchronized (mLock) {
428                         mPackageManager.forgetPackageInfo(packageName, userId);
429                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
430                         if (oi != null) {
431                             mImpl.onOverlayPackageReplacing(packageName, userId);
432                         }
433                     }
434                 }
435             } finally {
436                 traceEnd(TRACE_TAG_RRO);
437             }
438         }
439 
onPackageReplaced(@onNull final String packageName, @NonNull final int[] userIds)440         private void onPackageReplaced(@NonNull final String packageName,
441                 @NonNull final int[] userIds) {
442             try {
443                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
444                 for (int userId : userIds) {
445                     synchronized (mLock) {
446                         final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
447                                 false);
448                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
449                             mPackageManager.cachePackageInfo(packageName, userId, pi);
450                             if (pi.isOverlayPackage()) {
451                                 mImpl.onOverlayPackageReplaced(packageName, userId);
452                             } else {
453                                 mImpl.onTargetPackageReplaced(packageName, userId);
454                             }
455                         }
456                     }
457                 }
458             } finally {
459                 traceEnd(TRACE_TAG_RRO);
460             }
461         }
462 
onPackageRemoved(@onNull final String packageName, @NonNull final int[] userIds)463         private void onPackageRemoved(@NonNull final String packageName,
464                 @NonNull final int[] userIds) {
465             try {
466                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
467                 for (int userId : userIds) {
468                     synchronized (mLock) {
469                         mPackageManager.forgetPackageInfo(packageName, userId);
470                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
471                         if (oi != null) {
472                             mImpl.onOverlayPackageRemoved(packageName, userId);
473                         } else {
474                             mImpl.onTargetPackageRemoved(packageName, userId);
475                         }
476                     }
477                 }
478             } finally {
479                 traceEnd(TRACE_TAG_RRO);
480             }
481         }
482     }
483 
484     private final class UserReceiver extends BroadcastReceiver {
485         @Override
onReceive(@onNull final Context context, @NonNull final Intent intent)486         public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
487             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
488             switch (intent.getAction()) {
489                 case ACTION_USER_ADDED:
490                     if (userId != UserHandle.USER_NULL) {
491                         try {
492                             traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED");
493                             final ArrayList<String> targets;
494                             synchronized (mLock) {
495                                 targets = mImpl.updateOverlaysForUser(userId);
496                             }
497                             updateOverlayPaths(userId, targets);
498                         } finally {
499                             traceEnd(TRACE_TAG_RRO);
500                         }
501                     }
502                     break;
503 
504                 case ACTION_USER_REMOVED:
505                     if (userId != UserHandle.USER_NULL) {
506                         try {
507                             traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_REMOVED");
508                             synchronized (mLock) {
509                                 mImpl.onUserRemoved(userId);
510                                 mPackageManager.forgetAllPackageInfos(userId);
511                             }
512                         } finally {
513                             traceEnd(TRACE_TAG_RRO);
514                         }
515                     }
516                     break;
517                 default:
518                     // do nothing
519                     break;
520             }
521         }
522     }
523 
524     private final IBinder mService = new IOverlayManager.Stub() {
525         @Override
526         public Map<String, List<OverlayInfo>> getAllOverlays(int userId) throws RemoteException {
527             try {
528                 traceBegin(TRACE_TAG_RRO, "OMS#getAllOverlays " + userId);
529                 userId = handleIncomingUser(userId, "getAllOverlays");
530 
531                 synchronized (mLock) {
532                     return mImpl.getOverlaysForUser(userId);
533                 }
534             } finally {
535                 traceEnd(TRACE_TAG_RRO);
536             }
537         }
538 
539         @Override
540         public List<OverlayInfo> getOverlayInfosForTarget(@Nullable final String targetPackageName,
541                 int userId) throws RemoteException {
542             try {
543                 traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfosForTarget " + targetPackageName);
544                 userId = handleIncomingUser(userId, "getOverlayInfosForTarget");
545                 if (targetPackageName == null) {
546                     return Collections.emptyList();
547                 }
548 
549                 synchronized (mLock) {
550                     return mImpl.getOverlayInfosForTarget(targetPackageName, userId);
551                 }
552             } finally {
553                 traceEnd(TRACE_TAG_RRO);
554             }
555         }
556 
557         @Override
558         public OverlayInfo getOverlayInfo(@Nullable final String packageName,
559                 int userId) throws RemoteException {
560             try {
561                 traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName);
562                 userId = handleIncomingUser(userId, "getOverlayInfo");
563                 if (packageName == null) {
564                     return null;
565                 }
566 
567                 synchronized (mLock) {
568                     return mImpl.getOverlayInfo(packageName, userId);
569                 }
570             } finally {
571                 traceEnd(TRACE_TAG_RRO);
572             }
573         }
574 
575         @Override
576         public boolean setEnabled(@Nullable final String packageName, final boolean enable,
577                 int userId) throws RemoteException {
578             try {
579                 traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable);
580                 enforceChangeOverlayPackagesPermission("setEnabled");
581                 userId = handleIncomingUser(userId, "setEnabled");
582                 if (packageName == null) {
583                     return false;
584                 }
585 
586                 final long ident = Binder.clearCallingIdentity();
587                 try {
588                     synchronized (mLock) {
589                         return mImpl.setEnabled(packageName, enable, userId);
590                     }
591                 } finally {
592                     Binder.restoreCallingIdentity(ident);
593                 }
594             } finally {
595                 traceEnd(TRACE_TAG_RRO);
596             }
597         }
598 
599         @Override
600         public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable,
601                 int userId) throws RemoteException {
602             try {
603                 traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable);
604                 enforceChangeOverlayPackagesPermission("setEnabledExclusive");
605                 userId = handleIncomingUser(userId, "setEnabledExclusive");
606                 if (packageName == null || !enable) {
607                     return false;
608                 }
609 
610                 final long ident = Binder.clearCallingIdentity();
611                 try {
612                     synchronized (mLock) {
613                         return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
614                                 userId);
615                     }
616                 } finally {
617                     Binder.restoreCallingIdentity(ident);
618                 }
619             } finally {
620                 traceEnd(TRACE_TAG_RRO);
621             }
622         }
623 
624         @Override
625         public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId)
626                 throws RemoteException {
627             try {
628                 traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName);
629                 enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory");
630                 userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory");
631                 if (packageName == null) {
632                     return false;
633                 }
634 
635                 final long ident = Binder.clearCallingIdentity();
636                 try {
637                     synchronized (mLock) {
638                         return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
639                                 userId);
640                     }
641                 } finally {
642                     Binder.restoreCallingIdentity(ident);
643                 }
644             } finally {
645                 traceEnd(TRACE_TAG_RRO);
646             }
647         }
648 
649         @Override
650         public boolean setPriority(@Nullable final String packageName,
651                 @Nullable final String parentPackageName, int userId) throws RemoteException {
652             try {
653                 traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " "
654                         + parentPackageName);
655                 enforceChangeOverlayPackagesPermission("setPriority");
656                 userId = handleIncomingUser(userId, "setPriority");
657                 if (packageName == null || parentPackageName == null) {
658                     return false;
659                 }
660 
661                 final long ident = Binder.clearCallingIdentity();
662                 try {
663                     synchronized (mLock) {
664                         return mImpl.setPriority(packageName, parentPackageName, userId);
665                     }
666                 } finally {
667                     Binder.restoreCallingIdentity(ident);
668                 }
669             } finally {
670                 traceEnd(TRACE_TAG_RRO);
671             }
672         }
673 
674         @Override
675         public boolean setHighestPriority(@Nullable final String packageName, int userId)
676                 throws RemoteException {
677             try {
678                 traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName);
679                 enforceChangeOverlayPackagesPermission("setHighestPriority");
680                 userId = handleIncomingUser(userId, "setHighestPriority");
681                 if (packageName == null) {
682                     return false;
683                 }
684 
685                 final long ident = Binder.clearCallingIdentity();
686                 try {
687                     synchronized (mLock) {
688                         return mImpl.setHighestPriority(packageName, userId);
689                     }
690                 } finally {
691                     Binder.restoreCallingIdentity(ident);
692                 }
693             } finally {
694                 traceEnd(TRACE_TAG_RRO);
695             }
696         }
697 
698         @Override
699         public boolean setLowestPriority(@Nullable final String packageName, int userId)
700                 throws RemoteException {
701             try {
702                 traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName);
703                 enforceChangeOverlayPackagesPermission("setLowestPriority");
704                 userId = handleIncomingUser(userId, "setLowestPriority");
705                 if (packageName == null) {
706                     return false;
707                 }
708 
709                 final long ident = Binder.clearCallingIdentity();
710                 try {
711                     synchronized (mLock) {
712                         return mImpl.setLowestPriority(packageName, userId);
713                     }
714                 } finally {
715                     Binder.restoreCallingIdentity(ident);
716                 }
717             } finally {
718                 traceEnd(TRACE_TAG_RRO);
719             }
720         }
721 
722         @Override
723         public String[] getDefaultOverlayPackages() throws RemoteException {
724             try {
725                 traceBegin(TRACE_TAG_RRO, "OMS#getDefaultOverlayPackages");
726                 getContext().enforceCallingOrSelfPermission(
727                         android.Manifest.permission.MODIFY_THEME_OVERLAY, null);
728 
729                 final long ident = Binder.clearCallingIdentity();
730                 try {
731                     synchronized (mLock) {
732                         return mImpl.getDefaultOverlayPackages();
733                     }
734                 } finally {
735                     Binder.restoreCallingIdentity(ident);
736                 }
737             } finally {
738                 traceEnd(TRACE_TAG_RRO);
739             }
740         }
741 
742         @Override
743         public void onShellCommand(@NonNull final FileDescriptor in,
744                 @NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
745                 @NonNull final String[] args, @NonNull final ShellCallback callback,
746                 @NonNull final ResultReceiver resultReceiver) {
747             (new OverlayManagerShellCommand(this)).exec(
748                     this, in, out, err, args, callback, resultReceiver);
749         }
750 
751         @Override
752         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
753             final DumpState dumpState = new DumpState();
754             dumpState.setUserId(UserHandle.getUserId(Binder.getCallingUid()));
755 
756             int opti = 0;
757             while (opti < args.length) {
758                 final String opt = args[opti];
759                 if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
760                     break;
761                 }
762                 opti++;
763 
764                 if ("-h".equals(opt)) {
765                     pw.println("dump [-h] [--verbose] [--user USER_ID] [[FIELD] PACKAGE]");
766                     pw.println("  Print debugging information about the overlay manager.");
767                     pw.println("  With optional parameter PACKAGE, limit output to the specified");
768                     pw.println("  package. With optional parameter FIELD, limit output to");
769                     pw.println("  the value of that SettingsItem field. Field names are");
770                     pw.println("  case insensitive and out.println the m prefix can be omitted,");
771                     pw.println("  so the following are equivalent: mState, mstate, State, state.");
772                     return;
773                 } else if ("--user".equals(opt)) {
774                     opti++;
775                     if (opti >= args.length) {
776                         pw.println("Error: user missing argument");
777                         return;
778                     }
779                     try {
780                         dumpState.setUserId(Integer.parseInt(args[opti]));
781                     } catch (NumberFormatException e) {
782                         pw.println("Error: user argument is not a number: " + args[opti]);
783                         return;
784                     }
785                 } else if ("--verbose".equals(opt)) {
786                     dumpState.setVerbose(true);
787                 } else {
788                     pw.println("Unknown argument: " + opt + "; use -h for help");
789                 }
790             }
791             if (opti < args.length) {
792                 final String arg = args[opti];
793                 opti++;
794                 switch (arg) {
795                     case "packagename":
796                     case "userid":
797                     case "targetpackagename":
798                     case "targetoverlayablename":
799                     case "basecodepath":
800                     case "state":
801                     case "isenabled":
802                     case "isstatic":
803                     case "priority":
804                     case "category":
805                         dumpState.setField(arg);
806                         break;
807                     default:
808                         dumpState.setPackageName(arg);
809                         break;
810                 }
811             }
812             if (dumpState.getPackageName() == null && opti < args.length) {
813                 dumpState.setPackageName(args[opti]);
814                 opti++;
815             }
816 
817             enforceDumpPermission("dump");
818             synchronized (mLock) {
819                 mImpl.dump(pw, dumpState);
820                 if (dumpState.getPackageName() == null) {
821                     mPackageManager.dump(pw, dumpState);
822                 }
823             }
824         }
825 
826         /**
827          * Ensure that the caller has permission to interact with the given userId.
828          * If the calling user is not the same as the provided user, the caller needs
829          * to hold the INTERACT_ACROSS_USERS_FULL permission (or be system uid or
830          * root).
831          *
832          * @param userId the user to interact with
833          * @param message message for any SecurityException
834          */
835         private int handleIncomingUser(final int userId, @NonNull final String message) {
836             return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
837                     Binder.getCallingUid(), userId, false, true, message, null);
838         }
839 
840         /**
841          * Enforce that the caller holds the CHANGE_OVERLAY_PACKAGES permission (or is
842          * system or root).
843          *
844          * @param message used as message if SecurityException is thrown
845          * @throws SecurityException if the permission check fails
846          */
847         private void enforceChangeOverlayPackagesPermission(@NonNull final String message) {
848             getContext().enforceCallingOrSelfPermission(
849                     android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, message);
850         }
851 
852         /**
853          * Enforce that the caller holds the DUMP permission (or is system or root).
854          *
855          * @param message used as message if SecurityException is thrown
856          * @throws SecurityException if the permission check fails
857          */
858         private void enforceDumpPermission(@NonNull final String message) {
859             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message);
860         }
861     };
862 
863     private final class OverlayChangeListener
864             implements OverlayManagerServiceImpl.OverlayChangeListener {
865         @Override
onOverlaysChanged(@onNull final String targetPackageName, final int userId)866         public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
867             schedulePersistSettings();
868             FgThread.getHandler().post(() -> {
869                 updateAssets(userId, targetPackageName);
870 
871                 final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
872                         Uri.fromParts("package", targetPackageName, null));
873                 intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
874 
875                 if (DEBUG) {
876                     Slog.d(TAG, "send broadcast " + intent);
877                 }
878 
879                 try {
880                     ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
881                             null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
882                             userId);
883                 } catch (RemoteException e) {
884                     // Intentionally left empty.
885                 }
886             });
887         }
888     }
889 
890     /**
891      * Updates the target packages' set of enabled overlays in PackageManager.
892      */
updateOverlayPaths(int userId, List<String> targetPackageNames)893     private void updateOverlayPaths(int userId, List<String> targetPackageNames) {
894         try {
895             traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
896             if (DEBUG) {
897                 Slog.d(TAG, "Updating overlay assets");
898             }
899             final PackageManagerInternal pm =
900                     LocalServices.getService(PackageManagerInternal.class);
901             final boolean updateFrameworkRes = targetPackageNames.contains("android");
902             if (updateFrameworkRes) {
903                 targetPackageNames = pm.getTargetPackageNames(userId);
904             }
905 
906             final Map<String, List<String>> pendingChanges =
907                     new ArrayMap<>(targetPackageNames.size());
908             synchronized (mLock) {
909                 final List<String> frameworkOverlays =
910                         mImpl.getEnabledOverlayPackageNames("android", userId);
911                 final int n = targetPackageNames.size();
912                 for (int i = 0; i < n; i++) {
913                     final String targetPackageName = targetPackageNames.get(i);
914                     List<String> list = new ArrayList<>();
915                     if (!"android".equals(targetPackageName)) {
916                         list.addAll(frameworkOverlays);
917                     }
918                     list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
919                     pendingChanges.put(targetPackageName, list);
920                 }
921             }
922 
923             final int n = targetPackageNames.size();
924             for (int i = 0; i < n; i++) {
925                 final String targetPackageName = targetPackageNames.get(i);
926                 if (DEBUG) {
927                     Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
928                             + TextUtils.join(",", pendingChanges.get(targetPackageName))
929                             + "] userId=" + userId);
930                 }
931 
932                 if (!pm.setEnabledOverlayPackages(
933                         userId, targetPackageName, pendingChanges.get(targetPackageName))) {
934                     Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
935                             targetPackageName, userId));
936                 }
937             }
938         } finally {
939             traceEnd(TRACE_TAG_RRO);
940         }
941     }
942 
updateAssets(final int userId, final String targetPackageName)943     private void updateAssets(final int userId, final String targetPackageName) {
944         updateAssets(userId, Collections.singletonList(targetPackageName));
945     }
946 
updateAssets(final int userId, List<String> targetPackageNames)947     private void updateAssets(final int userId, List<String> targetPackageNames) {
948         updateOverlayPaths(userId, targetPackageNames);
949         final IActivityManager am = ActivityManager.getService();
950         try {
951             am.scheduleApplicationInfoChanged(targetPackageNames, userId);
952         } catch (RemoteException e) {
953             // Intentionally left empty.
954         }
955     }
956 
schedulePersistSettings()957     private void schedulePersistSettings() {
958         if (mPersistSettingsScheduled.getAndSet(true)) {
959             return;
960         }
961         IoThread.getHandler().post(() -> {
962             mPersistSettingsScheduled.set(false);
963             if (DEBUG) {
964                 Slog.d(TAG, "Writing overlay settings");
965             }
966             synchronized (mLock) {
967                 FileOutputStream stream = null;
968                 try {
969                     stream = mSettingsFile.startWrite();
970                     mSettings.persist(stream);
971                     mSettingsFile.finishWrite(stream);
972                 } catch (IOException | XmlPullParserException e) {
973                     mSettingsFile.failWrite(stream);
974                     Slog.e(TAG, "failed to persist overlay state", e);
975                 }
976             }
977         });
978     }
979 
restoreSettings()980     private void restoreSettings() {
981         try {
982             traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
983             synchronized (mLock) {
984                 if (!mSettingsFile.getBaseFile().exists()) {
985                     return;
986                 }
987                 try (FileInputStream stream = mSettingsFile.openRead()) {
988                     mSettings.restore(stream);
989 
990                     // We might have data for dying users if the device was
991                     // restarted before we received USER_REMOVED. Remove data for
992                     // users that will not exist after the system is ready.
993 
994                     final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
995                     final int[] liveUserIds = new int[liveUsers.size()];
996                     for (int i = 0; i < liveUsers.size(); i++) {
997                         liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
998                     }
999                     Arrays.sort(liveUserIds);
1000 
1001                     for (int userId : mSettings.getUsers()) {
1002                         if (Arrays.binarySearch(liveUserIds, userId) < 0) {
1003                             mSettings.removeUser(userId);
1004                         }
1005                     }
1006                 } catch (IOException | XmlPullParserException e) {
1007                     Slog.e(TAG, "failed to restore overlay state", e);
1008                 }
1009             }
1010         } finally {
1011             traceEnd(TRACE_TAG_RRO);
1012         }
1013     }
1014 
1015     private static final class PackageManagerHelper implements
1016             OverlayManagerServiceImpl.PackageManagerHelper {
1017 
1018         private final IPackageManager mPackageManager;
1019         private final PackageManagerInternal mPackageManagerInternal;
1020 
1021         // Use a cache for performance and for consistency within OMS: because
1022         // additional PACKAGE_* intents may be delivered while we process an
1023         // intent, querying the PackageManagerService for the actual current
1024         // state may lead to contradictions within OMS. Better then to lag
1025         // behind until all pending intents have been processed.
1026         private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
1027 
PackageManagerHelper()1028         PackageManagerHelper() {
1029             mPackageManager = getPackageManager();
1030             mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
1031         }
1032 
getPackageInfo(@onNull final String packageName, final int userId, final boolean useCache)1033         public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
1034                 final boolean useCache) {
1035             if (useCache) {
1036                 final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId);
1037                 if (cachedPi != null) {
1038                     return cachedPi;
1039                 }
1040             }
1041             try {
1042                 final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId);
1043                 if (useCache && pi != null) {
1044                     cachePackageInfo(packageName, userId, pi);
1045                 }
1046                 return pi;
1047             } catch (RemoteException e) {
1048                 // Intentionally left empty.
1049             }
1050             return null;
1051         }
1052 
1053         @Override
getPackageInfo(@onNull final String packageName, final int userId)1054         public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
1055             return getPackageInfo(packageName, userId, true);
1056         }
1057 
1058         @Override
signaturesMatching(@onNull final String packageName1, @NonNull final String packageName2, final int userId)1059         public boolean signaturesMatching(@NonNull final String packageName1,
1060                 @NonNull final String packageName2, final int userId) {
1061             // The package manager does not support different versions of packages
1062             // to be installed for different users: ignore userId for now.
1063             try {
1064                 return mPackageManager.checkSignatures(
1065                         packageName1, packageName2) == SIGNATURE_MATCH;
1066             } catch (RemoteException e) {
1067                 // Intentionally left blank
1068             }
1069             return false;
1070         }
1071 
1072         @Override
getOverlayPackages(final int userId)1073         public List<PackageInfo> getOverlayPackages(final int userId) {
1074             return mPackageManagerInternal.getOverlayPackages(userId);
1075         }
1076 
getCachedPackageInfo(@onNull final String packageName, final int userId)1077         public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
1078                 final int userId) {
1079             final HashMap<String, PackageInfo> map = mCache.get(userId);
1080             return map == null ? null : map.get(packageName);
1081         }
1082 
cachePackageInfo(@onNull final String packageName, final int userId, @NonNull final PackageInfo pi)1083         public void cachePackageInfo(@NonNull final String packageName, final int userId,
1084                 @NonNull final PackageInfo pi) {
1085             HashMap<String, PackageInfo> map = mCache.get(userId);
1086             if (map == null) {
1087                 map = new HashMap<>();
1088                 mCache.put(userId, map);
1089             }
1090             map.put(packageName, pi);
1091         }
1092 
forgetPackageInfo(@onNull final String packageName, final int userId)1093         public void forgetPackageInfo(@NonNull final String packageName, final int userId) {
1094             final HashMap<String, PackageInfo> map = mCache.get(userId);
1095             if (map == null) {
1096                 return;
1097             }
1098             map.remove(packageName);
1099             if (map.isEmpty()) {
1100                 mCache.delete(userId);
1101             }
1102         }
1103 
forgetAllPackageInfos(final int userId)1104         public void forgetAllPackageInfos(final int userId) {
1105             mCache.delete(userId);
1106         }
1107 
1108         private static final String TAB1 = "    ";
1109         private static final String TAB2 = TAB1 + TAB1;
1110 
dump(@onNull final PrintWriter pw, @NonNull DumpState dumpState)1111         public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
1112             pw.println("PackageInfo cache");
1113 
1114             if (!dumpState.isVerbose()) {
1115                 int count = 0;
1116                 final int n = mCache.size();
1117                 for (int i = 0; i < n; i++) {
1118                     final int userId = mCache.keyAt(i);
1119                     count += mCache.get(userId).size();
1120                 }
1121                 pw.println(TAB1 + count + " package(s)");
1122                 return;
1123             }
1124 
1125             if (mCache.size() == 0) {
1126                 pw.println(TAB1 + "<empty>");
1127                 return;
1128             }
1129 
1130             final int n = mCache.size();
1131             for (int i = 0; i < n; i++) {
1132                 final int userId = mCache.keyAt(i);
1133                 pw.println(TAB1 + "User " + userId);
1134                 final HashMap<String, PackageInfo> map = mCache.get(userId);
1135                 for (Map.Entry<String, PackageInfo> entry : map.entrySet()) {
1136                     pw.println(TAB2 + entry.getKey() + ": " + entry.getValue());
1137                 }
1138             }
1139         }
1140     }
1141 }
1142