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