1 /*
2  * Copyright (C) 2019 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.timedetector;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.os.TimestampedValue;
24 
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Objects;
29 
30 /**
31  * A time suggestion from an identified telephony source. e.g. from NITZ information from a specific
32  * radio.
33  *
34  * <p>{@code slotIndex} identifies the suggestion source. This enables detection logic to identify
35  * suggestions from the same source when there are several in use.
36  *
37  * <p>{@code utcTime}. When not {@code null}, the {@code utcTime.value} is the number of
38  * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the
39  * value of the elapsed realtime clock when the {@code utcTime.value} was established.
40  * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
41  * suggestions cannot be persisted across device resets. {@code utcTime} can be {@code null} to
42  * indicate that the telephony source has entered an "un-opinionated" state and any previous
43  * suggestion from the source is being withdrawn.
44  *
45  * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
46  * record why the suggestion exists, e.g. what triggered it to be made and what heuristic was used
47  * to determine the time or its absence. This information exists only to aid in debugging and
48  * therefore is used by {@link #toString()}, but it is not for use in detection logic and is not
49  * considered in {@link #hashCode()} or {@link #equals(Object)}.
50  *
51  * @hide
52  */
53 public final class TelephonyTimeSuggestion implements Parcelable {
54 
55     /** @hide */
56     public static final @NonNull Parcelable.Creator<TelephonyTimeSuggestion> CREATOR =
57             new Parcelable.Creator<TelephonyTimeSuggestion>() {
58                 public TelephonyTimeSuggestion createFromParcel(Parcel in) {
59                     return TelephonyTimeSuggestion.createFromParcel(in);
60                 }
61 
62                 public TelephonyTimeSuggestion[] newArray(int size) {
63                     return new TelephonyTimeSuggestion[size];
64                 }
65             };
66 
67     private final int mSlotIndex;
68     @Nullable private final TimestampedValue<Long> mUtcTime;
69     @Nullable private ArrayList<String> mDebugInfo;
70 
TelephonyTimeSuggestion(Builder builder)71     private TelephonyTimeSuggestion(Builder builder) {
72         mSlotIndex = builder.mSlotIndex;
73         mUtcTime = builder.mUtcTime;
74         mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
75     }
76 
createFromParcel(Parcel in)77     private static TelephonyTimeSuggestion createFromParcel(Parcel in) {
78         int slotIndex = in.readInt();
79         TelephonyTimeSuggestion suggestion = new TelephonyTimeSuggestion.Builder(slotIndex)
80                 .setUtcTime(in.readParcelable(null /* classLoader */))
81                 .build();
82         @SuppressWarnings("unchecked")
83         ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
84         if (debugInfo != null) {
85             suggestion.addDebugInfo(debugInfo);
86         }
87         return suggestion;
88     }
89 
90     @Override
describeContents()91     public int describeContents() {
92         return 0;
93     }
94 
95     @Override
writeToParcel(@onNull Parcel dest, int flags)96     public void writeToParcel(@NonNull Parcel dest, int flags) {
97         dest.writeInt(mSlotIndex);
98         dest.writeParcelable(mUtcTime, 0);
99         dest.writeList(mDebugInfo);
100     }
101 
102     /**
103      * Returns an identifier for the source of this suggestion.
104      *
105      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}.
106      */
getSlotIndex()107     public int getSlotIndex() {
108         return mSlotIndex;
109     }
110 
111     /**
112      * Returns the suggested time or {@code null} if there isn't one.
113      *
114      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}.
115      */
116     @Nullable
getUtcTime()117     public TimestampedValue<Long> getUtcTime() {
118         return mUtcTime;
119     }
120 
121     /**
122      * Returns debug metadata for the suggestion.
123      *
124      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
125      */
126     @NonNull
getDebugInfo()127     public List<String> getDebugInfo() {
128         return mDebugInfo == null
129                 ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
130     }
131 
132     /**
133      * Associates information with the instance that can be useful for debugging / logging.
134      *
135      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
136      */
addDebugInfo(@onNull String debugInfo)137     public void addDebugInfo(@NonNull String debugInfo) {
138         if (mDebugInfo == null) {
139             mDebugInfo = new ArrayList<>();
140         }
141         mDebugInfo.add(debugInfo);
142     }
143 
144     /**
145      * Associates information with the instance that can be useful for debugging / logging.
146      *
147      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
148      */
addDebugInfo(@onNull List<String> debugInfo)149     public void addDebugInfo(@NonNull List<String> debugInfo) {
150         if (mDebugInfo == null) {
151             mDebugInfo = new ArrayList<>(debugInfo.size());
152         }
153         mDebugInfo.addAll(debugInfo);
154     }
155 
156     @Override
equals(Object o)157     public boolean equals(Object o) {
158         if (this == o) {
159             return true;
160         }
161         if (o == null || getClass() != o.getClass()) {
162             return false;
163         }
164         TelephonyTimeSuggestion that = (TelephonyTimeSuggestion) o;
165         return mSlotIndex == that.mSlotIndex
166                 && Objects.equals(mUtcTime, that.mUtcTime);
167     }
168 
169     @Override
hashCode()170     public int hashCode() {
171         return Objects.hash(mSlotIndex, mUtcTime);
172     }
173 
174     @Override
toString()175     public String toString() {
176         return "TelephonyTimeSuggestion{"
177                 + "mSlotIndex='" + mSlotIndex + '\''
178                 + ", mUtcTime=" + mUtcTime
179                 + ", mDebugInfo=" + mDebugInfo
180                 + '}';
181     }
182 
183     /**
184      * Builds {@link TelephonyTimeSuggestion} instances.
185      *
186      * @hide
187      */
188     public static final class Builder {
189         private final int mSlotIndex;
190         @Nullable private TimestampedValue<Long> mUtcTime;
191         @Nullable private List<String> mDebugInfo;
192 
193         /**
194          * Creates a builder with the specified {@code slotIndex}.
195          *
196          * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}.
197          */
Builder(int slotIndex)198         public Builder(int slotIndex) {
199             mSlotIndex = slotIndex;
200         }
201 
202         /**
203          * Returns the builder for call chaining.
204          *
205          * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}.
206          */
207         @NonNull
setUtcTime(@ullable TimestampedValue<Long> utcTime)208         public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
209             if (utcTime != null) {
210                 // utcTime can be null, but the value it holds cannot.
211                 Objects.requireNonNull(utcTime.getValue());
212             }
213 
214             mUtcTime = utcTime;
215             return this;
216         }
217 
218         /**
219          * Returns the builder for call chaining.
220          *
221          * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
222          */
223         @NonNull
addDebugInfo(@onNull String debugInfo)224         public Builder addDebugInfo(@NonNull String debugInfo) {
225             if (mDebugInfo == null) {
226                 mDebugInfo = new ArrayList<>();
227             }
228             mDebugInfo.add(debugInfo);
229             return this;
230         }
231 
232         /** Returns the {@link TelephonyTimeSuggestion}. */
233         @NonNull
build()234         public TelephonyTimeSuggestion build() {
235             return new TelephonyTimeSuggestion(this);
236         }
237     }
238 }
239