1 /*
2  * Copyright (C) 2021 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.time;
18 
19 import android.annotation.CurrentTimeMillisLong;
20 import android.annotation.ElapsedRealtimeLong;
21 import android.annotation.NonNull;
22 import android.annotation.SystemApi;
23 import android.app.timedetector.TimeSuggestionHelper;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.ShellCommand;
27 
28 import java.io.PrintWriter;
29 import java.util.List;
30 import java.util.Objects;
31 
32 /**
33  * A time signal from an External source.
34  *
35  * <p>External time suggestions are for use in situations where the Android device is part of a
36  * wider network of devices that are required to use a single time source, and where authority for
37  * the time is external to the Android device. For example, for the Android Auto use case where the
38  * Android device is part of a wider in-car network of devices that should display the same time.
39  *
40  * <p>Android allows for a single external source for time. If there are several external sources
41  * then it is left to the caller to prioritize / filter accordingly to ensure consistency.
42  *
43  * <p>External is one of several time "origins" that the Android platform supports. Stock Android
44  * allows for configuration of which origins can be used and the prioritization between them. Until
45  * an external suggestion is made, the Android device may use its own RTC to initialize the system
46  * clock during boot, and then accept suggestions from the configured origins.
47  *
48  * <p>The creator of an external suggestion is expected to be separate Android process, e.g. a
49  * process integrating with the external time source via a HAL or local network. The creator must
50  * capture the elapsed realtime clock value, e.g. via {@link SystemClock#elapsedRealtime()}, when
51  * the Unix epoch time is first obtained (usually under a wakelock). This enables Android to adjust
52  * for latency introduced between suggestion creation and eventual use. Adjustments for other
53  * sources of latency, i.e. those before the external time suggestion is created, must be handled by
54  * the creator.
55  *
56  * <p>{@code elapsedRealtimeMillis} and {@code suggestionMillis} represent the suggested time.
57  * {@code suggestionMillis} is the number of milliseconds elapsed since 1/1/1970 00:00:00 UTC
58  * according to the Unix time scale. {@code elapsedRealtimeMillis} is the value of the elapsed
59  * realtime clock when {@code suggestionMillis} was established. Note that the elapsed realtime
60  * clock is considered accurate but it is volatile, so time suggestions cannot be persisted across
61  * device resets.
62  *
63  * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
64  * record why the suggestion exists and how it was entered. This information exists only to aid in
65  * debugging and therefore is used by {@link #toString()}, but it is not for use in detection logic
66  * and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
67  *
68  * @hide
69  */
70 @SystemApi
71 public final class ExternalTimeSuggestion implements Parcelable {
72 
73     public static final @NonNull Creator<ExternalTimeSuggestion> CREATOR =
74             new Creator<ExternalTimeSuggestion>() {
75                 public ExternalTimeSuggestion createFromParcel(Parcel in) {
76                     TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel(
77                             ExternalTimeSuggestion.class, in);
78                     return new ExternalTimeSuggestion(helper);
79                 }
80 
81                 public ExternalTimeSuggestion[] newArray(int size) {
82                     return new ExternalTimeSuggestion[size];
83                 }
84             };
85 
86     @NonNull private final TimeSuggestionHelper mTimeSuggestionHelper;
87 
88     /**
89      * Creates a time suggestion cross-referenced to the elapsed realtime clock. See {@link
90      * ExternalTimeSuggestion} for more details.
91      *
92      * @param elapsedRealtimeMillis the elapsed realtime clock reference for the suggestion
93      * @param suggestionMillis      the suggested time in milliseconds since the start of the
94      *                              Unix epoch
95      */
ExternalTimeSuggestion(@lapsedRealtimeLong long elapsedRealtimeMillis, @CurrentTimeMillisLong long suggestionMillis)96     public ExternalTimeSuggestion(@ElapsedRealtimeLong long elapsedRealtimeMillis,
97             @CurrentTimeMillisLong long suggestionMillis) {
98         mTimeSuggestionHelper = new TimeSuggestionHelper(ExternalTimeSuggestion.class,
99                 new UnixEpochTime(elapsedRealtimeMillis, suggestionMillis));
100     }
101 
ExternalTimeSuggestion(@onNull TimeSuggestionHelper helper)102     private ExternalTimeSuggestion(@NonNull TimeSuggestionHelper helper) {
103         mTimeSuggestionHelper = Objects.requireNonNull(helper);
104     }
105 
106     @Override
describeContents()107     public int describeContents() {
108         return 0;
109     }
110 
111     @Override
writeToParcel(@onNull Parcel dest, int flags)112     public void writeToParcel(@NonNull Parcel dest, int flags) {
113         mTimeSuggestionHelper.handleWriteToParcel(dest, flags);
114     }
115 
116     /**
117      * {@hide}
118      */
119     @NonNull
getUnixEpochTime()120     public UnixEpochTime getUnixEpochTime() {
121         return mTimeSuggestionHelper.getUnixEpochTime();
122     }
123 
124     /**
125      * Returns information that can be useful for debugging / logging. See {@link #addDebugInfo}.
126      */
127     @NonNull
getDebugInfo()128     public List<String> getDebugInfo() {
129         return mTimeSuggestionHelper.getDebugInfo();
130     }
131 
132     /**
133      * Associates information with the instance that can be useful for debugging / logging. The
134      * information is present in {@link #toString()} but is not considered for {@link
135      * #equals(Object)} and {@link #hashCode()}.
136      */
addDebugInfo(@onNull String... debugInfos)137     public void addDebugInfo(@NonNull String... debugInfos) {
138         mTimeSuggestionHelper.addDebugInfo(debugInfos);
139     }
140 
141     @Override
equals(Object o)142     public boolean equals(Object o) {
143         if (this == o) {
144             return true;
145         }
146         if (o == null || getClass() != o.getClass()) {
147             return false;
148         }
149         ExternalTimeSuggestion that = (ExternalTimeSuggestion) o;
150         return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper);
151     }
152 
153     @Override
hashCode()154     public int hashCode() {
155         return mTimeSuggestionHelper.hashCode();
156     }
157 
158     @Override
toString()159     public String toString() {
160         return mTimeSuggestionHelper.handleToString();
161     }
162 
163     /** @hide */
parseCommandLineArg(@onNull ShellCommand cmd)164     public static ExternalTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd)
165             throws IllegalArgumentException {
166         return new ExternalTimeSuggestion(
167                 TimeSuggestionHelper.handleParseCommandLineArg(ExternalTimeSuggestion.class, cmd));
168     }
169 
170     /** @hide */
printCommandLineOpts(PrintWriter pw)171     public static void printCommandLineOpts(PrintWriter pw) {
172         TimeSuggestionHelper.handlePrintCommandLineOpts(
173                 pw, "External", ExternalTimeSuggestion.class);
174     }
175 }
176