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