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