1 /*
2  * Copyright (C) 2017 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.app.timezone;
18 
19 import static android.app.timezone.Utils.validateConditionalNull;
20 import static android.app.timezone.Utils.validateNotNull;
21 import static android.app.timezone.Utils.validateRulesVersion;
22 
23 import android.annotation.IntDef;
24 import android.annotation.Nullable;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 
31 /**
32  * Description of the state of time zone rules on a device.
33  *
34  * <p>The following properties are included:
35  * <dl>
36  *     <dt>baseRulesVersion</dt>
37  *     <dd>the IANA rules version that shipped with the OS. Always present. e.g. "2017a".</dd>
38  *     <dt>distroFormatVersionSupported</dt>
39  *     <dd>the distro format version supported by this device. Always present.</dd>
40  *     <dt>operationInProgress</dt>
41  *     <dd>{@code true} if there is an install / uninstall operation currently happening.</dd>
42  *     <dt>stagedOperationType</dt>
43  *     <dd>one of {@link #STAGED_OPERATION_UNKNOWN}, {@link #STAGED_OPERATION_NONE},
44  *     {@link #STAGED_OPERATION_UNINSTALL} and {@link #STAGED_OPERATION_INSTALL} indicating whether
45  *     there is a currently staged time zone distro operation. {@link #STAGED_OPERATION_UNKNOWN} is
46  *     used when {@link #isOperationInProgress()} is {@code true}. Staged operations currently
47  *     require a reboot to become active.</dd>
48  *     <dt>stagedDistroRulesVersion</dt>
49  *     <dd>[present if distroStagedState == STAGED_STATE_INSTALL], the rules version of the distro
50  *     currently staged for installation.</dd>
51  *     <dt>distroStatus</dt>
52  *     <dd>{@link #DISTRO_STATUS_INSTALLED} if there is a time zone distro installed and active,
53  *     {@link #DISTRO_STATUS_NONE} if there is no active installed distro.
54  *     {@link #DISTRO_STATUS_UNKNOWN} is used when {@link #isOperationInProgress()} is {@code true}.
55  *     </dd>
56  *     <dt>installedDistroRulesVersion</dt>
57  *     <dd>[present if distroStatus == {@link #DISTRO_STATUS_INSTALLED}], the rules version of the
58  *     installed and active distro.</dd>
59  * </dl>
60  *
61  * @hide
62  */
63 public final class RulesState implements Parcelable {
64 
65     @Retention(RetentionPolicy.SOURCE)
66     @IntDef(prefix = { "STAGED_OPERATION_" }, value = {
67             STAGED_OPERATION_UNKNOWN,
68             STAGED_OPERATION_NONE,
69             STAGED_OPERATION_UNINSTALL,
70             STAGED_OPERATION_INSTALL
71     })
72     private @interface StagedOperationType {}
73 
74     /** Staged state could not be determined. */
75     public static final int STAGED_OPERATION_UNKNOWN = 0;
76     /** Nothing is staged. */
77     public static final int STAGED_OPERATION_NONE = 1;
78     /** An uninstall is staged. */
79     public static final int STAGED_OPERATION_UNINSTALL = 2;
80     /** An install is staged. */
81     public static final int STAGED_OPERATION_INSTALL = 3;
82 
83     @Retention(RetentionPolicy.SOURCE)
84     @IntDef(prefix = { "DISTRO_STATUS_" }, value = {
85             DISTRO_STATUS_UNKNOWN,
86             DISTRO_STATUS_NONE,
87             DISTRO_STATUS_INSTALLED
88     })
89     private @interface DistroStatus {}
90 
91     /** The current distro status could not be determined. */
92     public static final int DISTRO_STATUS_UNKNOWN = 0;
93     /** There is no active installed time zone distro. */
94     public static final int DISTRO_STATUS_NONE = 1;
95     /** The is an active, installed time zone distro. */
96     public static final int DISTRO_STATUS_INSTALLED = 2;
97 
98     private static final byte BYTE_FALSE = 0;
99     private static final byte BYTE_TRUE = 1;
100 
101     private final String mBaseRulesVersion;
102     private final DistroFormatVersion mDistroFormatVersionSupported;
103     private final boolean mOperationInProgress;
104     @StagedOperationType private final int mStagedOperationType;
105     @Nullable private final DistroRulesVersion mStagedDistroRulesVersion;
106     @DistroStatus private final int mDistroStatus;
107     @Nullable private final DistroRulesVersion mInstalledDistroRulesVersion;
108 
RulesState(String baseRulesVersion, DistroFormatVersion distroFormatVersionSupported, boolean operationInProgress, @StagedOperationType int stagedOperationType, @Nullable DistroRulesVersion stagedDistroRulesVersion, @DistroStatus int distroStatus, @Nullable DistroRulesVersion installedDistroRulesVersion)109     public RulesState(String baseRulesVersion, DistroFormatVersion distroFormatVersionSupported,
110             boolean operationInProgress,
111             @StagedOperationType int stagedOperationType,
112             @Nullable DistroRulesVersion stagedDistroRulesVersion,
113             @DistroStatus int distroStatus,
114             @Nullable DistroRulesVersion installedDistroRulesVersion) {
115         this.mBaseRulesVersion = validateRulesVersion("baseRulesVersion", baseRulesVersion);
116         this.mDistroFormatVersionSupported =
117                 validateNotNull("distroFormatVersionSupported", distroFormatVersionSupported);
118         this.mOperationInProgress = operationInProgress;
119 
120         if (operationInProgress && stagedOperationType != STAGED_OPERATION_UNKNOWN) {
121             throw new IllegalArgumentException(
122                     "stagedOperationType != STAGED_OPERATION_UNKNOWN");
123         }
124         this.mStagedOperationType = validateStagedOperation(stagedOperationType);
125         this.mStagedDistroRulesVersion = validateConditionalNull(
126                 mStagedOperationType == STAGED_OPERATION_INSTALL /* requireNotNull */,
127                 "stagedDistroRulesVersion", stagedDistroRulesVersion);
128 
129         this.mDistroStatus = validateDistroStatus(distroStatus);
130         this.mInstalledDistroRulesVersion = validateConditionalNull(
131                 mDistroStatus == DISTRO_STATUS_INSTALLED/* requireNotNull */,
132                 "installedDistroRulesVersion", installedDistroRulesVersion);
133     }
134 
getBaseRulesVersion()135     public String getBaseRulesVersion() {
136         return mBaseRulesVersion;
137     }
138 
isOperationInProgress()139     public boolean isOperationInProgress() {
140         return mOperationInProgress;
141     }
142 
getStagedOperationType()143     public @StagedOperationType int getStagedOperationType() {
144         return mStagedOperationType;
145     }
146 
147     /**
148      * Returns the staged rules version when {@link #getStagedOperationType()} is
149      * {@link #STAGED_OPERATION_INSTALL}.
150      */
getStagedDistroRulesVersion()151     public @Nullable DistroRulesVersion getStagedDistroRulesVersion() {
152         return mStagedDistroRulesVersion;
153     }
154 
getDistroStatus()155     public @DistroStatus int getDistroStatus() {
156         return mDistroStatus;
157     }
158 
159     /**
160      * Returns the installed rules version when {@link #getDistroStatus()} is
161      * {@link #DISTRO_STATUS_INSTALLED}.
162      */
getInstalledDistroRulesVersion()163     public @Nullable DistroRulesVersion getInstalledDistroRulesVersion() {
164         return mInstalledDistroRulesVersion;
165     }
166 
167     /**
168      * Returns true if a distro in the specified format is supported on this device.
169      */
isDistroFormatVersionSupported(DistroFormatVersion distroFormatVersion)170     public boolean isDistroFormatVersionSupported(DistroFormatVersion distroFormatVersion) {
171         return mDistroFormatVersionSupported.supports(distroFormatVersion);
172     }
173 
174     /**
175      * Returns true if the base data files contain IANA rules data that are newer than the
176      * distro IANA rules version supplied, i.e. true when the version specified would be "worse"
177      * than the one that is in the base data. Returns false if the base version is the
178      * same or older, i.e. false when the version specified would be "better" than the one that is
179      * in the base set.
180      */
isBaseVersionNewerThan(DistroRulesVersion distroRulesVersion)181     public boolean isBaseVersionNewerThan(DistroRulesVersion distroRulesVersion) {
182         return mBaseRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0;
183     }
184 
185     public static final @android.annotation.NonNull Parcelable.Creator<RulesState> CREATOR =
186             new Parcelable.Creator<RulesState>() {
187         public RulesState createFromParcel(Parcel in) {
188             return RulesState.createFromParcel(in);
189         }
190 
191         public RulesState[] newArray(int size) {
192             return new RulesState[size];
193         }
194     };
195 
createFromParcel(Parcel in)196     private static RulesState createFromParcel(Parcel in) {
197         String baseRulesVersion = in.readString();
198         DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null);
199         boolean operationInProgress = in.readByte() == BYTE_TRUE;
200         int distroStagedState = in.readByte();
201         DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null);
202         int installedDistroStatus = in.readByte();
203         DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null);
204         return new RulesState(baseRulesVersion, distroFormatVersionSupported, operationInProgress,
205                 distroStagedState, stagedDistroRulesVersion,
206                 installedDistroStatus, installedDistroRulesVersion);
207     }
208 
209     @Override
describeContents()210     public int describeContents() {
211         return 0;
212     }
213 
214     @Override
writeToParcel(Parcel out, int flags)215     public void writeToParcel(Parcel out, int flags) {
216         out.writeString(mBaseRulesVersion);
217         out.writeParcelable(mDistroFormatVersionSupported, 0);
218         out.writeByte(mOperationInProgress ? BYTE_TRUE : BYTE_FALSE);
219         out.writeByte((byte) mStagedOperationType);
220         out.writeParcelable(mStagedDistroRulesVersion, 0);
221         out.writeByte((byte) mDistroStatus);
222         out.writeParcelable(mInstalledDistroRulesVersion, 0);
223     }
224 
225     @Override
equals(Object o)226     public boolean equals(Object o) {
227         if (this == o) {
228             return true;
229         }
230         if (o == null || getClass() != o.getClass()) {
231             return false;
232         }
233 
234         RulesState that = (RulesState) o;
235 
236         if (mOperationInProgress != that.mOperationInProgress) {
237             return false;
238         }
239         if (mStagedOperationType != that.mStagedOperationType) {
240             return false;
241         }
242         if (mDistroStatus != that.mDistroStatus) {
243             return false;
244         }
245         if (!mBaseRulesVersion.equals(that.mBaseRulesVersion)) {
246             return false;
247         }
248         if (!mDistroFormatVersionSupported.equals(that.mDistroFormatVersionSupported)) {
249             return false;
250         }
251         if (mStagedDistroRulesVersion != null ? !mStagedDistroRulesVersion
252                 .equals(that.mStagedDistroRulesVersion) : that.mStagedDistroRulesVersion != null) {
253             return false;
254         }
255         return mInstalledDistroRulesVersion != null ? mInstalledDistroRulesVersion
256                 .equals(that.mInstalledDistroRulesVersion)
257                 : that.mInstalledDistroRulesVersion == null;
258     }
259 
260     @Override
hashCode()261     public int hashCode() {
262         int result = mBaseRulesVersion.hashCode();
263         result = 31 * result + mDistroFormatVersionSupported.hashCode();
264         result = 31 * result + (mOperationInProgress ? 1 : 0);
265         result = 31 * result + mStagedOperationType;
266         result = 31 * result + (mStagedDistroRulesVersion != null ? mStagedDistroRulesVersion
267                 .hashCode()
268                 : 0);
269         result = 31 * result + mDistroStatus;
270         result = 31 * result + (mInstalledDistroRulesVersion != null ? mInstalledDistroRulesVersion
271                 .hashCode() : 0);
272         return result;
273     }
274 
275     @Override
toString()276     public String toString() {
277         return "RulesState{"
278                 + "mBaseRulesVersion='" + mBaseRulesVersion + '\''
279                 + ", mDistroFormatVersionSupported=" + mDistroFormatVersionSupported
280                 + ", mOperationInProgress=" + mOperationInProgress
281                 + ", mStagedOperationType=" + mStagedOperationType
282                 + ", mStagedDistroRulesVersion=" + mStagedDistroRulesVersion
283                 + ", mDistroStatus=" + mDistroStatus
284                 + ", mInstalledDistroRulesVersion=" + mInstalledDistroRulesVersion
285                 + '}';
286     }
287 
validateStagedOperation(int stagedOperationType)288     private static int validateStagedOperation(int stagedOperationType) {
289         if (stagedOperationType < STAGED_OPERATION_UNKNOWN
290                 || stagedOperationType > STAGED_OPERATION_INSTALL) {
291             throw new IllegalArgumentException("Unknown operation type=" + stagedOperationType);
292         }
293         return stagedOperationType;
294     }
295 
validateDistroStatus(int distroStatus)296     private static int validateDistroStatus(int distroStatus) {
297         if (distroStatus < DISTRO_STATUS_UNKNOWN || distroStatus > DISTRO_STATUS_INSTALLED) {
298             throw new IllegalArgumentException("Unknown distro status=" + distroStatus);
299         }
300         return distroStatus;
301     }
302 }
303