1 /* 2 * Copyright (C) 2015 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 android.content.om; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.annotation.UserIdInt; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.os.Build; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 import java.util.Objects; 34 35 /** 36 * An immutable information about an overlay. 37 * 38 * <p>Applications calling {@link OverlayManager#getOverlayInfosForTarget(String)} get the 39 * information list of the registered overlays. Each element in the list presents the information of 40 * the particular overlay. 41 * 42 * <!-- For OverlayManagerService, it isn't public part and hidden by HTML comment. --> 43 * <!-- 44 * Immutable overlay information about a package. All PackageInfos that 45 * represent an overlay package will have a corresponding OverlayInfo. 46 * --> 47 * 48 * @see OverlayManager#getOverlayInfosForTarget(String) 49 */ 50 public final class OverlayInfo implements CriticalOverlayInfo, Parcelable { 51 52 /** @hide */ 53 @IntDef(prefix = "STATE_", value = { 54 STATE_UNKNOWN, 55 STATE_MISSING_TARGET, 56 STATE_NO_IDMAP, 57 STATE_DISABLED, 58 STATE_ENABLED, 59 STATE_ENABLED_IMMUTABLE, 60 STATE_OVERLAY_IS_BEING_REPLACED, 61 STATE_SYSTEM_UPDATE_UNINSTALL, 62 }) 63 /** @hide */ 64 @Retention(RetentionPolicy.SOURCE) 65 public @interface State {} 66 67 /** 68 * An internal state used as the initial state of an overlay. OverlayInfo 69 * objects exposed outside the {@link 70 * com.android.server.om.OverlayManagerService} should never have this 71 * state. 72 * 73 * @hide 74 */ 75 public static final int STATE_UNKNOWN = -1; 76 77 /** 78 * The target package of the overlay is not installed. The overlay cannot be enabled. 79 * 80 * @hide 81 */ 82 public static final int STATE_MISSING_TARGET = 0; 83 84 /** 85 * Creation of idmap file failed (e.g. no matching resources). The overlay 86 * cannot be enabled. 87 * 88 * @hide 89 */ 90 public static final int STATE_NO_IDMAP = 1; 91 92 /** 93 * The overlay is currently disabled. It can be enabled. 94 * 95 * @see IOverlayManager#setEnabled 96 * @hide 97 */ 98 public static final int STATE_DISABLED = 2; 99 100 /** 101 * The overlay is currently enabled. It can be disabled. 102 * 103 * @see IOverlayManager#setEnabled 104 * @hide 105 */ 106 public static final int STATE_ENABLED = 3; 107 108 /** 109 * The target package is currently being upgraded or downgraded; the state 110 * will change once the package installation has finished. 111 * @hide 112 * 113 * @deprecated No longer used. Caused invalid transitions from enabled -> upgrading -> enabled, 114 * where an update is propagated when nothing has changed. Can occur during --dont-kill 115 * installs when code and resources are hot swapped and the Activity should not be relaunched. 116 * In all other cases, the process and therefore Activity is killed, so the state loop is 117 * irrelevant. 118 */ 119 @Deprecated 120 public static final int STATE_TARGET_IS_BEING_REPLACED = 4; 121 122 /** 123 * The overlay package is currently being upgraded or downgraded; the state 124 * will change once the package installation has finished. 125 * @hide 126 */ 127 public static final int STATE_OVERLAY_IS_BEING_REPLACED = 5; 128 129 /** 130 * The overlay package is currently enabled because it is marked as 131 * 'immutable'. It cannot be disabled but will change state if for instance 132 * its target is uninstalled. 133 * @hide 134 */ 135 @Deprecated 136 public static final int STATE_ENABLED_IMMUTABLE = 6; 137 138 /** 139 * The target package needs to be refreshed as a result of a system update uninstall, which 140 * must recalculate the state of overlays against the newly enabled system package, which may 141 * differ in resources/policy from the /data variant that was uninstalled. 142 * @hide 143 */ 144 public static final int STATE_SYSTEM_UPDATE_UNINSTALL = 7; 145 146 /** 147 * Overlay category: theme. 148 * <p> 149 * Change how Android (including the status bar, dialogs, ...) looks. 150 * 151 * @hide 152 */ 153 public static final String CATEGORY_THEME = "android.theme"; 154 155 /** 156 * Package name of the overlay package 157 * 158 * @hide 159 */ 160 @NonNull 161 public final String packageName; 162 163 /** 164 * The unique name within the package of the overlay. 165 * 166 * @hide 167 */ 168 @Nullable 169 public final String overlayName; 170 171 /** 172 * Package name of the target package 173 * 174 * @hide 175 */ 176 @NonNull 177 public final String targetPackageName; 178 179 /** 180 * Name of the target overlayable declaration. 181 * 182 * @hide 183 */ 184 @Nullable public final String targetOverlayableName; 185 186 /** 187 * Category of the overlay package 188 * 189 * @hide 190 */ 191 @Nullable public final String category; 192 193 /** 194 * Full path to the base APK for this overlay package 195 * @hide 196 */ 197 @NonNull 198 public final String baseCodePath; 199 200 /** 201 * The state of this OverlayInfo as defined by the STATE_* constants in this class. 202 * @hide 203 */ 204 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 205 public final @State int state; 206 207 /** 208 * User handle for which this overlay applies 209 * @hide 210 */ 211 public final int userId; 212 213 /** 214 * Priority as configured by {@link com.android.internal.content.om.OverlayConfig}. 215 * Not intended to be exposed to 3rd party. 216 * 217 * @hide 218 */ 219 public final int priority; 220 221 /** 222 * isMutable as configured by {@link com.android.internal.content.om.OverlayConfig}. 223 * If false, the overlay is unconditionally loaded and cannot be unloaded. Not intended to be 224 * exposed to 3rd party. 225 * 226 * @hide 227 */ 228 public final boolean isMutable; 229 230 private OverlayIdentifier mIdentifierCached; 231 232 /** 233 * 234 * @hide 235 */ 236 public final boolean isFabricated; 237 238 /** 239 * Create a new OverlayInfo based on source with an updated state. 240 * 241 * @param source the source OverlayInfo to base the new instance on 242 * @param state the new state for the source OverlayInfo 243 * 244 * @hide 245 */ OverlayInfo(@onNull OverlayInfo source, @State int state)246 public OverlayInfo(@NonNull OverlayInfo source, @State int state) { 247 this(source.packageName, source.overlayName, source.targetPackageName, 248 source.targetOverlayableName, source.category, source.baseCodePath, state, 249 source.userId, source.priority, source.isMutable, source.isFabricated); 250 } 251 252 /** @hide */ 253 @VisibleForTesting OverlayInfo(@onNull String packageName, @NonNull String targetPackageName, @Nullable String targetOverlayableName, @Nullable String category, @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable)254 public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName, 255 @Nullable String targetOverlayableName, @Nullable String category, 256 @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) { 257 this(packageName, null /* overlayName */, targetPackageName, targetOverlayableName, 258 category, baseCodePath, state, userId, priority, isMutable, 259 false /* isFabricated */); 260 } 261 262 /** @hide */ OverlayInfo(@onNull String packageName, @Nullable String overlayName, @NonNull String targetPackageName, @Nullable String targetOverlayableName, @Nullable String category, @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable, boolean isFabricated)263 public OverlayInfo(@NonNull String packageName, @Nullable String overlayName, 264 @NonNull String targetPackageName, @Nullable String targetOverlayableName, 265 @Nullable String category, @NonNull String baseCodePath, int state, int userId, 266 int priority, boolean isMutable, boolean isFabricated) { 267 this.packageName = packageName; 268 this.overlayName = overlayName; 269 this.targetPackageName = targetPackageName; 270 this.targetOverlayableName = targetOverlayableName; 271 this.category = category; 272 this.baseCodePath = baseCodePath; 273 this.state = state; 274 this.userId = userId; 275 this.priority = priority; 276 this.isMutable = isMutable; 277 this.isFabricated = isFabricated; 278 ensureValidState(); 279 } 280 281 /** @hide */ OverlayInfo(@onNull Parcel source)282 public OverlayInfo(@NonNull Parcel source) { 283 packageName = source.readString(); 284 overlayName = source.readString(); 285 targetPackageName = source.readString(); 286 targetOverlayableName = source.readString(); 287 category = source.readString(); 288 baseCodePath = source.readString(); 289 state = source.readInt(); 290 userId = source.readInt(); 291 priority = source.readInt(); 292 isMutable = source.readBoolean(); 293 isFabricated = source.readBoolean(); 294 ensureValidState(); 295 } 296 297 /** 298 * {@inheritDoc} 299 * @hide 300 */ 301 @Override 302 @SystemApi 303 @NonNull getPackageName()304 public String getPackageName() { 305 return packageName; 306 } 307 308 /** 309 * Get the overlay name from the registered fabricated overlay. 310 * 311 * @return the overlay name 312 */ 313 @Override 314 @Nullable getOverlayName()315 public String getOverlayName() { 316 return overlayName; 317 } 318 319 /** 320 * Returns the name of the target overlaid package. 321 * 322 * @return the target package name 323 */ 324 @Override 325 @NonNull getTargetPackageName()326 public String getTargetPackageName() { 327 return targetPackageName; 328 } 329 330 /** 331 * Returns the category of the current overlay. 332 * 333 * @hide 334 */ 335 @SystemApi 336 @Nullable getCategory()337 public String getCategory() { 338 return category; 339 } 340 341 /** 342 * Returns user handle for which this overlay applies to. 343 * 344 * @hide 345 */ 346 @SystemApi 347 @UserIdInt getUserId()348 public int getUserId() { 349 return userId; 350 } 351 352 /** 353 * Return the target overlayable name. 354 * 355 * @return the name of the target overlayable resources set 356 */ 357 @Override 358 @Nullable getTargetOverlayableName()359 public String getTargetOverlayableName() { 360 return targetOverlayableName; 361 } 362 363 /** 364 * {@inheritDoc} 365 * @hide 366 */ 367 @Override isFabricated()368 public boolean isFabricated() { 369 return isFabricated; 370 } 371 372 /** 373 * Full path to the base APK or fabricated overlay for this overlay package. 374 * 375 * @hide 376 */ 377 @NonNull getBaseCodePath()378 public String getBaseCodePath() { 379 return baseCodePath; 380 } 381 382 /** 383 * Get the unique identifier from the overlay information. 384 * 385 * <p>The return value of this function can be used to unregister the related overlay. 386 * 387 * @return an identifier representing the current overlay. 388 */ 389 @Override 390 @NonNull getOverlayIdentifier()391 public OverlayIdentifier getOverlayIdentifier() { 392 if (mIdentifierCached == null) { 393 mIdentifierCached = new OverlayIdentifier(packageName, overlayName); 394 } 395 return mIdentifierCached; 396 } 397 398 @SuppressWarnings("ConstantConditions") ensureValidState()399 private void ensureValidState() { 400 if (packageName == null) { 401 throw new IllegalArgumentException("packageName must not be null"); 402 } 403 if (targetPackageName == null) { 404 throw new IllegalArgumentException("targetPackageName must not be null"); 405 } 406 if (baseCodePath == null) { 407 throw new IllegalArgumentException("baseCodePath must not be null"); 408 } 409 switch (state) { 410 case STATE_UNKNOWN: 411 case STATE_MISSING_TARGET: 412 case STATE_NO_IDMAP: 413 case STATE_DISABLED: 414 case STATE_ENABLED: 415 case STATE_ENABLED_IMMUTABLE: 416 case STATE_TARGET_IS_BEING_REPLACED: 417 case STATE_OVERLAY_IS_BEING_REPLACED: 418 break; 419 default: 420 throw new IllegalArgumentException("State " + state + " is not a valid state"); 421 } 422 } 423 424 @Override describeContents()425 public int describeContents() { 426 return 0; 427 } 428 429 @Override writeToParcel(@onNull Parcel dest, int flags)430 public void writeToParcel(@NonNull Parcel dest, int flags) { 431 dest.writeString(packageName); 432 dest.writeString(overlayName); 433 dest.writeString(targetPackageName); 434 dest.writeString(targetOverlayableName); 435 dest.writeString(category); 436 dest.writeString(baseCodePath); 437 dest.writeInt(state); 438 dest.writeInt(userId); 439 dest.writeInt(priority); 440 dest.writeBoolean(isMutable); 441 dest.writeBoolean(isFabricated); 442 } 443 444 public static final @NonNull Parcelable.Creator<OverlayInfo> CREATOR = 445 new Parcelable.Creator<OverlayInfo>() { 446 @Override 447 public OverlayInfo createFromParcel(Parcel source) { 448 return new OverlayInfo(source); 449 } 450 451 @Override 452 public OverlayInfo[] newArray(int size) { 453 return new OverlayInfo[size]; 454 } 455 }; 456 457 /** 458 * Return true if this overlay is enabled, i.e. should be used to overlay 459 * the resources in the target package. 460 * 461 * Disabled overlay packages are installed but are currently not in use. 462 * 463 * @return true if the overlay is enabled, else false. 464 * @hide 465 */ 466 @SystemApi isEnabled()467 public boolean isEnabled() { 468 switch (state) { 469 case STATE_ENABLED: 470 case STATE_ENABLED_IMMUTABLE: 471 return true; 472 default: 473 return false; 474 } 475 } 476 477 /** 478 * Translate a state to a human readable string. Only intended for 479 * debugging purposes. 480 * 481 * @return a human readable String representing the state. 482 * @hide 483 */ stateToString(@tate int state)484 public static String stateToString(@State int state) { 485 switch (state) { 486 case STATE_UNKNOWN: 487 return "STATE_UNKNOWN"; 488 case STATE_MISSING_TARGET: 489 return "STATE_MISSING_TARGET"; 490 case STATE_NO_IDMAP: 491 return "STATE_NO_IDMAP"; 492 case STATE_DISABLED: 493 return "STATE_DISABLED"; 494 case STATE_ENABLED: 495 return "STATE_ENABLED"; 496 case STATE_ENABLED_IMMUTABLE: 497 return "STATE_ENABLED_IMMUTABLE"; 498 case STATE_TARGET_IS_BEING_REPLACED: 499 return "STATE_TARGET_IS_BEING_REPLACED"; 500 case STATE_OVERLAY_IS_BEING_REPLACED: 501 return "STATE_OVERLAY_IS_BEING_REPLACED"; 502 default: 503 return "<unknown state>"; 504 } 505 } 506 507 /** 508 * {@inheritDoc} 509 * 510 * @hide 511 */ 512 @Override hashCode()513 public int hashCode() { 514 final int prime = 31; 515 int result = 1; 516 result = prime * result + userId; 517 result = prime * result + state; 518 result = prime * result + ((packageName == null) ? 0 : packageName.hashCode()); 519 result = prime * result + ((overlayName == null) ? 0 : overlayName.hashCode()); 520 result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode()); 521 result = prime * result + ((targetOverlayableName == null) ? 0 522 : targetOverlayableName.hashCode()); 523 result = prime * result + ((category == null) ? 0 : category.hashCode()); 524 result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode()); 525 return result; 526 } 527 528 /** 529 * {@inheritDoc} 530 * 531 * @hide 532 */ 533 @Override equals(@ullable Object obj)534 public boolean equals(@Nullable Object obj) { 535 if (this == obj) { 536 return true; 537 } 538 if (obj == null) { 539 return false; 540 } 541 if (getClass() != obj.getClass()) { 542 return false; 543 } 544 OverlayInfo other = (OverlayInfo) obj; 545 if (userId != other.userId) { 546 return false; 547 } 548 if (state != other.state) { 549 return false; 550 } 551 if (!packageName.equals(other.packageName)) { 552 return false; 553 } 554 if (!Objects.equals(overlayName, other.overlayName)) { 555 return false; 556 } 557 if (!targetPackageName.equals(other.targetPackageName)) { 558 return false; 559 } 560 if (!Objects.equals(targetOverlayableName, other.targetOverlayableName)) { 561 return false; 562 } 563 if (!Objects.equals(category, other.category)) { 564 return false; 565 } 566 if (!baseCodePath.equals(other.baseCodePath)) { 567 return false; 568 } 569 return true; 570 } 571 572 /** 573 * {@inheritDoc} 574 * 575 * @hide 576 */ 577 @NonNull 578 @Override toString()579 public String toString() { 580 return "OverlayInfo {" 581 + "packageName=" + packageName 582 + ", overlayName=" + overlayName 583 + ", targetPackage=" + targetPackageName 584 + ", targetOverlayable=" + targetOverlayableName 585 + ", state=" + state + " (" + stateToString(state) + ")," 586 + ", userId=" + userId 587 + " }"; 588 } 589 } 590