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