/* * Copyright (C) 2017 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.app.timezone; import static android.app.timezone.Utils.validateConditionalNull; import static android.app.timezone.Utils.validateNotNull; import static android.app.timezone.Utils.validateRulesVersion; import android.annotation.IntDef; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Description of the state of time zone rules on a device. * * <p>The following properties are included: * <dl> * <dt>systemRulesVersion</dt> * <dd>the IANA rules version that shipped with the OS. Always present. e.g. "2017a".</dd> * <dt>distroFormatVersionSupported</dt> * <dd>the distro format version supported by this device. Always present.</dd> * <dt>operationInProgress</dt> * <dd>{@code true} if there is an install / uninstall operation currently happening.</dd> * <dt>stagedOperationType</dt> * <dd>one of {@link #STAGED_OPERATION_UNKNOWN}, {@link #STAGED_OPERATION_NONE}, * {@link #STAGED_OPERATION_UNINSTALL} and {@link #STAGED_OPERATION_INSTALL} indicating whether * there is a currently staged time zone distro operation. {@link #STAGED_OPERATION_UNKNOWN} is * used when {@link #isOperationInProgress()} is {@code true}. Staged operations currently * require a reboot to become active.</dd> * <dt>stagedDistroRulesVersion</dt> * <dd>[present if distroStagedState == STAGED_STATE_INSTALL], the rules version of the distro * currently staged for installation.</dd> * <dt>distroStatus</dt> * <dd>{@link #DISTRO_STATUS_INSTALLED} if there is a time zone distro installed and active, * {@link #DISTRO_STATUS_NONE} if there is no active installed distro. * {@link #DISTRO_STATUS_UNKNOWN} is used when {@link #isOperationInProgress()} is {@code true}. * </dd> * <dt>installedDistroRulesVersion</dt> * <dd>[present if distroStatus == {@link #DISTRO_STATUS_INSTALLED}], the rules version of the * installed and active distro.</dd> * </dl> * * @hide */ // TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728 public final class RulesState implements Parcelable { @Retention(RetentionPolicy.SOURCE) @IntDef({ STAGED_OPERATION_UNKNOWN, STAGED_OPERATION_NONE, STAGED_OPERATION_UNINSTALL, STAGED_OPERATION_INSTALL }) private @interface StagedOperationType {} /** Staged state could not be determined. */ public static final int STAGED_OPERATION_UNKNOWN = 0; /** Nothing is staged. */ public static final int STAGED_OPERATION_NONE = 1; /** An uninstall is staged. */ public static final int STAGED_OPERATION_UNINSTALL = 2; /** An install is staged. */ public static final int STAGED_OPERATION_INSTALL = 3; @Retention(RetentionPolicy.SOURCE) @IntDef({ DISTRO_STATUS_UNKNOWN, DISTRO_STATUS_NONE, DISTRO_STATUS_INSTALLED }) private @interface DistroStatus {} /** The current distro status could not be determined. */ public static final int DISTRO_STATUS_UNKNOWN = 0; /** There is no active installed time zone distro. */ public static final int DISTRO_STATUS_NONE = 1; /** The is an active, installed time zone distro. */ public static final int DISTRO_STATUS_INSTALLED = 2; private static final byte BYTE_FALSE = 0; private static final byte BYTE_TRUE = 1; private final String mSystemRulesVersion; private final DistroFormatVersion mDistroFormatVersionSupported; private final boolean mOperationInProgress; @StagedOperationType private final int mStagedOperationType; @Nullable private final DistroRulesVersion mStagedDistroRulesVersion; @DistroStatus private final int mDistroStatus; @Nullable private final DistroRulesVersion mInstalledDistroRulesVersion; public RulesState(String systemRulesVersion, DistroFormatVersion distroFormatVersionSupported, boolean operationInProgress, @StagedOperationType int stagedOperationType, @Nullable DistroRulesVersion stagedDistroRulesVersion, @DistroStatus int distroStatus, @Nullable DistroRulesVersion installedDistroRulesVersion) { this.mSystemRulesVersion = validateRulesVersion("systemRulesVersion", systemRulesVersion); this.mDistroFormatVersionSupported = validateNotNull("distroFormatVersionSupported", distroFormatVersionSupported); this.mOperationInProgress = operationInProgress; if (operationInProgress && stagedOperationType != STAGED_OPERATION_UNKNOWN) { throw new IllegalArgumentException( "stagedOperationType != STAGED_OPERATION_UNKNOWN"); } this.mStagedOperationType = validateStagedOperation(stagedOperationType); this.mStagedDistroRulesVersion = validateConditionalNull( mStagedOperationType == STAGED_OPERATION_INSTALL /* requireNotNull */, "stagedDistroRulesVersion", stagedDistroRulesVersion); if (operationInProgress && distroStatus != DISTRO_STATUS_UNKNOWN) { throw new IllegalArgumentException("distroInstalled != DISTRO_STATUS_UNKNOWN"); } this.mDistroStatus = validateDistroStatus(distroStatus); this.mInstalledDistroRulesVersion = validateConditionalNull( mDistroStatus == DISTRO_STATUS_INSTALLED/* requireNotNull */, "installedDistroRulesVersion", installedDistroRulesVersion); } public String getSystemRulesVersion() { return mSystemRulesVersion; } public boolean isOperationInProgress() { return mOperationInProgress; } public @StagedOperationType int getStagedOperationType() { return mStagedOperationType; } /** * Returns the staged rules version when {@link #getStagedOperationType()} is * {@link #STAGED_OPERATION_INSTALL}. */ public @Nullable DistroRulesVersion getStagedDistroRulesVersion() { return mStagedDistroRulesVersion; } public @DistroStatus int getDistroStatus() { return mDistroStatus; } /** * Returns the installed rules version when {@link #getDistroStatus()} is * {@link #DISTRO_STATUS_INSTALLED}. */ public @Nullable DistroRulesVersion getInstalledDistroRulesVersion() { return mInstalledDistroRulesVersion; } /** * Returns true if a distro in the specified format is supported on this device. */ public boolean isDistroFormatVersionSupported(DistroFormatVersion distroFormatVersion) { return mDistroFormatVersionSupported.supports(distroFormatVersion); } /** * Returns true if the distro IANA rules version supplied is newer or the same as the version in * the system image data files. */ public boolean isSystemVersionOlderThan(DistroRulesVersion distroRulesVersion) { return mSystemRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) < 0; } public boolean isDistroInstalled() { return mDistroStatus == DISTRO_STATUS_INSTALLED; } /** * Returns true if the rules version supplied is newer than the one currently installed. If * there is no installed distro this method throws IllegalStateException. */ public boolean isInstalledDistroOlderThan(DistroRulesVersion distroRulesVersion) { if (mOperationInProgress) { throw new IllegalStateException("Distro state not known: operation in progress."); } if (!isDistroInstalled()) { throw new IllegalStateException("No distro installed."); } return mInstalledDistroRulesVersion.isOlderThan(distroRulesVersion); } public static final Parcelable.Creator<RulesState> CREATOR = new Parcelable.Creator<RulesState>() { public RulesState createFromParcel(Parcel in) { return RulesState.createFromParcel(in); } public RulesState[] newArray(int size) { return new RulesState[size]; } }; private static RulesState createFromParcel(Parcel in) { String systemRulesVersion = in.readString(); DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null); boolean operationInProgress = in.readByte() == BYTE_TRUE; int distroStagedState = in.readByte(); DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null); int installedDistroStatus = in.readByte(); DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null); return new RulesState(systemRulesVersion, distroFormatVersionSupported, operationInProgress, distroStagedState, stagedDistroRulesVersion, installedDistroStatus, installedDistroRulesVersion); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mSystemRulesVersion); out.writeParcelable(mDistroFormatVersionSupported, 0); out.writeByte(mOperationInProgress ? BYTE_TRUE : BYTE_FALSE); out.writeByte((byte) mStagedOperationType); out.writeParcelable(mStagedDistroRulesVersion, 0); out.writeByte((byte) mDistroStatus); out.writeParcelable(mInstalledDistroRulesVersion, 0); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RulesState that = (RulesState) o; if (mOperationInProgress != that.mOperationInProgress) { return false; } if (mStagedOperationType != that.mStagedOperationType) { return false; } if (mDistroStatus != that.mDistroStatus) { return false; } if (!mSystemRulesVersion.equals(that.mSystemRulesVersion)) { return false; } if (!mDistroFormatVersionSupported.equals(that.mDistroFormatVersionSupported)) { return false; } if (mStagedDistroRulesVersion != null ? !mStagedDistroRulesVersion .equals(that.mStagedDistroRulesVersion) : that.mStagedDistroRulesVersion != null) { return false; } return mInstalledDistroRulesVersion != null ? mInstalledDistroRulesVersion .equals(that.mInstalledDistroRulesVersion) : that.mInstalledDistroRulesVersion == null; } @Override public int hashCode() { int result = mSystemRulesVersion.hashCode(); result = 31 * result + mDistroFormatVersionSupported.hashCode(); result = 31 * result + (mOperationInProgress ? 1 : 0); result = 31 * result + mStagedOperationType; result = 31 * result + (mStagedDistroRulesVersion != null ? mStagedDistroRulesVersion .hashCode() : 0); result = 31 * result + mDistroStatus; result = 31 * result + (mInstalledDistroRulesVersion != null ? mInstalledDistroRulesVersion .hashCode() : 0); return result; } @Override public String toString() { return "RulesState{" + "mSystemRulesVersion='" + mSystemRulesVersion + '\'' + ", mDistroFormatVersionSupported=" + mDistroFormatVersionSupported + ", mOperationInProgress=" + mOperationInProgress + ", mStagedOperationType=" + mStagedOperationType + ", mStagedDistroRulesVersion=" + mStagedDistroRulesVersion + ", mDistroStatus=" + mDistroStatus + ", mInstalledDistroRulesVersion=" + mInstalledDistroRulesVersion + '}'; } private static int validateStagedOperation(int stagedOperationType) { if (stagedOperationType < STAGED_OPERATION_UNKNOWN || stagedOperationType > STAGED_OPERATION_INSTALL) { throw new IllegalArgumentException("Unknown operation type=" + stagedOperationType); } return stagedOperationType; } private static int validateDistroStatus(int distroStatus) { if (distroStatus < DISTRO_STATUS_UNKNOWN || distroStatus > DISTRO_STATUS_INSTALLED) { throw new IllegalArgumentException("Unknown distro status=" + distroStatus); } return distroStatus; } }