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