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_ENABLED_STATIC; 22 import static android.content.om.OverlayInfo.STATE_MISSING_TARGET; 23 import static android.content.om.OverlayInfo.STATE_NO_IDMAP; 24 import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED; 25 import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED; 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.OverlayInfo; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.PackageInfo; 35 import android.text.TextUtils; 36 import android.util.ArrayMap; 37 import android.util.ArraySet; 38 import android.util.Slog; 39 40 import com.android.internal.util.ArrayUtils; 41 42 import java.io.PrintWriter; 43 import java.util.ArrayList; 44 import java.util.Iterator; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.Objects; 48 import java.util.Set; 49 50 /** 51 * Internal implementation of OverlayManagerService. 52 * 53 * Methods in this class should only be called by the OverlayManagerService. 54 * This class is not thread-safe; the caller is expected to ensure the 55 * necessary thread synchronization. 56 * 57 * @see OverlayManagerService 58 */ 59 final class OverlayManagerServiceImpl { 60 /** 61 * @deprecated Not used. See {@link android.content.om.OverlayInfo#STATE_TARGET_UPGRADING}. 62 */ 63 @Deprecated 64 private static final int FLAG_TARGET_IS_BEING_REPLACED = 1 << 0; 65 66 // Flags to use in conjunction with updateState. 67 private static final int FLAG_OVERLAY_IS_BEING_REPLACED = 1 << 1; 68 69 private final PackageManagerHelper mPackageManager; 70 private final IdmapManager mIdmapManager; 71 private final OverlayManagerSettings mSettings; 72 private final String[] mDefaultOverlays; 73 private final OverlayChangeListener mListener; 74 75 /** 76 * Helper method to merge the overlay manager's (as read from overlays.xml) 77 * and package manager's (as parsed from AndroidManifest.xml files) views 78 * on overlays. 79 * 80 * Both managers are usually in agreement, but especially after an OTA things 81 * may differ. The package manager is always providing the truth; the overlay 82 * manager has to adapt. Depending on what has changed about an overlay, we 83 * should either scrap the overlay manager's previous settings or merge the old 84 * settings with the new. 85 */ mustReinitializeOverlay(@onNull final PackageInfo theTruth, @Nullable final OverlayInfo oldSettings)86 private static boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth, 87 @Nullable final OverlayInfo oldSettings) { 88 if (oldSettings == null) { 89 return true; 90 } 91 if (!Objects.equals(theTruth.overlayTarget, oldSettings.targetPackageName)) { 92 return true; 93 } 94 if (!Objects.equals(theTruth.targetOverlayableName, oldSettings.targetOverlayableName)) { 95 return true; 96 } 97 if (theTruth.isStaticOverlayPackage() != oldSettings.isStatic) { 98 return true; 99 } 100 // a change in priority is only relevant for static RROs: specifically, 101 // a regular RRO should not have its state reset only because a change 102 // in priority 103 if (theTruth.isStaticOverlayPackage() 104 && theTruth.overlayPriority != oldSettings.priority) { 105 return true; 106 } 107 return false; 108 } 109 OverlayManagerServiceImpl(@onNull final PackageManagerHelper packageManager, @NonNull final IdmapManager idmapManager, @NonNull final OverlayManagerSettings settings, @NonNull final String[] defaultOverlays, @NonNull final OverlayChangeListener listener)110 OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager, 111 @NonNull final IdmapManager idmapManager, 112 @NonNull final OverlayManagerSettings settings, 113 @NonNull final String[] defaultOverlays, 114 @NonNull final OverlayChangeListener listener) { 115 mPackageManager = packageManager; 116 mIdmapManager = idmapManager; 117 mSettings = settings; 118 mDefaultOverlays = defaultOverlays; 119 mListener = listener; 120 } 121 122 /** 123 * Call this to synchronize the Settings for a user with what PackageManager knows about a user. 124 * Returns a list of target packages that must refresh their overlays. This list is the union 125 * of two sets: the set of targets with currently active overlays, and the 126 * set of targets that had, but no longer have, active overlays. 127 */ updateOverlaysForUser(final int newUserId)128 ArrayList<String> updateOverlaysForUser(final int newUserId) { 129 if (DEBUG) { 130 Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId); 131 } 132 133 final Set<String> packagesToUpdateAssets = new ArraySet<>(); 134 final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId); 135 final int tmpSize = tmp.size(); 136 final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize); 137 for (int i = 0; i < tmpSize; i++) { 138 final List<OverlayInfo> chunk = tmp.valueAt(i); 139 final int chunkSize = chunk.size(); 140 for (int j = 0; j < chunkSize; j++) { 141 final OverlayInfo oi = chunk.get(j); 142 storedOverlayInfos.put(oi.packageName, oi); 143 } 144 } 145 146 // Reset overlays if something critical like the target package name 147 // has changed 148 List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId); 149 final int overlayPackagesSize = overlayPackages.size(); 150 for (int i = 0; i < overlayPackagesSize; i++) { 151 final PackageInfo overlayPackage = overlayPackages.get(i); 152 final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName); 153 154 if (mustReinitializeOverlay(overlayPackage, oi)) { 155 // if targetPackageName has changed the package that *used* to 156 // be the target must also update its assets 157 if (oi != null) { 158 packagesToUpdateAssets.add(oi.targetPackageName); 159 } 160 161 mSettings.init(overlayPackage.packageName, newUserId, 162 overlayPackage.overlayTarget, 163 overlayPackage.targetOverlayableName, 164 overlayPackage.applicationInfo.getBaseCodePath(), 165 overlayPackage.isStaticOverlayPackage(), 166 overlayPackage.overlayPriority, 167 overlayPackage.overlayCategory); 168 } 169 170 storedOverlayInfos.remove(overlayPackage.packageName); 171 } 172 173 // any OverlayInfo left in storedOverlayInfos is no longer 174 // installed and should be removed 175 final int storedOverlayInfosSize = storedOverlayInfos.size(); 176 for (int i = 0; i < storedOverlayInfosSize; i++) { 177 final OverlayInfo oi = storedOverlayInfos.valueAt(i); 178 mSettings.remove(oi.packageName, oi.userId); 179 removeIdmapIfPossible(oi); 180 packagesToUpdateAssets.add(oi.targetPackageName); 181 } 182 183 // make sure every overlay's state is up-to-date; this needs to happen 184 // after old overlays have been removed, or we risk removing a 185 // legitimate idmap file if a new overlay package has the same apk path 186 // as the removed overlay package used to have 187 for (int i = 0; i < overlayPackagesSize; i++) { 188 final PackageInfo overlayPackage = overlayPackages.get(i); 189 try { 190 updateState(overlayPackage.overlayTarget, overlayPackage.packageName, 191 newUserId, 0); 192 } catch (OverlayManagerSettings.BadKeyException e) { 193 Slog.e(TAG, "failed to update settings", e); 194 mSettings.remove(overlayPackage.packageName, newUserId); 195 } 196 packagesToUpdateAssets.add(overlayPackage.overlayTarget); 197 } 198 199 // remove target packages that are not installed 200 final Iterator<String> iter = packagesToUpdateAssets.iterator(); 201 while (iter.hasNext()) { 202 String targetPackageName = iter.next(); 203 if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) { 204 iter.remove(); 205 } 206 } 207 208 // Collect all of the categories in which we have at least one overlay enabled. 209 final ArraySet<String> enabledCategories = new ArraySet<>(); 210 final ArrayMap<String, List<OverlayInfo>> userOverlays = 211 mSettings.getOverlaysForUser(newUserId); 212 final int userOverlayTargetCount = userOverlays.size(); 213 for (int i = 0; i < userOverlayTargetCount; i++) { 214 final List<OverlayInfo> overlayList = userOverlays.valueAt(i); 215 final int overlayCount = overlayList != null ? overlayList.size() : 0; 216 for (int j = 0; j < overlayCount; j++) { 217 final OverlayInfo oi = overlayList.get(j); 218 if (oi.isEnabled()) { 219 enabledCategories.add(oi.category); 220 } 221 } 222 } 223 224 // Enable the default overlay if its category does not have a single overlay enabled. 225 for (final String defaultOverlay : mDefaultOverlays) { 226 try { 227 final OverlayInfo oi = mSettings.getOverlayInfo(defaultOverlay, newUserId); 228 if (!enabledCategories.contains(oi.category)) { 229 Slog.w(TAG, "Enabling default overlay '" + defaultOverlay + "' for target '" 230 + oi.targetPackageName + "' in category '" + oi.category + "' for user " 231 + newUserId); 232 mSettings.setEnabled(oi.packageName, newUserId, true); 233 if (updateState(oi.targetPackageName, oi.packageName, newUserId, 0)) { 234 packagesToUpdateAssets.add(oi.targetPackageName); 235 } 236 } 237 } catch (OverlayManagerSettings.BadKeyException e) { 238 Slog.e(TAG, "Failed to set default overlay '" + defaultOverlay + "' for user " 239 + newUserId, e); 240 } 241 } 242 243 return new ArrayList<>(packagesToUpdateAssets); 244 } 245 onUserRemoved(final int userId)246 void onUserRemoved(final int userId) { 247 if (DEBUG) { 248 Slog.d(TAG, "onUserRemoved userId=" + userId); 249 } 250 mSettings.removeUser(userId); 251 } 252 onTargetPackageAdded(@onNull final String packageName, final int userId)253 void onTargetPackageAdded(@NonNull final String packageName, final int userId) { 254 if (DEBUG) { 255 Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId); 256 } 257 258 updateAndRefreshOverlaysForTarget(packageName, userId, 0); 259 } 260 onTargetPackageChanged(@onNull final String packageName, final int userId)261 void onTargetPackageChanged(@NonNull final String packageName, final int userId) { 262 if (DEBUG) { 263 Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); 264 } 265 266 updateAndRefreshOverlaysForTarget(packageName, userId, 0); 267 } 268 onTargetPackageReplacing(@onNull final String packageName, final int userId)269 void onTargetPackageReplacing(@NonNull final String packageName, final int userId) { 270 if (DEBUG) { 271 Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId=" 272 + userId); 273 } 274 275 updateAndRefreshOverlaysForTarget(packageName, userId, 0); 276 } 277 onTargetPackageReplaced(@onNull final String packageName, final int userId)278 void onTargetPackageReplaced(@NonNull final String packageName, final int userId) { 279 if (DEBUG) { 280 Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId); 281 } 282 283 updateAndRefreshOverlaysForTarget(packageName, userId, 0); 284 } 285 onTargetPackageRemoved(@onNull final String packageName, final int userId)286 void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { 287 if (DEBUG) { 288 Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); 289 } 290 291 updateAndRefreshOverlaysForTarget(packageName, userId, 0); 292 } 293 294 /** 295 * Update the state of any overlays for this target. 296 */ updateAndRefreshOverlaysForTarget(@onNull final String targetPackageName, final int userId, final int flags)297 private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName, 298 final int userId, final int flags) { 299 final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName, 300 userId); 301 302 // Update the state for any overlay that targets this package. 303 boolean modified = false; 304 for (final OverlayInfo oi : targetOverlays) { 305 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, 306 userId); 307 if (overlayPackage == null) { 308 modified |= mSettings.remove(oi.packageName, oi.userId); 309 removeIdmapIfPossible(oi); 310 } else { 311 try { 312 modified |= updateState(targetPackageName, oi.packageName, userId, flags); 313 } catch (OverlayManagerSettings.BadKeyException e) { 314 Slog.e(TAG, "failed to update settings", e); 315 modified |= mSettings.remove(oi.packageName, userId); 316 } 317 } 318 } 319 320 if (!modified) { 321 // Update the overlay paths of the target within package manager if necessary. 322 final List<String> enabledOverlayPaths = new ArrayList<>(targetOverlays.size()); 323 324 // Framework overlays are first in the overlay paths of a package within PackageManager. 325 for (final OverlayInfo oi : mSettings.getOverlaysForTarget("android", userId)) { 326 if (oi.isEnabled()) { 327 enabledOverlayPaths.add(oi.baseCodePath); 328 } 329 } 330 331 for (final OverlayInfo oi : targetOverlays) { 332 if (oi.isEnabled()) { 333 enabledOverlayPaths.add(oi.baseCodePath); 334 } 335 } 336 337 // TODO(): Use getEnabledOverlayPaths(userId, targetPackageName) instead of 338 // resourceDirs if in the future resourceDirs contains APKs other than overlays 339 PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId); 340 ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo; 341 String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs; 342 343 // If the lists aren't the same length, the enabled overlays have changed 344 if (ArrayUtils.size(resourceDirs) != enabledOverlayPaths.size()) { 345 modified = true; 346 } else if (resourceDirs != null) { 347 // If any element isn't equal, an overlay or the order of overlays has changed 348 for (int index = 0; index < resourceDirs.length; index++) { 349 if (!resourceDirs[index].equals(enabledOverlayPaths.get(index))) { 350 modified = true; 351 break; 352 } 353 } 354 } 355 } 356 357 if (modified) { 358 mListener.onOverlaysChanged(targetPackageName, userId); 359 } 360 } 361 onOverlayPackageAdded(@onNull final String packageName, final int userId)362 void onOverlayPackageAdded(@NonNull final String packageName, final int userId) { 363 if (DEBUG) { 364 Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId); 365 } 366 367 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 368 if (overlayPackage == null) { 369 Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found"); 370 onOverlayPackageRemoved(packageName, userId); 371 return; 372 } 373 374 mSettings.init(packageName, userId, overlayPackage.overlayTarget, 375 overlayPackage.targetOverlayableName, 376 overlayPackage.applicationInfo.getBaseCodePath(), 377 overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority, 378 overlayPackage.overlayCategory); 379 try { 380 if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) { 381 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 382 } 383 } catch (OverlayManagerSettings.BadKeyException e) { 384 Slog.e(TAG, "failed to update settings", e); 385 mSettings.remove(packageName, userId); 386 } 387 } 388 onOverlayPackageChanged(@onNull final String packageName, final int userId)389 void onOverlayPackageChanged(@NonNull final String packageName, final int userId) { 390 if (DEBUG) { 391 Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId); 392 } 393 394 try { 395 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); 396 if (updateState(oi.targetPackageName, packageName, userId, 0)) { 397 mListener.onOverlaysChanged(oi.targetPackageName, userId); 398 } 399 } catch (OverlayManagerSettings.BadKeyException e) { 400 Slog.e(TAG, "failed to update settings", e); 401 } 402 } 403 onOverlayPackageReplacing(@onNull final String packageName, final int userId)404 void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) { 405 if (DEBUG) { 406 Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId=" 407 + userId); 408 } 409 410 try { 411 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); 412 if (updateState(oi.targetPackageName, packageName, userId, 413 FLAG_OVERLAY_IS_BEING_REPLACED)) { 414 removeIdmapIfPossible(oi); 415 mListener.onOverlaysChanged(oi.targetPackageName, userId); 416 } 417 } catch (OverlayManagerSettings.BadKeyException e) { 418 Slog.e(TAG, "failed to update settings", e); 419 } 420 } 421 onOverlayPackageReplaced(@onNull final String packageName, final int userId)422 void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) { 423 if (DEBUG) { 424 Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId=" 425 + userId); 426 } 427 428 final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId); 429 if (pkg == null) { 430 Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found"); 431 onOverlayPackageRemoved(packageName, userId); 432 return; 433 } 434 435 try { 436 final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId); 437 if (mustReinitializeOverlay(pkg, oldOi)) { 438 if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) { 439 mListener.onOverlaysChanged(pkg.overlayTarget, userId); 440 } 441 mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName, 442 pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(), 443 pkg.overlayPriority, pkg.overlayCategory); 444 } 445 446 if (updateState(pkg.overlayTarget, packageName, userId, 0)) { 447 mListener.onOverlaysChanged(pkg.overlayTarget, userId); 448 } 449 } catch (OverlayManagerSettings.BadKeyException e) { 450 Slog.e(TAG, "failed to update settings", e); 451 } 452 } 453 onOverlayPackageRemoved(@onNull final String packageName, final int userId)454 void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) { 455 try { 456 final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId); 457 if (mSettings.remove(packageName, userId)) { 458 removeIdmapIfPossible(overlayInfo); 459 mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId); 460 } 461 } catch (OverlayManagerSettings.BadKeyException e) { 462 Slog.e(TAG, "failed to remove overlay", e); 463 } 464 } 465 getOverlayInfo(@onNull final String packageName, final int userId)466 OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) { 467 try { 468 return mSettings.getOverlayInfo(packageName, userId); 469 } catch (OverlayManagerSettings.BadKeyException e) { 470 return null; 471 } 472 } 473 getOverlayInfosForTarget(@onNull final String targetPackageName, final int userId)474 List<OverlayInfo> getOverlayInfosForTarget(@NonNull final String targetPackageName, 475 final int userId) { 476 return mSettings.getOverlaysForTarget(targetPackageName, userId); 477 } 478 getOverlaysForUser(final int userId)479 Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { 480 return mSettings.getOverlaysForUser(userId); 481 } 482 setEnabled(@onNull final String packageName, final boolean enable, final int userId)483 boolean setEnabled(@NonNull final String packageName, final boolean enable, 484 final int userId) { 485 if (DEBUG) { 486 Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d", 487 packageName, enable, userId)); 488 } 489 490 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 491 if (overlayPackage == null) { 492 return false; 493 } 494 495 // Ignore static overlays. 496 if (overlayPackage.isStaticOverlayPackage()) { 497 return false; 498 } 499 500 try { 501 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); 502 boolean modified = mSettings.setEnabled(packageName, userId, enable); 503 modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0); 504 505 if (modified) { 506 mListener.onOverlaysChanged(oi.targetPackageName, userId); 507 } 508 return true; 509 } catch (OverlayManagerSettings.BadKeyException e) { 510 return false; 511 } 512 } 513 setEnabledExclusive(@onNull final String packageName, boolean withinCategory, final int userId)514 boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory, 515 final int userId) { 516 if (DEBUG) { 517 Slog.d(TAG, String.format("setEnabledExclusive packageName=%s" 518 + " withinCategory=%s userId=%d", packageName, withinCategory, userId)); 519 } 520 521 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 522 if (overlayPackage == null) { 523 return false; 524 } 525 526 try { 527 final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); 528 final String targetPackageName = oi.targetPackageName; 529 530 List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId); 531 532 boolean modified = false; 533 534 // Disable all other overlays. 535 allOverlays.remove(oi); 536 for (int i = 0; i < allOverlays.size(); i++) { 537 final String disabledOverlayPackageName = allOverlays.get(i).packageName; 538 final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo( 539 disabledOverlayPackageName, userId); 540 if (disabledOverlayPackageInfo == null) { 541 modified |= mSettings.remove(disabledOverlayPackageName, userId); 542 continue; 543 } 544 545 if (disabledOverlayPackageInfo.isStaticOverlayPackage()) { 546 // Don't touch static overlays. 547 continue; 548 } 549 if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory, 550 oi.category)) { 551 // Don't touch overlays from other categories. 552 continue; 553 } 554 555 // Disable the overlay. 556 modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false); 557 modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0); 558 } 559 560 // Enable the selected overlay. 561 modified |= mSettings.setEnabled(packageName, userId, true); 562 modified |= updateState(targetPackageName, packageName, userId, 0); 563 564 if (modified) { 565 mListener.onOverlaysChanged(targetPackageName, userId); 566 } 567 return true; 568 } catch (OverlayManagerSettings.BadKeyException e) { 569 return false; 570 } 571 } 572 isPackageUpdatableOverlay(@onNull final String packageName, final int userId)573 private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) { 574 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 575 if (overlayPackage == null || overlayPackage.isStaticOverlayPackage()) { 576 return false; 577 } 578 return true; 579 } 580 setPriority(@onNull final String packageName, @NonNull final String newParentPackageName, final int userId)581 boolean setPriority(@NonNull final String packageName, 582 @NonNull final String newParentPackageName, final int userId) { 583 if (DEBUG) { 584 Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName=" 585 + newParentPackageName + " userId=" + userId); 586 } 587 588 if (!isPackageUpdatableOverlay(packageName, userId)) { 589 return false; 590 } 591 592 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 593 if (overlayPackage == null) { 594 return false; 595 } 596 597 if (mSettings.setPriority(packageName, newParentPackageName, userId)) { 598 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 599 } 600 return true; 601 } 602 setHighestPriority(@onNull final String packageName, final int userId)603 boolean setHighestPriority(@NonNull final String packageName, final int userId) { 604 if (DEBUG) { 605 Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId); 606 } 607 608 if (!isPackageUpdatableOverlay(packageName, userId)) { 609 return false; 610 } 611 612 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 613 if (overlayPackage == null) { 614 return false; 615 } 616 617 if (mSettings.setHighestPriority(packageName, userId)) { 618 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 619 } 620 return true; 621 } 622 setLowestPriority(@onNull final String packageName, final int userId)623 boolean setLowestPriority(@NonNull final String packageName, final int userId) { 624 if (DEBUG) { 625 Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId); 626 } 627 628 if (!isPackageUpdatableOverlay(packageName, userId)) { 629 return false; 630 } 631 632 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); 633 if (overlayPackage == null) { 634 return false; 635 } 636 637 if (mSettings.setLowestPriority(packageName, userId)) { 638 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); 639 } 640 return true; 641 } 642 dump(@onNull final PrintWriter pw, @NonNull DumpState dumpState)643 void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { 644 mSettings.dump(pw, dumpState); 645 if (dumpState.getPackageName() == null) { 646 pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays)); 647 } 648 } 649 getDefaultOverlayPackages()650 @NonNull String[] getDefaultOverlayPackages() { 651 return mDefaultOverlays; 652 } 653 getEnabledOverlayPackageNames(@onNull final String targetPackageName, final int userId)654 List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName, 655 final int userId) { 656 final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, 657 userId); 658 final List<String> paths = new ArrayList<>(overlays.size()); 659 final int n = overlays.size(); 660 for (int i = 0; i < n; i++) { 661 final OverlayInfo oi = overlays.get(i); 662 if (oi.isEnabled()) { 663 paths.add(oi.packageName); 664 } 665 } 666 return paths; 667 } 668 669 /** 670 * Returns true if the settings/state was modified, false otherwise. 671 */ updateState(@onNull final String targetPackageName, @NonNull final String overlayPackageName, final int userId, final int flags)672 private boolean updateState(@NonNull final String targetPackageName, 673 @NonNull final String overlayPackageName, final int userId, final int flags) 674 throws OverlayManagerSettings.BadKeyException { 675 676 final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId); 677 final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName, 678 userId); 679 680 // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. 681 if (targetPackage != null && overlayPackage != null 682 && !("android".equals(targetPackageName) 683 && overlayPackage.isStaticOverlayPackage())) { 684 mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); 685 } 686 687 boolean modified = false; 688 if (overlayPackage != null) { 689 modified |= mSettings.setBaseCodePath(overlayPackageName, userId, 690 overlayPackage.applicationInfo.getBaseCodePath()); 691 modified |= mSettings.setCategory(overlayPackageName, userId, 692 overlayPackage.overlayCategory); 693 } 694 695 final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId); 696 final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage, 697 userId, flags); 698 if (currentState != newState) { 699 if (DEBUG) { 700 Slog.d(TAG, String.format("%s:%d: %s -> %s", 701 overlayPackageName, userId, 702 OverlayInfo.stateToString(currentState), 703 OverlayInfo.stateToString(newState))); 704 } 705 modified |= mSettings.setState(overlayPackageName, userId, newState); 706 } 707 return modified; 708 } 709 calculateNewState(@ullable final PackageInfo targetPackage, @Nullable final PackageInfo overlayPackage, final int userId, final int flags)710 private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage, 711 @Nullable final PackageInfo overlayPackage, final int userId, final int flags) 712 throws OverlayManagerSettings.BadKeyException { 713 714 if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) { 715 return STATE_TARGET_IS_BEING_REPLACED; 716 } 717 718 if ((flags & FLAG_OVERLAY_IS_BEING_REPLACED) != 0) { 719 return STATE_OVERLAY_IS_BEING_REPLACED; 720 } 721 722 // assert expectation on overlay package: can only be null if the flags are used 723 if (DEBUG && overlayPackage == null) { 724 throw new IllegalArgumentException("null overlay package not compatible with no flags"); 725 } 726 727 if (targetPackage == null) { 728 return STATE_MISSING_TARGET; 729 } 730 731 if (!mIdmapManager.idmapExists(overlayPackage, userId)) { 732 return STATE_NO_IDMAP; 733 } 734 735 if (overlayPackage.isStaticOverlayPackage()) { 736 return STATE_ENABLED_STATIC; 737 } 738 739 final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId); 740 return enabled ? STATE_ENABLED : STATE_DISABLED; 741 } 742 removeIdmapIfPossible(@onNull final OverlayInfo oi)743 private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) { 744 // For a given package, all Android users share the same idmap file. 745 // This works because Android currently does not support users to 746 // install different versions of the same package. It also means we 747 // cannot remove an idmap file if any user still needs it. 748 // 749 // When/if the Android framework allows different versions of the same 750 // package to be installed for different users, idmap file handling 751 // should be revised: 752 // 753 // - an idmap file should be unique for each {user, package} pair 754 // 755 // - the path to the idmap file should be passed to the native Asset 756 // Manager layers, just like the path to the apk is passed today 757 // 758 // As part of that change, calls to this method should be replaced by 759 // direct calls to IdmapManager.removeIdmap, without looping over all 760 // users. 761 762 if (!mIdmapManager.idmapExists(oi)) { 763 return; 764 } 765 final int[] userIds = mSettings.getUsers(); 766 for (int userId : userIds) { 767 try { 768 final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId); 769 if (tmp != null && tmp.isEnabled()) { 770 // someone is still using the idmap file -> we cannot remove it 771 return; 772 } 773 } catch (OverlayManagerSettings.BadKeyException e) { 774 // intentionally left empty 775 } 776 } 777 mIdmapManager.removeIdmap(oi, oi.userId); 778 } 779 780 interface OverlayChangeListener { 781 782 /** 783 * An event triggered by changes made to overlay state or settings as well as changes that 784 * add or remove target packages of overlays. 785 **/ onOverlaysChanged(@onNull String targetPackage, int userId)786 void onOverlaysChanged(@NonNull String targetPackage, int userId); 787 } 788 789 interface PackageManagerHelper { getPackageInfo(@onNull String packageName, int userId)790 PackageInfo getPackageInfo(@NonNull String packageName, int userId); signaturesMatching(@onNull String packageName1, @NonNull String packageName2, int userId)791 boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2, 792 int userId); getOverlayPackages(int userId)793 List<PackageInfo> getOverlayPackages(int userId); 794 } 795 } 796