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