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