• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.time.UnixEpochTime;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.ShellCommand;
25 
26 import java.io.PrintWriter;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Objects;
31 
32 /**
33  * A time suggestion from an identified telephony source. e.g. from NITZ information from a specific
34  * radio.
35  *
36  * <p>{@code slotIndex} identifies the suggestion source. This enables detection logic to identify
37  * suggestions from the same source when there are several in use.
38  *
39  * <p>{@code unixEpochTime}. When not {@code null}, the {@code unixEpochTime.value} is the number of
40  * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code unixEpochTime.referenceTimeMillis}
41  * is the value of the elapsed realtime clock when the {@code unixEpochTime.value} was established.
42  * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
43  * suggestions cannot be persisted across device resets. {@code unixEpochTime} can be {@code null}
44  * to indicate that the telephony source has entered an "un-opinionated" state and any previous
45  * suggestion from the source is being withdrawn.
46  *
47  * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
48  * record why the suggestion exists, e.g. what triggered it to be made and what heuristic was used
49  * to determine the time or its absence. This information exists only to aid in debugging and
50  * therefore is used by {@link #toString()}, but it is not for use in detection logic and is not
51  * considered in {@link #hashCode()} or {@link #equals(Object)}.
52  *
53  * @hide
54  */
55 public final class TelephonyTimeSuggestion implements Parcelable {
56 
57     /** @hide */
58     public static final @NonNull Parcelable.Creator<TelephonyTimeSuggestion> CREATOR =
59             new Parcelable.Creator<TelephonyTimeSuggestion>() {
60                 public TelephonyTimeSuggestion createFromParcel(Parcel in) {
61                     return TelephonyTimeSuggestion.createFromParcel(in);
62                 }
63 
64                 public TelephonyTimeSuggestion[] newArray(int size) {
65                     return new TelephonyTimeSuggestion[size];
66                 }
67             };
68 
69     private final int mSlotIndex;
70     @Nullable private final UnixEpochTime mUnixEpochTime;
71     @Nullable private ArrayList<String> mDebugInfo;
72 
TelephonyTimeSuggestion(Builder builder)73     private TelephonyTimeSuggestion(Builder builder) {
74         mSlotIndex = builder.mSlotIndex;
75         mUnixEpochTime = builder.mUnixEpochTime;
76         mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
77     }
78 
createFromParcel(Parcel in)79     private static TelephonyTimeSuggestion createFromParcel(Parcel in) {
80         int slotIndex = in.readInt();
81         UnixEpochTime unixEpochTime =
82                 in.readParcelable(null /* classLoader */, UnixEpochTime.class);
83         TelephonyTimeSuggestion suggestion = new TelephonyTimeSuggestion.Builder(slotIndex)
84                 .setUnixEpochTime(unixEpochTime)
85                 .build();
86         @SuppressWarnings("unchecked")
87         ArrayList<String> debugInfo = in.readArrayList(
88                 null /* classLoader */, java.lang.String.class);
89         if (debugInfo != null) {
90             suggestion.addDebugInfo(debugInfo);
91         }
92         return suggestion;
93     }
94 
95     /** @hide */
parseCommandLineArg(@onNull ShellCommand cmd)96     public static TelephonyTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd)
97             throws IllegalArgumentException {
98         Integer slotIndex = null;
99         Long elapsedRealtimeMillis = null;
100         Long unixEpochTimeMillis = null;
101         String opt;
102         while ((opt = cmd.getNextArg()) != null) {
103             switch (opt) {
104                 case "--slot_index": {
105                     slotIndex = Integer.parseInt(cmd.getNextArgRequired());
106                     break;
107                 }
108                 case "--reference_time":
109                 case "--elapsed_realtime": {
110                     elapsedRealtimeMillis = Long.parseLong(cmd.getNextArgRequired());
111                     break;
112                 }
113                 case "--unix_epoch_time": {
114                     unixEpochTimeMillis = Long.parseLong(cmd.getNextArgRequired());
115                     break;
116                 }
117                 default: {
118                     throw new IllegalArgumentException("Unknown option: " + opt);
119                 }
120             }
121         }
122 
123         if (slotIndex == null) {
124             throw new IllegalArgumentException("No slotIndex specified.");
125         }
126         if (elapsedRealtimeMillis == null) {
127             throw new IllegalArgumentException("No elapsedRealtimeMillis specified.");
128         }
129         if (unixEpochTimeMillis == null) {
130             throw new IllegalArgumentException("No unixEpochTimeMillis specified.");
131         }
132 
133         UnixEpochTime timeSignal = new UnixEpochTime(elapsedRealtimeMillis, unixEpochTimeMillis);
134         Builder builder = new Builder(slotIndex)
135                 .setUnixEpochTime(timeSignal)
136                 .addDebugInfo("Command line injection");
137         return builder.build();
138     }
139 
140     /** @hide */
printCommandLineOpts(PrintWriter pw)141     public static void printCommandLineOpts(PrintWriter pw) {
142         pw.println("Telephony suggestion options:");
143         pw.println("  --slot_index <number>");
144         pw.println("  --elapsed_realtime <elapsed realtime millis>");
145         pw.println("  --unix_epoch_time <Unix epoch time millis>");
146         pw.println();
147         pw.println("See " + TelephonyTimeSuggestion.class.getName() + " for more information");
148     }
149 
150     @Override
describeContents()151     public int describeContents() {
152         return 0;
153     }
154 
155     @Override
writeToParcel(@onNull Parcel dest, int flags)156     public void writeToParcel(@NonNull Parcel dest, int flags) {
157         dest.writeInt(mSlotIndex);
158         dest.writeParcelable(mUnixEpochTime, 0);
159         dest.writeList(mDebugInfo);
160     }
161 
162     /**
163      * Returns an identifier for the source of this suggestion.
164      *
165      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}.
166      */
getSlotIndex()167     public int getSlotIndex() {
168         return mSlotIndex;
169     }
170 
171     /**
172      * Returns the suggested time or {@code null} if there isn't one.
173      *
174      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code unixEpochTime}.
175      */
176     @Nullable
getUnixEpochTime()177     public UnixEpochTime getUnixEpochTime() {
178         return mUnixEpochTime;
179     }
180 
181     /**
182      * Returns debug metadata for the suggestion.
183      *
184      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
185      */
186     @NonNull
getDebugInfo()187     public List<String> getDebugInfo() {
188         return mDebugInfo == null
189                 ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
190     }
191 
192     /**
193      * Associates information with the instance that can be useful for debugging / logging.
194      *
195      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
196      */
addDebugInfo(@onNull String debugInfo)197     public void addDebugInfo(@NonNull String debugInfo) {
198         if (mDebugInfo == null) {
199             mDebugInfo = new ArrayList<>();
200         }
201         mDebugInfo.add(debugInfo);
202     }
203 
204     /**
205      * Associates information with the instance that can be useful for debugging / logging.
206      *
207      * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
208      */
addDebugInfo(@onNull List<String> debugInfo)209     public void addDebugInfo(@NonNull List<String> debugInfo) {
210         if (mDebugInfo == null) {
211             mDebugInfo = new ArrayList<>(debugInfo.size());
212         }
213         mDebugInfo.addAll(debugInfo);
214     }
215 
216     @Override
equals(@ullable Object o)217     public boolean equals(@Nullable Object o) {
218         if (this == o) {
219             return true;
220         }
221         if (o == null || getClass() != o.getClass()) {
222             return false;
223         }
224         TelephonyTimeSuggestion that = (TelephonyTimeSuggestion) o;
225         return mSlotIndex == that.mSlotIndex
226                 && Objects.equals(mUnixEpochTime, that.mUnixEpochTime);
227     }
228 
229     @Override
hashCode()230     public int hashCode() {
231         return Objects.hash(mSlotIndex, mUnixEpochTime);
232     }
233 
234     @Override
toString()235     public String toString() {
236         return "TelephonyTimeSuggestion{"
237                 + "mSlotIndex='" + mSlotIndex + '\''
238                 + ", mUnixEpochTime=" + mUnixEpochTime
239                 + ", mDebugInfo=" + mDebugInfo
240                 + '}';
241     }
242 
243     /**
244      * Builds {@link TelephonyTimeSuggestion} instances.
245      *
246      * @hide
247      */
248     public static final class Builder {
249         private final int mSlotIndex;
250         @Nullable private UnixEpochTime mUnixEpochTime;
251         @Nullable private List<String> mDebugInfo;
252 
253         /**
254          * Creates a builder with the specified {@code slotIndex}.
255          *
256          * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}.
257          */
Builder(int slotIndex)258         public Builder(int slotIndex) {
259             mSlotIndex = slotIndex;
260         }
261 
262         /**
263          * Returns the builder for call chaining.
264          *
265          * <p>See {@link TelephonyTimeSuggestion} for more information about {@code unixEpochTime}.
266          */
267         @NonNull
setUnixEpochTime(@ullable UnixEpochTime unixEpochTime)268         public Builder setUnixEpochTime(@Nullable UnixEpochTime unixEpochTime) {
269             mUnixEpochTime = unixEpochTime;
270             return this;
271         }
272 
273         /**
274          * Returns the builder for call chaining.
275          *
276          * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}.
277          */
278         @NonNull
addDebugInfo(@onNull String debugInfo)279         public Builder addDebugInfo(@NonNull String debugInfo) {
280             if (mDebugInfo == null) {
281                 mDebugInfo = new ArrayList<>();
282             }
283             mDebugInfo.add(debugInfo);
284             return this;
285         }
286 
287         /** Returns the {@link TelephonyTimeSuggestion}. */
288         @NonNull
build()289         public TelephonyTimeSuggestion build() {
290             return new TelephonyTimeSuggestion(this);
291         }
292     }
293 }
294