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