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