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.content.om.OverlayInfo.STATE_DISABLED;
20 import static android.content.om.OverlayInfo.STATE_ENABLED;
21 import static android.content.om.OverlayInfo.STATE_ENABLED_STATIC;
22 import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
23 import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
24 import static android.content.om.OverlayInfo.STATE_OVERLAY_UPGRADING;
25 import static android.content.om.OverlayInfo.STATE_TARGET_UPGRADING;
26 
27 import static com.android.server.om.OverlayManagerService.DEBUG;
28 import static com.android.server.om.OverlayManagerService.TAG;
29 
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.content.om.OverlayInfo;
33 import android.content.pm.PackageInfo;
34 import android.text.TextUtils;
35 import android.util.ArrayMap;
36 import android.util.ArraySet;
37 import android.util.Slog;
38 
39 import java.io.PrintWriter;
40 import java.util.ArrayList;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Objects;
45 import java.util.Set;
46 
47 /**
48  * Internal implementation of OverlayManagerService.
49  *
50  * Methods in this class should only be called by the OverlayManagerService.
51  * This class is not thread-safe; the caller is expected to ensure the
52  * necessary thread synchronization.
53  *
54  * @see OverlayManagerService
55  */
56 final class OverlayManagerServiceImpl {
57     // Flags to use in conjunction with updateState.
58     private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
59     private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
60 
61     private final PackageManagerHelper mPackageManager;
62     private final IdmapManager mIdmapManager;
63     private final OverlayManagerSettings mSettings;
64     private final String[] mDefaultOverlays;
65     private final OverlayChangeListener mListener;
66 
67     /**
68      * Helper method to merge the overlay manager's (as read from overlays.xml)
69      * and package manager's (as parsed from AndroidManifest.xml files) views
70      * on overlays.
71      *
72      * Both managers are usually in agreement, but especially after an OTA things
73      * may differ. The package manager is always providing the truth; the overlay
74      * manager has to adapt. Depending on what has changed about an overlay, we
75      * should either scrap the overlay manager's previous settings or merge the old
76      * settings with the new.
77      */
mustReinitializeOverlay(@onNull final PackageInfo theTruth, @Nullable final OverlayInfo oldSettings)78     private static boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth,
79             @Nullable final OverlayInfo oldSettings) {
80         if (oldSettings == null) {
81             return true;
82         }
83         if (!Objects.equals(theTruth.overlayTarget, oldSettings.targetPackageName)) {
84             return true;
85         }
86         if (theTruth.isStaticOverlayPackage() != oldSettings.isStatic) {
87             return true;
88         }
89         // a change in priority is only relevant for static RROs: specifically,
90         // a regular RRO should not have its state reset only because a change
91         // in priority
92         if (theTruth.isStaticOverlayPackage() &&
93                 theTruth.overlayPriority != oldSettings.priority) {
94             return true;
95         }
96         return false;
97     }
98 
OverlayManagerServiceImpl(@onNull final PackageManagerHelper packageManager, @NonNull final IdmapManager idmapManager, @NonNull final OverlayManagerSettings settings, @NonNull final String[] defaultOverlays, @NonNull final OverlayChangeListener listener)99     OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager,
100             @NonNull final IdmapManager idmapManager,
101             @NonNull final OverlayManagerSettings settings,
102             @NonNull final String[] defaultOverlays,
103             @NonNull final OverlayChangeListener listener) {
104         mPackageManager = packageManager;
105         mIdmapManager = idmapManager;
106         mSettings = settings;
107         mDefaultOverlays = defaultOverlays;
108         mListener = listener;
109     }
110 
111     /**
112      * Call this to synchronize the Settings for a user with what PackageManager knows about a user.
113      * Returns a list of target packages that must refresh their overlays. This list is the union
114      * of two sets: the set of targets with currently active overlays, and the
115      * set of targets that had, but no longer have, active overlays.
116      */
updateOverlaysForUser(final int newUserId)117     ArrayList<String> updateOverlaysForUser(final int newUserId) {
118         if (DEBUG) {
119             Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId);
120         }
121 
122         final Set<String> packagesToUpdateAssets = new ArraySet<>();
123         final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId);
124         final int tmpSize = tmp.size();
125         final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize);
126         for (int i = 0; i < tmpSize; i++) {
127             final List<OverlayInfo> chunk = tmp.valueAt(i);
128             final int chunkSize = chunk.size();
129             for (int j = 0; j < chunkSize; j++) {
130                 final OverlayInfo oi = chunk.get(j);
131                 storedOverlayInfos.put(oi.packageName, oi);
132             }
133         }
134 
135         // Reset overlays if something critical like the target package name
136         // has changed
137         List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId);
138         final int overlayPackagesSize = overlayPackages.size();
139         for (int i = 0; i < overlayPackagesSize; i++) {
140             final PackageInfo overlayPackage = overlayPackages.get(i);
141             final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName);
142 
143             if (mustReinitializeOverlay(overlayPackage, oi)) {
144                 // if targetPackageName has changed the package that *used* to
145                 // be the target must also update its assets
146                 if (oi != null) {
147                     packagesToUpdateAssets.add(oi.targetPackageName);
148                 }
149 
150                 mSettings.init(overlayPackage.packageName, newUserId,
151                         overlayPackage.overlayTarget,
152                         overlayPackage.applicationInfo.getBaseCodePath(),
153                         overlayPackage.isStaticOverlayPackage(),
154                         overlayPackage.overlayPriority,
155                         overlayPackage.overlayCategory);
156             }
157 
158             storedOverlayInfos.remove(overlayPackage.packageName);
159         }
160 
161         // any OverlayInfo left in storedOverlayInfos is no longer
162         // installed and should be removed
163         final int storedOverlayInfosSize = storedOverlayInfos.size();
164         for (int i = 0; i < storedOverlayInfosSize; i++) {
165             final OverlayInfo oi = storedOverlayInfos.valueAt(i);
166             mSettings.remove(oi.packageName, oi.userId);
167             removeIdmapIfPossible(oi);
168             packagesToUpdateAssets.add(oi.targetPackageName);
169         }
170 
171         // make sure every overlay's state is up-to-date; this needs to happen
172         // after old overlays have been removed, or we risk removing a
173         // legitimate idmap file if a new overlay package has the same apk path
174         // as the removed overlay package used to have
175         for (int i = 0; i < overlayPackagesSize; i++) {
176             final PackageInfo overlayPackage = overlayPackages.get(i);
177             try {
178                 updateState(overlayPackage.overlayTarget, overlayPackage.packageName,
179                         newUserId, 0);
180             } catch (OverlayManagerSettings.BadKeyException e) {
181                 Slog.e(TAG, "failed to update settings", e);
182                 mSettings.remove(overlayPackage.packageName, newUserId);
183             }
184             packagesToUpdateAssets.add(overlayPackage.overlayTarget);
185         }
186 
187         // remove target packages that are not installed
188         final Iterator<String> iter = packagesToUpdateAssets.iterator();
189         while (iter.hasNext()) {
190             String targetPackageName = iter.next();
191             if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) {
192                 iter.remove();
193             }
194         }
195 
196         // Collect all of the categories in which we have at least one overlay enabled.
197         final ArraySet<String> enabledCategories = new ArraySet<>();
198         final ArrayMap<String, List<OverlayInfo>> userOverlays =
199                 mSettings.getOverlaysForUser(newUserId);
200         final int userOverlayTargetCount = userOverlays.size();
201         for (int i = 0; i < userOverlayTargetCount; i++) {
202             final List<OverlayInfo> overlayList = userOverlays.valueAt(i);
203             final int overlayCount = overlayList != null ? overlayList.size() : 0;
204             for (int j = 0; j < overlayCount; j++) {
205                 final OverlayInfo oi = overlayList.get(j);
206                 if (oi.isEnabled()) {
207                     enabledCategories.add(oi.category);
208                 }
209             }
210         }
211 
212         // Enable the default overlay if its category does not have a single overlay enabled.
213         for (final String defaultOverlay : mDefaultOverlays) {
214             try {
215                 final OverlayInfo oi = mSettings.getOverlayInfo(defaultOverlay, newUserId);
216                 if (!enabledCategories.contains(oi.category)) {
217                     Slog.w(TAG, "Enabling default overlay '" + defaultOverlay + "' for target '"
218                             + oi.targetPackageName + "' in category '" + oi.category + "' for user "
219                             + newUserId);
220                     mSettings.setEnabled(oi.packageName, newUserId, true);
221                     if (updateState(oi.targetPackageName, oi.packageName, newUserId, 0)) {
222                         packagesToUpdateAssets.add(oi.targetPackageName);
223                     }
224                 }
225             } catch (OverlayManagerSettings.BadKeyException e) {
226                 Slog.e(TAG, "Failed to set default overlay '" + defaultOverlay + "' for user "
227                         + newUserId, e);
228             }
229         }
230 
231         return new ArrayList<>(packagesToUpdateAssets);
232     }
233 
onUserRemoved(final int userId)234     void onUserRemoved(final int userId) {
235         if (DEBUG) {
236             Slog.d(TAG, "onUserRemoved userId=" + userId);
237         }
238         mSettings.removeUser(userId);
239     }
240 
onTargetPackageAdded(@onNull final String packageName, final int userId)241     void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
242         if (DEBUG) {
243             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
244         }
245 
246         if (updateAllOverlaysForTarget(packageName, userId, 0)) {
247             mListener.onOverlaysChanged(packageName, userId);
248         }
249     }
250 
onTargetPackageChanged(@onNull final String packageName, final int userId)251     void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
252         if (DEBUG) {
253             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
254         }
255 
256         updateAllOverlaysForTarget(packageName, userId, 0);
257     }
258 
onTargetPackageUpgrading(@onNull final String packageName, final int userId)259     void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
260         if (DEBUG) {
261             Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId="
262                     + userId);
263         }
264 
265         updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING);
266     }
267 
onTargetPackageUpgraded(@onNull final String packageName, final int userId)268     void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
269         if (DEBUG) {
270             Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
271         }
272 
273         updateAllOverlaysForTarget(packageName, userId, 0);
274     }
275 
onTargetPackageRemoved(@onNull final String packageName, final int userId)276     void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
277         if (DEBUG) {
278             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
279         }
280 
281         if (updateAllOverlaysForTarget(packageName, userId, 0)) {
282             mListener.onOverlaysChanged(packageName, userId);
283         }
284     }
285 
286     /**
287      * Update the state of any overlays for this target.
288      *
289      * Returns true if the system should refresh the app's overlay paths (i.e.
290      * if the settings were modified for this target, or there is at least one
291      * enabled framework overlay).
292      */
updateAllOverlaysForTarget(@onNull final String targetPackageName, final int userId, final int flags)293     private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName,
294             final int userId, final int flags) {
295         boolean modified = false;
296         final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
297         final int N = ois.size();
298         for (int i = 0; i < N; i++) {
299             final OverlayInfo oi = ois.get(i);
300             final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
301                     userId);
302             if (overlayPackage == null) {
303                 modified |= mSettings.remove(oi.packageName, oi.userId);
304                 removeIdmapIfPossible(oi);
305             } else {
306                 try {
307                     modified |= updateState(targetPackageName, oi.packageName, userId, flags);
308                 } catch (OverlayManagerSettings.BadKeyException e) {
309                     Slog.e(TAG, "failed to update settings", e);
310                     modified |= mSettings.remove(oi.packageName, userId);
311                 }
312             }
313         }
314 
315         // check for enabled framework overlays
316         modified = modified || !getEnabledOverlayPackageNames("android", userId).isEmpty();
317 
318         return modified;
319     }
320 
onOverlayPackageAdded(@onNull final String packageName, final int userId)321     void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
322         if (DEBUG) {
323             Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
324         }
325 
326         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
327         if (overlayPackage == null) {
328             Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
329             onOverlayPackageRemoved(packageName, userId);
330             return;
331         }
332 
333         mSettings.init(packageName, userId, overlayPackage.overlayTarget,
334                 overlayPackage.applicationInfo.getBaseCodePath(),
335                 overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority,
336                 overlayPackage.overlayCategory);
337         try {
338             if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
339                 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
340             }
341         } catch (OverlayManagerSettings.BadKeyException e) {
342             Slog.e(TAG, "failed to update settings", e);
343             mSettings.remove(packageName, userId);
344         }
345     }
346 
onOverlayPackageChanged(@onNull final String packageName, final int userId)347     void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
348         if (DEBUG) {
349             Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
350         }
351 
352         try {
353             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
354             if (updateState(oi.targetPackageName, packageName, userId, 0)) {
355                 mListener.onOverlaysChanged(oi.targetPackageName, userId);
356             }
357         } catch (OverlayManagerSettings.BadKeyException e) {
358             Slog.e(TAG, "failed to update settings", e);
359         }
360     }
361 
onOverlayPackageUpgrading(@onNull final String packageName, final int userId)362     void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) {
363         if (DEBUG) {
364             Slog.d(TAG, "onOverlayPackageUpgrading packageName=" + packageName + " userId="
365                     + userId);
366         }
367 
368         try {
369             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
370             if (updateState(oi.targetPackageName, packageName, userId, FLAG_OVERLAY_IS_UPGRADING)) {
371                 removeIdmapIfPossible(oi);
372                 mListener.onOverlaysChanged(oi.targetPackageName, userId);
373             }
374         } catch (OverlayManagerSettings.BadKeyException e) {
375             Slog.e(TAG, "failed to update settings", e);
376         }
377     }
378 
onOverlayPackageUpgraded(@onNull final String packageName, final int userId)379     void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) {
380         if (DEBUG) {
381             Slog.d(TAG, "onOverlayPackageUpgraded packageName=" + packageName + " userId="
382                     + userId);
383         }
384 
385         final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
386         if (pkg == null) {
387             Slog.w(TAG, "overlay package " + packageName + " was upgraded, but couldn't be found");
388             onOverlayPackageRemoved(packageName, userId);
389             return;
390         }
391 
392         try {
393             final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
394             if (mustReinitializeOverlay(pkg, oldOi)) {
395                 if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) {
396                     mListener.onOverlaysChanged(pkg.overlayTarget, userId);
397                 }
398                 mSettings.init(packageName, userId, pkg.overlayTarget,
399                         pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(),
400                         pkg.overlayPriority, pkg.overlayCategory);
401             }
402 
403             if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
404                 mListener.onOverlaysChanged(pkg.overlayTarget, userId);
405             }
406         } catch (OverlayManagerSettings.BadKeyException e) {
407             Slog.e(TAG, "failed to update settings", e);
408         }
409     }
410 
onOverlayPackageRemoved(@onNull final String packageName, final int userId)411     void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
412         try {
413             final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
414             if (mSettings.remove(packageName, userId)) {
415                 removeIdmapIfPossible(overlayInfo);
416                 if (overlayInfo.isEnabled()) {
417                     // Only trigger updates if the overlay was enabled.
418                     mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
419                 }
420             }
421         } catch (OverlayManagerSettings.BadKeyException e) {
422             Slog.e(TAG, "failed to remove overlay", e);
423         }
424     }
425 
getOverlayInfo(@onNull final String packageName, final int userId)426     OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) {
427         try {
428             return mSettings.getOverlayInfo(packageName, userId);
429         } catch (OverlayManagerSettings.BadKeyException e) {
430             return null;
431         }
432     }
433 
getOverlayInfosForTarget(@onNull final String targetPackageName, final int userId)434     List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
435             final int userId) {
436         return mSettings.getOverlaysForTarget(targetPackageName, userId);
437     }
438 
getOverlaysForUser(final int userId)439     Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
440         return mSettings.getOverlaysForUser(userId);
441     }
442 
setEnabled(@onNull final String packageName, final boolean enable, final int userId)443     boolean setEnabled(@NonNull final String packageName, final boolean enable,
444             final int userId) {
445         if (DEBUG) {
446             Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
447                         packageName, enable, userId));
448         }
449 
450         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
451         if (overlayPackage == null) {
452             return false;
453         }
454 
455         // Ignore static overlays.
456         if (overlayPackage.isStaticOverlayPackage()) {
457             return false;
458         }
459 
460         try {
461             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
462             boolean modified = mSettings.setEnabled(packageName, userId, enable);
463             modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
464 
465             if (modified) {
466                 mListener.onOverlaysChanged(oi.targetPackageName, userId);
467             }
468             return true;
469         } catch (OverlayManagerSettings.BadKeyException e) {
470             return false;
471         }
472     }
473 
setEnabledExclusive(@onNull final String packageName, boolean withinCategory, final int userId)474     boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
475             final int userId) {
476         if (DEBUG) {
477             Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
478                     + " withinCategory=%s userId=%d", packageName, withinCategory, userId));
479         }
480 
481         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
482         if (overlayPackage == null) {
483             return false;
484         }
485 
486         try {
487             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
488             final String targetPackageName = oi.targetPackageName;
489 
490             List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId);
491 
492             boolean modified = false;
493 
494             // Disable all other overlays.
495             allOverlays.remove(oi);
496             for (int i = 0; i < allOverlays.size(); i++) {
497                 final String disabledOverlayPackageName = allOverlays.get(i).packageName;
498                 final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo(
499                         disabledOverlayPackageName, userId);
500                 if (disabledOverlayPackageInfo == null) {
501                     modified |= mSettings.remove(disabledOverlayPackageName, userId);
502                     continue;
503                 }
504 
505                 if (disabledOverlayPackageInfo.isStaticOverlayPackage()) {
506                     // Don't touch static overlays.
507                     continue;
508                 }
509                 if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory,
510                         oi.category)) {
511                     // Don't touch overlays from other categories.
512                     continue;
513                 }
514 
515                 // Disable the overlay.
516                 modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);
517                 modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0);
518             }
519 
520             // Enable the selected overlay.
521             modified |= mSettings.setEnabled(packageName, userId, true);
522             modified |= updateState(targetPackageName, packageName, userId, 0);
523 
524             if (modified) {
525                 mListener.onOverlaysChanged(targetPackageName, userId);
526             }
527             return true;
528         } catch (OverlayManagerSettings.BadKeyException e) {
529             return false;
530         }
531     }
532 
isPackageUpdatableOverlay(@onNull final String packageName, final int userId)533     private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
534         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
535         if (overlayPackage == null || overlayPackage.isStaticOverlayPackage()) {
536             return false;
537         }
538         return true;
539     }
540 
setPriority(@onNull final String packageName, @NonNull final String newParentPackageName, final int userId)541     boolean setPriority(@NonNull final String packageName,
542             @NonNull final String newParentPackageName, final int userId) {
543         if (DEBUG) {
544             Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
545                     + newParentPackageName + " userId=" + userId);
546         }
547 
548         if (!isPackageUpdatableOverlay(packageName, userId)) {
549             return false;
550         }
551 
552         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
553         if (overlayPackage == null) {
554             return false;
555         }
556 
557         if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
558             mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
559         }
560         return true;
561     }
562 
setHighestPriority(@onNull final String packageName, final int userId)563     boolean setHighestPriority(@NonNull final String packageName, final int userId) {
564         if (DEBUG) {
565             Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
566         }
567 
568         if (!isPackageUpdatableOverlay(packageName, userId)) {
569             return false;
570         }
571 
572         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
573         if (overlayPackage == null) {
574             return false;
575         }
576 
577         if (mSettings.setHighestPriority(packageName, userId)) {
578             mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
579         }
580         return true;
581     }
582 
setLowestPriority(@onNull final String packageName, final int userId)583     boolean setLowestPriority(@NonNull final String packageName, final int userId) {
584         if (DEBUG) {
585             Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
586         }
587 
588         if (!isPackageUpdatableOverlay(packageName, userId)) {
589             return false;
590         }
591 
592         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
593         if (overlayPackage == null) {
594             return false;
595         }
596 
597         if (mSettings.setLowestPriority(packageName, userId)) {
598             mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
599         }
600         return true;
601     }
602 
onDump(@onNull final PrintWriter pw)603     void onDump(@NonNull final PrintWriter pw) {
604         mSettings.dump(pw);
605         pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
606     }
607 
getEnabledOverlayPackageNames(@onNull final String targetPackageName, final int userId)608     List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
609             final int userId) {
610         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
611                 userId);
612         final List<String> paths = new ArrayList<>(overlays.size());
613         final int N = overlays.size();
614         for (int i = 0; i < N; i++) {
615             final OverlayInfo oi = overlays.get(i);
616             if (oi.isEnabled()) {
617                 paths.add(oi.packageName);
618             }
619         }
620         return paths;
621     }
622 
623     /**
624      * Returns true if the settings/state was modified, false otherwise.
625      */
updateState(@onNull final String targetPackageName, @NonNull final String overlayPackageName, final int userId, final int flags)626     private boolean updateState(@NonNull final String targetPackageName,
627             @NonNull final String overlayPackageName, final int userId, final int flags)
628             throws OverlayManagerSettings.BadKeyException {
629 
630         final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId);
631         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
632                 userId);
633 
634         // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
635         if (targetPackage != null && overlayPackage != null &&
636                 !("android".equals(targetPackageName)
637                         && overlayPackage.isStaticOverlayPackage())) {
638             mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
639         }
640 
641         boolean modified = false;
642         if (overlayPackage != null) {
643             modified |= mSettings.setBaseCodePath(overlayPackageName, userId,
644                     overlayPackage.applicationInfo.getBaseCodePath());
645             modified |= mSettings.setCategory(overlayPackageName, userId,
646                     overlayPackage.overlayCategory);
647         }
648 
649         final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId);
650         final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage,
651                 userId, flags);
652         if (currentState != newState) {
653             if (DEBUG) {
654                 Slog.d(TAG, String.format("%s:%d: %s -> %s",
655                             overlayPackageName, userId,
656                             OverlayInfo.stateToString(currentState),
657                             OverlayInfo.stateToString(newState)));
658             }
659             modified |= mSettings.setState(overlayPackageName, userId, newState);
660         }
661         return modified;
662     }
663 
calculateNewState(@ullable final PackageInfo targetPackage, @Nullable final PackageInfo overlayPackage, final int userId, final int flags)664     private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
665             @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
666         throws OverlayManagerSettings.BadKeyException {
667 
668         if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
669             return STATE_TARGET_UPGRADING;
670         }
671 
672         if ((flags & FLAG_OVERLAY_IS_UPGRADING) != 0) {
673             return STATE_OVERLAY_UPGRADING;
674         }
675 
676         // assert expectation on overlay package: can only be null if the flags are used
677         if (DEBUG && overlayPackage == null) {
678             throw new IllegalArgumentException("null overlay package not compatible with no flags");
679         }
680 
681         if (targetPackage == null) {
682             return STATE_MISSING_TARGET;
683         }
684 
685         if (!mIdmapManager.idmapExists(overlayPackage, userId)) {
686             return STATE_NO_IDMAP;
687         }
688 
689         if (overlayPackage.isStaticOverlayPackage()) {
690             return STATE_ENABLED_STATIC;
691         }
692 
693         final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
694         return enabled ? STATE_ENABLED : STATE_DISABLED;
695     }
696 
removeIdmapIfPossible(@onNull final OverlayInfo oi)697     private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) {
698         // For a given package, all Android users share the same idmap file.
699         // This works because Android currently does not support users to
700         // install different versions of the same package. It also means we
701         // cannot remove an idmap file if any user still needs it.
702         //
703         // When/if the Android framework allows different versions of the same
704         // package to be installed for different users, idmap file handling
705         // should be revised:
706         //
707         // - an idmap file should be unique for each {user, package} pair
708         //
709         // - the path to the idmap file should be passed to the native Asset
710         //   Manager layers, just like the path to the apk is passed today
711         //
712         // As part of that change, calls to this method should be replaced by
713         // direct calls to IdmapManager.removeIdmap, without looping over all
714         // users.
715 
716         if (!mIdmapManager.idmapExists(oi)) {
717             return;
718         }
719         final int[] userIds = mSettings.getUsers();
720         for (int userId : userIds) {
721             try {
722                 final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId);
723                 if (tmp != null && tmp.isEnabled()) {
724                     // someone is still using the idmap file -> we cannot remove it
725                     return;
726                 }
727             } catch (OverlayManagerSettings.BadKeyException e) {
728                 // intentionally left empty
729             }
730         }
731         mIdmapManager.removeIdmap(oi, oi.userId);
732     }
733 
734     interface OverlayChangeListener {
735 
736         /**
737          * An event triggered by changes made to overlay state or settings as well as changes that
738          * add or remove target packages of overlays.
739          **/
onOverlaysChanged(@onNull String targetPackage, int userId)740         void onOverlaysChanged(@NonNull String targetPackage, int userId);
741     }
742 
743     interface PackageManagerHelper {
getPackageInfo(@onNull String packageName, int userId)744         PackageInfo getPackageInfo(@NonNull String packageName, int userId);
signaturesMatching(@onNull String packageName1, @NonNull String packageName2, int userId)745         boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
746                                    int userId);
getOverlayPackages(int userId)747         List<PackageInfo> getOverlayPackages(int userId);
748     }
749 }
750