1 /* 2 * Copyright (C) 2020 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.service.timezone; 18 19 import android.annotation.ElapsedRealtimeLong; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.os.SystemClock; 26 27 import java.time.Duration; 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 import java.util.Objects; 32 33 /** 34 * A suggestion from a {@link TimeZoneProviderService} containing zero or more time zones. 35 * 36 * @hide 37 */ 38 @SystemApi 39 public final class TimeZoneProviderSuggestion implements Parcelable { 40 41 @NonNull 42 private final List<String> mTimeZoneIds; 43 44 @ElapsedRealtimeLong 45 private final long mElapsedRealtimeMillis; 46 TimeZoneProviderSuggestion(@onNull List<String> timeZoneIds, @ElapsedRealtimeLong long elapsedRealtimeMillis)47 private TimeZoneProviderSuggestion(@NonNull List<String> timeZoneIds, 48 @ElapsedRealtimeLong long elapsedRealtimeMillis) { 49 mTimeZoneIds = immutableList(timeZoneIds); 50 mElapsedRealtimeMillis = elapsedRealtimeMillis; 51 } 52 53 /** 54 * Returns the time of the suggestion in elapsed real-time since system boot. Where possible, 55 * the time should be based on the time of the data used when determining time zone. For 56 * example, if it was based on a {@link android.location.Location} then it should be the time 57 * associated with that location. 58 * 59 * <p>This value is compared to {@link 60 * android.os.SystemClock#elapsedRealtime()}, to calculate the age of a fix and to compare 61 * {@link TimeZoneProviderSuggestion} instances. 62 * 63 * @return elapsed real-time of fix, in milliseconds 64 */ 65 @ElapsedRealtimeLong getElapsedRealtimeMillis()66 public long getElapsedRealtimeMillis() { 67 return mElapsedRealtimeMillis; 68 } 69 70 /** 71 * Returns the zero or more time zone IDs for this suggestion. 72 * 73 * <p>Time zone IDs are TZDB IDs like "America/Los_Angeles" that would be accepted by {@link 74 * java.util.TimeZone#getTimeZone(String)}. 75 * 76 * <p>Most often a suggestion will contain a single time zone ID but other possibilities are 77 * valid. A suggestion with zero time zone IDs means the provider is certain there are no time 78 * zones for the current location, e.g. for oceans, boundaries or disputed areas. A suggestion 79 * with multiple IDs can occur on boundaries or disputed areas. The ordering should be in order 80 * of likelihood if possible, but the time zone detection service may choose from any of the 81 * zones suggested if it has other supporting information available. 82 */ 83 @NonNull getTimeZoneIds()84 public List<String> getTimeZoneIds() { 85 return mTimeZoneIds; 86 } 87 88 @Override toString()89 public String toString() { 90 return "TimeZoneProviderSuggestion{" 91 + "mTimeZoneIds=" + mTimeZoneIds 92 + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis 93 + "(" + Duration.ofMillis(mElapsedRealtimeMillis) + ")" 94 + '}'; 95 } 96 97 public static final @NonNull Creator<TimeZoneProviderSuggestion> CREATOR = 98 new Creator<TimeZoneProviderSuggestion>() { 99 @Override 100 public TimeZoneProviderSuggestion createFromParcel(Parcel in) { 101 @SuppressWarnings("unchecked") 102 ArrayList<String> timeZoneIds = 103 (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); 104 long elapsedRealtimeMillis = in.readLong(); 105 return new TimeZoneProviderSuggestion(timeZoneIds, elapsedRealtimeMillis); 106 } 107 108 @Override 109 public TimeZoneProviderSuggestion[] newArray(int size) { 110 return new TimeZoneProviderSuggestion[size]; 111 } 112 }; 113 114 @Override describeContents()115 public int describeContents() { 116 return 0; 117 } 118 119 @Override writeToParcel(@onNull Parcel parcel, int flags)120 public void writeToParcel(@NonNull Parcel parcel, int flags) { 121 parcel.writeList(mTimeZoneIds); 122 parcel.writeLong(mElapsedRealtimeMillis); 123 } 124 125 /** 126 * Similar to {@link #equals} except this methods checks for equivalence, not equality. 127 * i.e. two suggestions are equivalent if they suggest the same time zones. 128 * 129 * @hide 130 */ 131 @SuppressWarnings("ReferenceEquality") isEquivalentTo(@ullable TimeZoneProviderSuggestion other)132 public boolean isEquivalentTo(@Nullable TimeZoneProviderSuggestion other) { 133 if (this == other) { 134 return true; 135 } 136 if (other == null) { 137 return false; 138 } 139 // Only check the time zone IDs. The times can be different, but we don't mind. 140 return mTimeZoneIds.equals(other.mTimeZoneIds); 141 } 142 143 @Override equals(Object o)144 public boolean equals(Object o) { 145 if (this == o) { 146 return true; 147 } 148 if (o == null || getClass() != o.getClass()) { 149 return false; 150 } 151 TimeZoneProviderSuggestion that = (TimeZoneProviderSuggestion) o; 152 return mElapsedRealtimeMillis == that.mElapsedRealtimeMillis 153 && mTimeZoneIds.equals(that.mTimeZoneIds); 154 } 155 156 @Override hashCode()157 public int hashCode() { 158 return Objects.hash(mTimeZoneIds, mElapsedRealtimeMillis); 159 } 160 161 /** A builder for {@link TimeZoneProviderSuggestion}. */ 162 public static final class Builder { 163 164 private @NonNull List<String> mTimeZoneIds = Collections.emptyList(); 165 @ElapsedRealtimeLong 166 private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime(); 167 168 /** 169 * Sets the time zone IDs of this suggestion. 170 */ 171 @NonNull setTimeZoneIds(@onNull List<String> timeZoneIds)172 public Builder setTimeZoneIds(@NonNull List<String> timeZoneIds) { 173 mTimeZoneIds = Objects.requireNonNull(timeZoneIds); 174 return this; 175 } 176 177 /** 178 * Sets the time of this suggestion, in elapsed real-time since system boot. 179 */ 180 @NonNull setElapsedRealtimeMillis(@lapsedRealtimeLong long time)181 public Builder setElapsedRealtimeMillis(@ElapsedRealtimeLong long time) { 182 mElapsedRealtimeMillis = time; 183 return this; 184 } 185 186 /** 187 * Builds a {@link TimeZoneProviderSuggestion} instance. 188 */ 189 @NonNull build()190 public TimeZoneProviderSuggestion build() { 191 return new TimeZoneProviderSuggestion(mTimeZoneIds, mElapsedRealtimeMillis); 192 } 193 } 194 195 @NonNull immutableList(@onNull List<String> list)196 private static List<String> immutableList(@NonNull List<String> list) { 197 Objects.requireNonNull(list); 198 if (list.isEmpty()) { 199 return Collections.emptyList(); 200 } else { 201 return Collections.unmodifiableList(new ArrayList<>(list)); 202 } 203 } 204 } 205