• 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_MISSING_TARGET;
22 import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
23 import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED;
24 import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED;
25 import static android.os.UserHandle.USER_SYSTEM;
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.CriticalOverlayInfo;
33 import android.content.om.OverlayIdentifier;
34 import android.content.om.OverlayInfo;
35 import android.content.pm.overlay.OverlayPaths;
36 import android.content.pm.parsing.FrameworkParsingPackageUtils;
37 import android.os.FabricatedOverlayInfo;
38 import android.os.FabricatedOverlayInternal;
39 import android.text.TextUtils;
40 import android.util.ArrayMap;
41 import android.util.ArraySet;
42 import android.util.Pair;
43 import android.util.Slog;
44 
45 import com.android.internal.content.om.OverlayConfig;
46 import com.android.internal.util.CollectionUtils;
47 import com.android.server.pm.parsing.pkg.AndroidPackage;
48 
49 import java.io.PrintWriter;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Objects;
55 import java.util.Optional;
56 import java.util.Set;
57 import java.util.function.Predicate;
58 
59 /**
60  * Internal implementation of OverlayManagerService.
61  *
62  * Methods in this class should only be called by the OverlayManagerService.
63  * This class is not thread-safe; the caller is expected to ensure the
64  * necessary thread synchronization.
65  *
66  * @see OverlayManagerService
67  */
68 final class OverlayManagerServiceImpl {
69     /**
70      * @deprecated Not used. See {@link android.content.om.OverlayInfo#STATE_TARGET_UPGRADING}.
71      */
72     @Deprecated
73     private static final int FLAG_TARGET_IS_BEING_REPLACED = 1 << 0;
74 
75     // Flags to use in conjunction with updateState.
76     private static final int FLAG_OVERLAY_IS_BEING_REPLACED = 1 << 1;
77 
78     private final PackageManagerHelper mPackageManager;
79     private final IdmapManager mIdmapManager;
80     private final OverlayManagerSettings mSettings;
81     private final OverlayConfig mOverlayConfig;
82     private final String[] mDefaultOverlays;
83 
84     /**
85      * Helper method to merge the overlay manager's (as read from overlays.xml)
86      * and package manager's (as parsed from AndroidManifest.xml files) views
87      * on overlays.
88      *
89      * Both managers are usually in agreement, but especially after an OTA things
90      * may differ. The package manager is always providing the truth; the overlay
91      * manager has to adapt. Depending on what has changed about an overlay, we
92      * should either scrap the overlay manager's previous settings or merge the old
93      * settings with the new.
94      */
mustReinitializeOverlay(@onNull final AndroidPackage theTruth, @Nullable final OverlayInfo oldSettings)95     private boolean mustReinitializeOverlay(@NonNull final AndroidPackage theTruth,
96             @Nullable final OverlayInfo oldSettings) {
97         if (oldSettings == null) {
98             return true;
99         }
100         if (!Objects.equals(theTruth.getOverlayTarget(), oldSettings.targetPackageName)) {
101             return true;
102         }
103         if (!Objects.equals(theTruth.getOverlayTargetOverlayableName(),
104                 oldSettings.targetOverlayableName)) {
105             return true;
106         }
107         if (oldSettings.isFabricated) {
108             return true;
109         }
110         boolean isMutable = isPackageConfiguredMutable(theTruth);
111         if (isMutable != oldSettings.isMutable) {
112             return true;
113         }
114         // If an immutable overlay changes its configured enabled state, reinitialize the overlay.
115         if (!isMutable && isPackageConfiguredEnabled(theTruth) != oldSettings.isEnabled()) {
116             return true;
117         }
118         return false;
119     }
120 
mustReinitializeOverlay(@onNull final FabricatedOverlayInfo theTruth, @Nullable final OverlayInfo oldSettings)121     private boolean mustReinitializeOverlay(@NonNull final FabricatedOverlayInfo theTruth,
122             @Nullable final OverlayInfo oldSettings) {
123         if (oldSettings == null) {
124             return true;
125         }
126         if (!Objects.equals(theTruth.targetPackageName, oldSettings.targetPackageName)) {
127             return true;
128         }
129         if (!Objects.equals(theTruth.targetOverlayable, oldSettings.targetOverlayableName)) {
130             return true;
131         }
132         return false;
133     }
134 
OverlayManagerServiceImpl(@onNull final PackageManagerHelper packageManager, @NonNull final IdmapManager idmapManager, @NonNull final OverlayManagerSettings settings, @NonNull final OverlayConfig overlayConfig, @NonNull final String[] defaultOverlays)135     OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager,
136             @NonNull final IdmapManager idmapManager,
137             @NonNull final OverlayManagerSettings settings,
138             @NonNull final OverlayConfig overlayConfig,
139             @NonNull final String[] defaultOverlays) {
140         mPackageManager = packageManager;
141         mIdmapManager = idmapManager;
142         mSettings = settings;
143         mOverlayConfig = overlayConfig;
144         mDefaultOverlays = defaultOverlays;
145     }
146 
147     /**
148      * Call this to synchronize the Settings for a user with what PackageManager knows about a user.
149      * Returns a list of target packages that must refresh their overlays. This list is the union
150      * of two sets: the set of targets with currently active overlays, and the
151      * set of targets that had, but no longer have, active overlays.
152      */
153     @NonNull
updateOverlaysForUser(final int newUserId)154     ArraySet<PackageAndUser> updateOverlaysForUser(final int newUserId) {
155         if (DEBUG) {
156             Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId);
157         }
158 
159         // Remove the settings of all overlays that are no longer installed for this user.
160         final ArraySet<PackageAndUser> updatedTargets = new ArraySet<>();
161         final ArrayMap<String, AndroidPackage> userPackages = mPackageManager.initializeForUser(
162                 newUserId);
163         CollectionUtils.addAll(updatedTargets, removeOverlaysForUser(
164                 (info) -> !userPackages.containsKey(info.packageName), newUserId));
165 
166         // Update the state of all installed packages containing overlays, and initialize new
167         // overlays that are not currently in the settings.
168         for (int i = 0, n = userPackages.size(); i < n; i++) {
169             final AndroidPackage pkg = userPackages.valueAt(i);
170             try {
171                 CollectionUtils.addAll(updatedTargets,
172                         updatePackageOverlays(pkg, newUserId, 0 /* flags */));
173 
174                 // When a new user is switched to for the first time, package manager must be
175                 // informed of the overlay paths for all packages installed in the user.
176                 updatedTargets.add(new PackageAndUser(pkg.getPackageName(), newUserId));
177             } catch (OperationFailedException e) {
178                 Slog.e(TAG, "failed to initialize overlays of '" + pkg.getPackageName()
179                         + "' for user " + newUserId + "", e);
180             }
181         }
182 
183         // Update the state of all fabricated overlays, and initialize fabricated overlays in the
184         // new user.
185         for (final FabricatedOverlayInfo info : getFabricatedOverlayInfos()) {
186             try {
187                 CollectionUtils.addAll(updatedTargets, registerFabricatedOverlay(
188                         info, newUserId));
189             } catch (OperationFailedException e) {
190                 Slog.e(TAG, "failed to initialize fabricated overlay of '" + info.path
191                         + "' for user " + newUserId + "", e);
192             }
193         }
194 
195         // Collect all of the categories in which we have at least one overlay enabled.
196         final ArraySet<String> enabledCategories = new ArraySet<>();
197         final ArrayMap<String, List<OverlayInfo>> userOverlays =
198                 mSettings.getOverlaysForUser(newUserId);
199         final int userOverlayTargetCount = userOverlays.size();
200         for (int i = 0; i < userOverlayTargetCount; i++) {
201             final List<OverlayInfo> overlayList = userOverlays.valueAt(i);
202             final int overlayCount = overlayList != null ? overlayList.size() : 0;
203             for (int j = 0; j < overlayCount; j++) {
204                 final OverlayInfo oi = overlayList.get(j);
205                 if (oi.isEnabled()) {
206                     enabledCategories.add(oi.category);
207                 }
208             }
209         }
210 
211         // Enable the default overlay if its category does not have a single overlay enabled.
212         for (final String defaultOverlay : mDefaultOverlays) {
213             try {
214                 // OverlayConfig is the new preferred way to enable overlays by default. This legacy
215                 // default enabled method was created before overlays could have a name specified.
216                 // Only allow enabling overlays without a name using this mechanism.
217                 final OverlayIdentifier overlay = new OverlayIdentifier(defaultOverlay);
218 
219                 final OverlayInfo oi = mSettings.getOverlayInfo(overlay, newUserId);
220                 if (!enabledCategories.contains(oi.category)) {
221                     Slog.w(TAG, "Enabling default overlay '" + defaultOverlay + "' for target '"
222                             + oi.targetPackageName + "' in category '" + oi.category + "' for user "
223                             + newUserId);
224                     mSettings.setEnabled(overlay, newUserId, true);
225                     if (updateState(oi, newUserId, 0)) {
226                         CollectionUtils.add(updatedTargets,
227                                 new PackageAndUser(oi.targetPackageName, oi.userId));
228                     }
229                 }
230             } catch (OverlayManagerSettings.BadKeyException e) {
231                 Slog.e(TAG, "Failed to set default overlay '" + defaultOverlay + "' for user "
232                         + newUserId, e);
233             }
234         }
235 
236         cleanStaleResourceCache();
237         return updatedTargets;
238     }
239 
onUserRemoved(final int userId)240     void onUserRemoved(final int userId) {
241         if (DEBUG) {
242             Slog.d(TAG, "onUserRemoved userId=" + userId);
243         }
244         mSettings.removeUser(userId);
245     }
246 
247     @NonNull
onPackageAdded(@onNull final String pkgName, final int userId)248     Set<PackageAndUser> onPackageAdded(@NonNull final String pkgName,
249             final int userId) throws OperationFailedException {
250         final Set<PackageAndUser> updatedTargets = new ArraySet<>();
251         // Always update the overlays of newly added packages.
252         updatedTargets.add(new PackageAndUser(pkgName, userId));
253         updatedTargets.addAll(reconcileSettingsForPackage(pkgName, userId, 0 /* flags */));
254         return updatedTargets;
255     }
256 
257     @NonNull
onPackageChanged(@onNull final String pkgName, final int userId)258     Set<PackageAndUser> onPackageChanged(@NonNull final String pkgName,
259             final int userId) throws OperationFailedException {
260         return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */);
261     }
262 
263     @NonNull
onPackageReplacing(@onNull final String pkgName, final int userId)264     Set<PackageAndUser> onPackageReplacing(@NonNull final String pkgName, final int userId)
265             throws OperationFailedException {
266         return reconcileSettingsForPackage(pkgName, userId, FLAG_OVERLAY_IS_BEING_REPLACED);
267     }
268 
269     @NonNull
onPackageReplaced(@onNull final String pkgName, final int userId)270     Set<PackageAndUser> onPackageReplaced(@NonNull final String pkgName, final int userId)
271             throws OperationFailedException {
272         return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */);
273     }
274 
275     @NonNull
onPackageRemoved(@onNull final String pkgName, final int userId)276     Set<PackageAndUser> onPackageRemoved(@NonNull final String pkgName, final int userId) {
277         if (DEBUG) {
278             Slog.d(TAG, "onPackageRemoved pkgName=" + pkgName + " userId=" + userId);
279         }
280         // Update the state of all overlays that target this package.
281         final Set<PackageAndUser> targets = updateOverlaysForTarget(pkgName, userId, 0 /* flags */);
282 
283         // Remove all the overlays this package declares.
284         return CollectionUtils.addAll(targets,
285                 removeOverlaysForUser(oi -> pkgName.equals(oi.packageName), userId));
286     }
287 
288     @NonNull
removeOverlaysForUser( @onNull final Predicate<OverlayInfo> condition, final int userId)289     private Set<PackageAndUser> removeOverlaysForUser(
290             @NonNull final Predicate<OverlayInfo> condition, final int userId) {
291         final List<OverlayInfo> overlays = mSettings.removeIf(
292                 io -> userId == io.userId && condition.test(io));
293         Set<PackageAndUser> targets = Collections.emptySet();
294         for (int i = 0, n = overlays.size(); i < n; i++) {
295             final OverlayInfo info = overlays.get(i);
296             targets = CollectionUtils.add(targets,
297                     new PackageAndUser(info.targetPackageName, userId));
298 
299             // Remove the idmap if the overlay is no longer installed for any user.
300             removeIdmapIfPossible(info);
301         }
302         return targets;
303     }
304 
305     @NonNull
updateOverlaysForTarget(@onNull final String targetPackage, final int userId, final int flags)306     private Set<PackageAndUser> updateOverlaysForTarget(@NonNull final String targetPackage,
307             final int userId, final int flags) {
308         boolean modified = false;
309         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackage, userId);
310         for (int i = 0, n = overlays.size(); i < n; i++) {
311             final OverlayInfo oi = overlays.get(i);
312             try {
313                 modified |= updateState(oi, userId, flags);
314             } catch (OverlayManagerSettings.BadKeyException e) {
315                 Slog.e(TAG, "failed to update settings", e);
316                 modified |= mSettings.remove(oi.getOverlayIdentifier(), userId);
317             }
318         }
319         if (!modified) {
320             return Collections.emptySet();
321         }
322         return Set.of(new PackageAndUser(targetPackage, userId));
323     }
324 
325     @NonNull
updatePackageOverlays(@onNull AndroidPackage pkg, final int userId, final int flags)326     private Set<PackageAndUser> updatePackageOverlays(@NonNull AndroidPackage pkg,
327             final int userId, final int flags) throws OperationFailedException {
328         if (pkg.getOverlayTarget() == null) {
329             // This package does not have overlays declared in its manifest.
330             return Collections.emptySet();
331         }
332 
333         Set<PackageAndUser> updatedTargets = Collections.emptySet();
334         final OverlayIdentifier overlay = new OverlayIdentifier(pkg.getPackageName());
335         final int priority = getPackageConfiguredPriority(pkg);
336         try {
337             OverlayInfo currentInfo = mSettings.getNullableOverlayInfo(overlay, userId);
338             if (mustReinitializeOverlay(pkg, currentInfo)) {
339                 if (currentInfo != null) {
340                     // If the targetPackageName has changed, the package that *used* to
341                     // be the target must also update its assets.
342                     updatedTargets = CollectionUtils.add(updatedTargets,
343                             new PackageAndUser(currentInfo.targetPackageName, userId));
344                 }
345 
346                 currentInfo = mSettings.init(overlay, userId, pkg.getOverlayTarget(),
347                         pkg.getOverlayTargetOverlayableName(), pkg.getBaseApkPath(),
348                         isPackageConfiguredMutable(pkg),
349                         isPackageConfiguredEnabled(pkg),
350                         getPackageConfiguredPriority(pkg), pkg.getOverlayCategory(),
351                         false);
352             } else if (priority != currentInfo.priority) {
353                 // Changing the priority of an overlay does not cause its settings to be
354                 // reinitialized. Reorder the overlay and update its target package.
355                 mSettings.setPriority(overlay, userId, priority);
356                 updatedTargets = CollectionUtils.add(updatedTargets,
357                         new PackageAndUser(currentInfo.targetPackageName, userId));
358             }
359 
360             // Update the enabled state of the overlay.
361             if (updateState(currentInfo, userId, flags)) {
362                 updatedTargets = CollectionUtils.add(updatedTargets,
363                         new PackageAndUser(currentInfo.targetPackageName, userId));
364             }
365         } catch (OverlayManagerSettings.BadKeyException e) {
366             throw new OperationFailedException("failed to update settings", e);
367         }
368         return updatedTargets;
369     }
370 
371     @NonNull
reconcileSettingsForPackage(@onNull final String pkgName, final int userId, final int flags)372     private Set<PackageAndUser> reconcileSettingsForPackage(@NonNull final String pkgName,
373             final int userId, final int flags) throws OperationFailedException {
374         if (DEBUG) {
375             Slog.d(TAG, "reconcileSettingsForPackage pkgName=" + pkgName + " userId=" + userId);
376         }
377 
378         // Update the state of overlays that target this package.
379         Set<PackageAndUser> updatedTargets = Collections.emptySet();
380         updatedTargets = CollectionUtils.addAll(updatedTargets,
381                 updateOverlaysForTarget(pkgName, userId, flags));
382 
383         // Realign the overlay settings with PackageManager's view of the package.
384         final AndroidPackage pkg = mPackageManager.getPackageForUser(pkgName, userId);
385         if (pkg == null) {
386             return onPackageRemoved(pkgName, userId);
387         }
388 
389         // Update the state of the overlays this package declares in its manifest.
390         updatedTargets = CollectionUtils.addAll(updatedTargets,
391                 updatePackageOverlays(pkg, userId, flags));
392         return updatedTargets;
393     }
394 
getOverlayInfo(@onNull final OverlayIdentifier packageName, final int userId)395     OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier packageName, final int userId) {
396         try {
397             return mSettings.getOverlayInfo(packageName, userId);
398         } catch (OverlayManagerSettings.BadKeyException e) {
399             return null;
400         }
401     }
402 
getOverlayInfosForTarget(@onNull final String targetPackageName, final int userId)403     List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName,
404             final int userId) {
405         return mSettings.getOverlaysForTarget(targetPackageName, userId);
406     }
407 
getOverlaysForUser(final int userId)408     Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
409         return mSettings.getOverlaysForUser(userId);
410     }
411 
412     @NonNull
setEnabled(@onNull final OverlayIdentifier overlay, final boolean enable, final int userId)413     Set<PackageAndUser> setEnabled(@NonNull final OverlayIdentifier overlay,
414             final boolean enable, final int userId) throws OperationFailedException {
415         if (DEBUG) {
416             Slog.d(TAG, String.format("setEnabled overlay=%s enable=%s userId=%d",
417                     overlay, enable, userId));
418         }
419 
420         try {
421             final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId);
422             if (!oi.isMutable) {
423                 // Ignore immutable overlays.
424                 throw new OperationFailedException(
425                         "cannot enable immutable overlay packages in runtime");
426             }
427 
428             boolean modified = mSettings.setEnabled(overlay, userId, enable);
429             modified |= updateState(oi, userId, 0);
430 
431             if (modified) {
432                 return Set.of(new PackageAndUser(oi.targetPackageName, userId));
433             }
434             return Set.of();
435         } catch (OverlayManagerSettings.BadKeyException e) {
436             throw new OperationFailedException("failed to update settings", e);
437         }
438     }
439 
setEnabledExclusive(@onNull final OverlayIdentifier overlay, boolean withinCategory, final int userId)440     Optional<PackageAndUser> setEnabledExclusive(@NonNull final OverlayIdentifier overlay,
441             boolean withinCategory, final int userId) throws OperationFailedException {
442         if (DEBUG) {
443             Slog.d(TAG, String.format("setEnabledExclusive overlay=%s"
444                     + " withinCategory=%s userId=%d", overlay, withinCategory, userId));
445         }
446 
447         try {
448             final OverlayInfo enabledInfo = mSettings.getOverlayInfo(overlay, userId);
449             if (!enabledInfo.isMutable) {
450                 throw new OperationFailedException(
451                         "cannot enable immutable overlay packages in runtime");
452             }
453 
454             // Remove the overlay to have enabled from the list of overlays to disable.
455             List<OverlayInfo> allOverlays = getOverlayInfosForTarget(enabledInfo.targetPackageName,
456                     userId);
457             allOverlays.remove(enabledInfo);
458 
459             boolean modified = false;
460             for (int i = 0; i < allOverlays.size(); i++) {
461                 final OverlayInfo disabledInfo = allOverlays.get(i);
462                 final OverlayIdentifier disabledOverlay = disabledInfo.getOverlayIdentifier();
463                 if (!disabledInfo.isMutable) {
464                     // Don't touch immutable overlays.
465                     continue;
466                 }
467                 if (withinCategory && !Objects.equals(disabledInfo.category,
468                         enabledInfo.category)) {
469                     // Don't touch overlays from other categories.
470                     continue;
471                 }
472 
473                 // Disable the overlay.
474                 modified |= mSettings.setEnabled(disabledOverlay, userId, false);
475                 modified |= updateState(disabledInfo, userId, 0);
476             }
477 
478             // Enable the selected overlay.
479             modified |= mSettings.setEnabled(overlay, userId, true);
480             modified |= updateState(enabledInfo, userId, 0);
481 
482             if (modified) {
483                 return Optional.of(new PackageAndUser(enabledInfo.targetPackageName, userId));
484             }
485             return Optional.empty();
486         } catch (OverlayManagerSettings.BadKeyException e) {
487             throw new OperationFailedException("failed to update settings", e);
488         }
489     }
490 
491     @NonNull
registerFabricatedOverlay( @onNull final FabricatedOverlayInternal overlay)492     Set<PackageAndUser> registerFabricatedOverlay(
493             @NonNull final FabricatedOverlayInternal overlay)
494             throws OperationFailedException {
495         if (FrameworkParsingPackageUtils.validateName(overlay.overlayName,
496                 false /* requireSeparator */, true /* requireFilename */) != null) {
497             throw new OperationFailedException(
498                     "overlay name can only consist of alphanumeric characters, '_', and '.'");
499         }
500 
501         final FabricatedOverlayInfo info = mIdmapManager.createFabricatedOverlay(overlay);
502         if (info == null) {
503             throw new OperationFailedException("failed to create fabricated overlay");
504         }
505 
506         final Set<PackageAndUser> updatedTargets = new ArraySet<>();
507         for (int userId : mSettings.getUsers()) {
508             updatedTargets.addAll(registerFabricatedOverlay(info, userId));
509         }
510         return updatedTargets;
511     }
512 
513     @NonNull
registerFabricatedOverlay( @onNull final FabricatedOverlayInfo info, int userId)514     private Set<PackageAndUser> registerFabricatedOverlay(
515             @NonNull final FabricatedOverlayInfo info, int userId)
516             throws OperationFailedException {
517         final OverlayIdentifier overlayIdentifier = new OverlayIdentifier(
518                 info.packageName, info.overlayName);
519         final Set<PackageAndUser> updatedTargets = new ArraySet<>();
520         OverlayInfo oi = mSettings.getNullableOverlayInfo(overlayIdentifier, userId);
521         if (oi != null) {
522             if (!oi.isFabricated) {
523                 throw new OperationFailedException("non-fabricated overlay with name '" +
524                         oi.overlayName + "' already present in '" + oi.packageName + "'");
525             }
526         }
527         try {
528             if (mustReinitializeOverlay(info, oi)) {
529                 if (oi != null) {
530                     // If the fabricated overlay changes its target package, update the previous
531                     // target package so it no longer is overlaid.
532                     updatedTargets.add(new PackageAndUser(oi.targetPackageName, userId));
533                 }
534                 oi = mSettings.init(overlayIdentifier, userId, info.targetPackageName,
535                         info.targetOverlayable, info.path, true, false,
536                         OverlayConfig.DEFAULT_PRIORITY, null, true);
537             } else {
538                 // The only non-critical part of the info that will change is path to the fabricated
539                 // overlay.
540                 mSettings.setBaseCodePath(overlayIdentifier, userId, info.path);
541             }
542             if (updateState(oi, userId, 0)) {
543                 updatedTargets.add(new PackageAndUser(oi.targetPackageName, userId));
544             }
545         } catch (OverlayManagerSettings.BadKeyException e) {
546             throw new OperationFailedException("failed to update settings", e);
547         }
548 
549         return updatedTargets;
550     }
551 
552     @NonNull
unregisterFabricatedOverlay(@onNull final OverlayIdentifier overlay)553     Set<PackageAndUser> unregisterFabricatedOverlay(@NonNull final OverlayIdentifier overlay) {
554         final Set<PackageAndUser> updatedTargets = new ArraySet<>();
555         for (int userId : mSettings.getUsers()) {
556             updatedTargets.addAll(unregisterFabricatedOverlay(overlay, userId));
557         }
558         return updatedTargets;
559     }
560 
561     @NonNull
unregisterFabricatedOverlay( @onNull final OverlayIdentifier overlay, int userId)562     private Set<PackageAndUser> unregisterFabricatedOverlay(
563             @NonNull final OverlayIdentifier overlay, int userId) {
564         final OverlayInfo oi = mSettings.getNullableOverlayInfo(overlay, userId);
565         if (oi != null) {
566             mSettings.remove(overlay, userId);
567             if (oi.isEnabled()) {
568                 // Removing a fabricated overlay only changes the overlay path of a package if it is
569                 // currently enabled.
570                 return Set.of(new PackageAndUser(oi.targetPackageName, userId));
571             }
572         }
573         return Set.of();
574     }
575 
576 
cleanStaleResourceCache()577     private void cleanStaleResourceCache() {
578         // Clean up fabricated overlays that are no longer registered in any user.
579         final Set<String> fabricatedPaths = mSettings.getAllBaseCodePaths();
580         for (final FabricatedOverlayInfo info : mIdmapManager.getFabricatedOverlayInfos()) {
581             if (!fabricatedPaths.contains(info.path)) {
582                 mIdmapManager.deleteFabricatedOverlay(info.path);
583             }
584         }
585     }
586 
587     /**
588      * Retrieves information about the fabricated overlays still in use.
589      * @return
590      */
591     @NonNull
getFabricatedOverlayInfos()592     private List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
593         final Set<String> fabricatedPaths = mSettings.getAllBaseCodePaths();
594         // Filter out stale fabricated overlays.
595         final ArrayList<FabricatedOverlayInfo> infos = new ArrayList<>(
596                 mIdmapManager.getFabricatedOverlayInfos());
597         infos.removeIf(info -> !fabricatedPaths.contains(info.path));
598         return infos;
599     }
600 
isPackageConfiguredMutable(@onNull final AndroidPackage overlay)601     private boolean isPackageConfiguredMutable(@NonNull final AndroidPackage overlay) {
602         // TODO(162841629): Support overlay name in OverlayConfig
603         return mOverlayConfig.isMutable(overlay.getPackageName());
604     }
605 
getPackageConfiguredPriority(@onNull final AndroidPackage overlay)606     private int getPackageConfiguredPriority(@NonNull final AndroidPackage overlay) {
607         // TODO(162841629): Support overlay name in OverlayConfig
608         return mOverlayConfig.getPriority(overlay.getPackageName());
609     }
610 
isPackageConfiguredEnabled(@onNull final AndroidPackage overlay)611     private boolean isPackageConfiguredEnabled(@NonNull final AndroidPackage overlay) {
612         // TODO(162841629): Support overlay name in OverlayConfig
613         return mOverlayConfig.isEnabled(overlay.getPackageName());
614     }
615 
setPriority(@onNull final OverlayIdentifier overlay, @NonNull final OverlayIdentifier newParentOverlay, final int userId)616     Optional<PackageAndUser> setPriority(@NonNull final OverlayIdentifier overlay,
617             @NonNull final OverlayIdentifier newParentOverlay, final int userId)
618             throws OperationFailedException {
619         try {
620             if (DEBUG) {
621                 Slog.d(TAG, "setPriority overlay=" + overlay + " newParentOverlay="
622                         + newParentOverlay + " userId=" + userId);
623             }
624 
625             final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId);
626             if (!overlayInfo.isMutable) {
627                 // Ignore immutable overlays.
628                 throw new OperationFailedException(
629                         "cannot change priority of an immutable overlay package at runtime");
630             }
631 
632             if (mSettings.setPriority(overlay, newParentOverlay, userId)) {
633                 return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
634             }
635             return Optional.empty();
636         } catch (OverlayManagerSettings.BadKeyException e) {
637             throw new OperationFailedException("failed to update settings", e);
638         }
639     }
640 
setHighestPriority(@onNull final OverlayIdentifier overlay, final int userId)641     Set<PackageAndUser> setHighestPriority(@NonNull final OverlayIdentifier overlay,
642             final int userId) throws OperationFailedException {
643         try{
644             if (DEBUG) {
645                 Slog.d(TAG, "setHighestPriority overlay=" + overlay + " userId=" + userId);
646             }
647 
648             final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId);
649             if (!overlayInfo.isMutable) {
650                 // Ignore immutable overlays.
651                 throw new OperationFailedException(
652                         "cannot change priority of an immutable overlay package at runtime");
653             }
654 
655             if (mSettings.setHighestPriority(overlay, userId)) {
656                 return Set.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
657             }
658             return Set.of();
659         } catch (OverlayManagerSettings.BadKeyException e) {
660             throw new OperationFailedException("failed to update settings", e);
661         }
662     }
663 
setLowestPriority(@onNull final OverlayIdentifier overlay, final int userId)664     Optional<PackageAndUser> setLowestPriority(@NonNull final OverlayIdentifier overlay,
665             final int userId) throws OperationFailedException {
666         try{
667             if (DEBUG) {
668                 Slog.d(TAG, "setLowestPriority packageName=" + overlay + " userId=" + userId);
669             }
670 
671             final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId);
672             if (!overlayInfo.isMutable) {
673                 // Ignore immutable overlays.
674                 throw new OperationFailedException(
675                         "cannot change priority of an immutable overlay package at runtime");
676             }
677 
678             if (mSettings.setLowestPriority(overlay, userId)) {
679                 return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
680             }
681             return Optional.empty();
682         } catch (OverlayManagerSettings.BadKeyException e) {
683             throw new OperationFailedException("failed to update settings", e);
684         }
685     }
686 
dump(@onNull final PrintWriter pw, @NonNull DumpState dumpState)687     void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
688         Pair<OverlayIdentifier, String> overlayIdmap = null;
689         if (dumpState.getPackageName() != null) {
690             OverlayIdentifier id = new OverlayIdentifier(dumpState.getPackageName(),
691                     dumpState.getOverlayName());
692             OverlayInfo oi = mSettings.getNullableOverlayInfo(id, USER_SYSTEM);
693             if (oi != null) {
694                 overlayIdmap = new Pair<>(id, oi.baseCodePath);
695             }
696         }
697 
698         // settings
699         mSettings.dump(pw, dumpState);
700 
701         // idmap data
702         if (dumpState.getField() == null) {
703             Set<Pair<OverlayIdentifier, String>> allIdmaps = (overlayIdmap != null)
704                     ? Set.of(overlayIdmap) : mSettings.getAllIdentifiersAndBaseCodePaths();
705             for (Pair<OverlayIdentifier, String> pair : allIdmaps) {
706                 pw.println("IDMAP OF " + pair.first);
707                 String dump = mIdmapManager.dumpIdmap(pair.second);
708                 if (dump != null) {
709                     pw.println(dump);
710                 } else {
711                     OverlayInfo oi = mSettings.getNullableOverlayInfo(pair.first, USER_SYSTEM);
712                     pw.println((oi != null && !mIdmapManager.idmapExists(oi))
713                             ? "<missing idmap>" : "<internal error>");
714                 }
715             }
716         }
717 
718         // default overlays
719         if (overlayIdmap == null) {
720             pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
721         }
722 
723         // overlay configurations
724         if (dumpState.getPackageName() == null) {
725             mOverlayConfig.dump(pw);
726         }
727     }
728 
getDefaultOverlayPackages()729     @NonNull String[] getDefaultOverlayPackages() {
730         return mDefaultOverlays;
731     }
732 
removeIdmapForOverlay(OverlayIdentifier overlay, int userId)733     void removeIdmapForOverlay(OverlayIdentifier overlay, int userId)
734             throws OperationFailedException {
735         try {
736             final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId);
737             removeIdmapIfPossible(oi);
738         } catch (OverlayManagerSettings.BadKeyException e) {
739             throw new OperationFailedException("failed to update settings", e);
740         }
741     }
742 
getEnabledOverlayPaths(@onNull final String targetPackageName, final int userId)743     OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName,
744             final int userId) {
745         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
746                 userId);
747         final OverlayPaths.Builder paths = new OverlayPaths.Builder();
748         final int n = overlays.size();
749         for (int i = 0; i < n; i++) {
750             final OverlayInfo oi = overlays.get(i);
751             if (!oi.isEnabled()) {
752                 continue;
753             }
754             if (oi.isFabricated()) {
755                 paths.addNonApkPath(oi.baseCodePath);
756             } else {
757                 paths.addApkPath(oi.baseCodePath);
758             }
759         }
760         return paths.build();
761     }
762 
763     /**
764      * Returns true if the settings/state was modified, false otherwise.
765      */
updateState(@onNull final CriticalOverlayInfo info, final int userId, final int flags)766     private boolean updateState(@NonNull final CriticalOverlayInfo info,
767             final int userId, final int flags) throws OverlayManagerSettings.BadKeyException {
768         final OverlayIdentifier overlay = info.getOverlayIdentifier();
769         final AndroidPackage targetPackage = mPackageManager.getPackageForUser(
770                 info.getTargetPackageName(), userId);
771         final AndroidPackage overlayPackage = mPackageManager.getPackageForUser(
772                 info.getPackageName(), userId);
773 
774         boolean modified = false;
775         if (overlayPackage == null) {
776             removeIdmapIfPossible(mSettings.getOverlayInfo(overlay, userId));
777             return mSettings.remove(overlay, userId);
778         }
779 
780         modified |= mSettings.setCategory(overlay, userId, overlayPackage.getOverlayCategory());
781         if (!info.isFabricated()) {
782             modified |= mSettings.setBaseCodePath(overlay, userId, overlayPackage.getBaseApkPath());
783         }
784 
785         // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native
786         // layers.
787         final OverlayInfo updatedOverlayInfo = mSettings.getOverlayInfo(overlay, userId);
788         if (targetPackage != null && !("android".equals(info.getTargetPackageName())
789                 && !isPackageConfiguredMutable(overlayPackage))) {
790             modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage,
791                     updatedOverlayInfo.baseCodePath, overlay.getOverlayName(), userId);
792         }
793 
794         final @OverlayInfo.State int currentState = mSettings.getState(overlay, userId);
795         final @OverlayInfo.State int newState = calculateNewState(updatedOverlayInfo, targetPackage,
796                 userId, flags);
797         if (currentState != newState) {
798             if (DEBUG) {
799                 Slog.d(TAG, String.format("%s:%d: %s -> %s",
800                         overlay, userId,
801                         OverlayInfo.stateToString(currentState),
802                         OverlayInfo.stateToString(newState)));
803             }
804             modified |= mSettings.setState(overlay, userId, newState);
805         }
806 
807         return modified;
808     }
809 
calculateNewState(@onNull final OverlayInfo info, @Nullable final AndroidPackage targetPackage, final int userId, final int flags)810     private @OverlayInfo.State int calculateNewState(@NonNull final OverlayInfo info,
811             @Nullable final AndroidPackage targetPackage, final int userId, final int flags)
812             throws OverlayManagerSettings.BadKeyException {
813         if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) {
814             return STATE_TARGET_IS_BEING_REPLACED;
815         }
816 
817         if ((flags & FLAG_OVERLAY_IS_BEING_REPLACED) != 0) {
818             return STATE_OVERLAY_IS_BEING_REPLACED;
819         }
820 
821         if (targetPackage == null) {
822             return STATE_MISSING_TARGET;
823         }
824 
825         if (!mIdmapManager.idmapExists(info)) {
826             return STATE_NO_IDMAP;
827         }
828 
829         final boolean enabled = mSettings.getEnabled(info.getOverlayIdentifier(), userId);
830         return enabled ? STATE_ENABLED : STATE_DISABLED;
831     }
832 
removeIdmapIfPossible(@onNull final OverlayInfo oi)833     private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) {
834         // For a given package, all Android users share the same idmap file.
835         // This works because Android currently does not support users to
836         // install different versions of the same package. It also means we
837         // cannot remove an idmap file if any user still needs it.
838         //
839         // When/if the Android framework allows different versions of the same
840         // package to be installed for different users, idmap file handling
841         // should be revised:
842         //
843         // - an idmap file should be unique for each {user, package} pair
844         //
845         // - the path to the idmap file should be passed to the native Asset
846         //   Manager layers, just like the path to the apk is passed today
847         //
848         // As part of that change, calls to this method should be replaced by
849         // direct calls to IdmapManager.removeIdmap, without looping over all
850         // users.
851 
852         if (!mIdmapManager.idmapExists(oi)) {
853             return;
854         }
855         final int[] userIds = mSettings.getUsers();
856         for (int userId : userIds) {
857             try {
858                 final OverlayInfo tmp = mSettings.getOverlayInfo(oi.getOverlayIdentifier(), userId);
859                 if (tmp != null && tmp.isEnabled()) {
860                     // someone is still using the idmap file -> we cannot remove it
861                     return;
862                 }
863             } catch (OverlayManagerSettings.BadKeyException e) {
864                 // intentionally left empty
865             }
866         }
867         mIdmapManager.removeIdmap(oi, oi.userId);
868     }
869 
870     static final class OperationFailedException extends Exception {
OperationFailedException(@onNull final String message)871         OperationFailedException(@NonNull final String message) {
872             super(message);
873         }
874 
OperationFailedException(@onNull final String message, @NonNull Throwable cause)875         OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
876             super(message, cause);
877         }
878     }
879 }
880