content: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.content.om;
  
  import android.annotation.IntDef;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.SystemApi;
  import android.annotation.UnsupportedAppUsage;
  import android.annotation.UserIdInt;
  import android.os.Parcel;
  import android.os.Parcelable;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.Objects;
  
  /**
   * Immutable overlay information about a package. All PackageInfos that
   * represent an overlay package will have a corresponding OverlayInfo.
   *
   * @hide
   */
  @SystemApi
  public final class OverlayInfo implements Parcelable {
  
      /** @hide */
      @IntDef(prefix = "STATE_", value = {
              STATE_UNKNOWN,
              STATE_MISSING_TARGET,
              STATE_NO_IDMAP,
              STATE_DISABLED,
              STATE_ENABLED,
              STATE_ENABLED_STATIC,
              // @Deprecated STATE_TARGET_IS_BEING_REPLACED,
              STATE_OVERLAY_IS_BEING_REPLACED,
      })
      /** @hide */
      @Retention(RetentionPolicy.SOURCE)
      public @interface State {}
  
      /**
       * An internal state used as the initial state of an overlay. OverlayInfo
       * objects exposed outside the {@link
       * com.android.server.om.OverlayManagerService} should never have this
       * state.
       *
       * @hide
       */
      public static final int STATE_UNKNOWN = -1;
  
      /**
       * The target package of the overlay is not installed. The overlay cannot be enabled.
       *
       * @hide
       */
      public static final int STATE_MISSING_TARGET = 0;
  
      /**
       * Creation of idmap file failed (e.g. no matching resources). The overlay
       * cannot be enabled.
       *
       * @hide
       */
      public static final int STATE_NO_IDMAP = 1;
  
      /**
       * The overlay is currently disabled. It can be enabled.
       *
       * @see IOverlayManager#setEnabled
       * @hide
       */
      public static final int STATE_DISABLED = 2;
  
      /**
       * The overlay is currently enabled. It can be disabled.
       *
       * @see IOverlayManager#setEnabled
       * @hide
       */
      public static final int STATE_ENABLED = 3;
  
      /**
       * The target package is currently being upgraded or downgraded; the state
       * will change once the package installation has finished.
       * @hide
       *
       * @deprecated No longer used. Caused invalid transitions from enabled -> upgrading -> enabled,
       * where an update is propagated when nothing has changed. Can occur during --dont-kill
       * installs when code and resources are hot swapped and the Activity should not be relaunched.
       * In all other cases, the process and therefore Activity is killed, so the state loop is
       * irrelevant.
       */
      @Deprecated
      public static final int STATE_TARGET_IS_BEING_REPLACED = 4;
  
      /**
       * The overlay package is currently being upgraded or downgraded; the state
       * will change once the package installation has finished.
       * @hide
       */
      public static final int STATE_OVERLAY_IS_BEING_REPLACED = 5;
  
      /**
       * The overlay package is currently enabled because it is marked as
       * 'static'. It cannot be disabled but will change state if for instance
       * its target is uninstalled.
       * @hide
       */
      public static final int STATE_ENABLED_STATIC = 6;
  
      /**
       * Overlay category: theme.
       * <p>
       * Change how Android (including the status bar, dialogs, ...) looks.
       *
       * @hide
       */
      public static final String CATEGORY_THEME = "android.theme";
  
      /**
       * Package name of the overlay package
       *
       * @hide
       */
      public final String packageName;
  
      /**
       * Package name of the target package
       *
       * @hide
       */
      public final String targetPackageName;
  
      /**
       * Name of the target overlayable declaration.
       *
       * @hide
       */
      public final String targetOverlayableName;
  
      /**
       * Category of the overlay package
       *
       * @hide
       */
      public final String category;
  
      /**
       * Full path to the base APK for this overlay package
       * @hide
       */
      public final String baseCodePath;
  
      /**
       * The state of this OverlayInfo as defined by the STATE_* constants in this class.
       * @hide
       */
      @UnsupportedAppUsage
      public final @State int state;
  
      /**
       * User handle for which this overlay applies
       * @hide
       */
      public final int userId;
  
      /**
       * Priority as read from the manifest. Used if isStatic is true. Not
       * intended to be exposed to 3rd party.
       *
       * @hide
       */
      public final int priority;
  
      /**
       * isStatic as read from the manifest. If true, the overlay is
       * unconditionally loaded and cannot be unloaded. Not intended to be
       * exposed to 3rd party.
       *
       * @hide
       */
      public final boolean isStatic;
  
      /**
       * Create a new OverlayInfo based on source with an updated state.
       *
       * @param source the source OverlayInfo to base the new instance on
       * @param state the new state for the source OverlayInfo
       *
       * @hide
       */
      public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
          this(source.packageName, source.targetPackageName, source.targetOverlayableName,
                  source.category, source.baseCodePath, state, source.userId, source.priority,
                  source.isStatic);
      }
  
      /** @hide */
      public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
              @Nullable String targetOverlayableName, @Nullable String category,
              @NonNull String baseCodePath, int state, int userId,
              int priority, boolean isStatic) {
          this.packageName = packageName;
          this.targetPackageName = targetPackageName;
          this.targetOverlayableName = targetOverlayableName;
          this.category = category;
          this.baseCodePath = baseCodePath;
          this.state = state;
          this.userId = userId;
          this.priority = priority;
          this.isStatic = isStatic;
          ensureValidState();
      }
  
      /** @hide */
      public OverlayInfo(Parcel source) {
          packageName = source.readString();
          targetPackageName = source.readString();
          targetOverlayableName = source.readString();
          category = source.readString();
          baseCodePath = source.readString();
          state = source.readInt();
          userId = source.readInt();
          priority = source.readInt();
          isStatic = source.readBoolean();
          ensureValidState();
      }
  
      /**
       * Returns package name of the current overlay.
       * @hide
       */
      @SystemApi
      @NonNull
      public String getPackageName() {
          return packageName;
      }
  
      /**
       * Returns the target package name of the current overlay.
       * @hide
       */
      @SystemApi
      @NonNull
      public String getTargetPackageName() {
          return targetPackageName;
      }
  
      /**
       * Returns the category of the current overlay.
       * @hide\
       */
      @SystemApi
      @Nullable
      public String getCategory() {
          return category;
      }
  
      /**
       * Returns user handle for which this overlay applies to.
       * @hide
       */
      @SystemApi
      @UserIdInt
      public int getUserId() {
          return userId;
      }
  
      /**
       * Returns name of the target overlayable declaration.
       * @hide
       */
      @SystemApi
      @Nullable
      public String getTargetOverlayableName() {
          return targetOverlayableName;
      }
  
      private void ensureValidState() {
          if (packageName == null) {
              throw new IllegalArgumentException("packageName must not be null");
          }
          if (targetPackageName == null) {
              throw new IllegalArgumentException("targetPackageName must not be null");
          }
          if (baseCodePath == null) {
              throw new IllegalArgumentException("baseCodePath must not be null");
          }
          switch (state) {
              case STATE_UNKNOWN:
              case STATE_MISSING_TARGET:
              case STATE_NO_IDMAP:
              case STATE_DISABLED:
              case STATE_ENABLED:
              case STATE_ENABLED_STATIC:
              case STATE_TARGET_IS_BEING_REPLACED:
              case STATE_OVERLAY_IS_BEING_REPLACED:
                  break;
              default:
                  throw new IllegalArgumentException("State " + state + " is not a valid state");
          }
      }
  
      @Override
      public int describeContents() {
          return 0;
      }
  
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          dest.writeString(packageName);
          dest.writeString(targetPackageName);
          dest.writeString(targetOverlayableName);
          dest.writeString(category);
          dest.writeString(baseCodePath);
          dest.writeInt(state);
          dest.writeInt(userId);
          dest.writeInt(priority);
          dest.writeBoolean(isStatic);
      }
  
      public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR =
              new Parcelable.Creator<OverlayInfo>() {
          @Override
          public OverlayInfo createFromParcel(Parcel source) {
              return new OverlayInfo(source);
          }
  
          @Override
          public OverlayInfo[] newArray(int size) {
              return new OverlayInfo[size];
          }
      };
  
      /**
       * Return true if this overlay is enabled, i.e. should be used to overlay
       * the resources in the target package.
       *
       * Disabled overlay packages are installed but are currently not in use.
       *
       * @return true if the overlay is enabled, else false.
       * @hide
       */
      @SystemApi
      public boolean isEnabled() {
          switch (state) {
              case STATE_ENABLED:
              case STATE_ENABLED_STATIC:
                  return true;
              default:
                  return false;
          }
      }
  
      /**
       * Translate a state to a human readable string. Only intended for
       * debugging purposes.
       *
       * @return a human readable String representing the state.
       * @hide
       */
      public static String stateToString(@State int state) {
          switch (state) {
              case STATE_UNKNOWN:
                  return "STATE_UNKNOWN";
              case STATE_MISSING_TARGET:
                  return "STATE_MISSING_TARGET";
              case STATE_NO_IDMAP:
                  return "STATE_NO_IDMAP";
              case STATE_DISABLED:
                  return "STATE_DISABLED";
              case STATE_ENABLED:
                  return "STATE_ENABLED";
              case STATE_ENABLED_STATIC:
                  return "STATE_ENABLED_STATIC";
              case STATE_TARGET_IS_BEING_REPLACED:
                  return "STATE_TARGET_IS_BEING_REPLACED";
              case STATE_OVERLAY_IS_BEING_REPLACED:
                  return "STATE_OVERLAY_IS_BEING_REPLACED";
              default:
                  return "<unknown state>";
          }
      }
  
      @Override
      public int hashCode() {
          final int prime = 31;
          int result = 1;
          result = prime * result + userId;
          result = prime * result + state;
          result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
          result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
          result = prime * result + ((targetOverlayableName == null) ? 0
                  : targetOverlayableName.hashCode());
          result = prime * result + ((category == null) ? 0 : category.hashCode());
          result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode());
          return result;
      }
  
      @Override
      public boolean equals(Object obj) {
          if (this == obj) {
              return true;
          }
          if (obj == null) {
              return false;
          }
          if (getClass() != obj.getClass()) {
              return false;
          }
          OverlayInfo other = (OverlayInfo) obj;
          if (userId != other.userId) {
              return false;
          }
          if (state != other.state) {
              return false;
          }
          if (!packageName.equals(other.packageName)) {
              return false;
          }
          if (!targetPackageName.equals(other.targetPackageName)) {
              return false;
          }
          if (!Objects.equals(targetOverlayableName, other.targetOverlayableName)) {
              return false;
          }
          if (!Objects.equals(category, other.category)) {
              return false;
          }
          if (!baseCodePath.equals(other.baseCodePath)) {
              return false;
          }
          return true;
      }
  
      @Override
      public String toString() {
          return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName
                  + ((targetOverlayableName == null) ? ""
                  : ", targetOverlayable=" + targetOverlayableName)
                  + ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }";
      }
  }
  