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