• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.server.timezonedetector;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.timezonedetector.ManualTimeZoneSuggestion;
23 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
24 
25 import java.lang.annotation.ElementType;
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.lang.annotation.Target;
29 import java.util.Arrays;
30 import java.util.List;
31 import java.util.Objects;
32 
33 /**
34  * A class that provides time zone detector state information for metrics.
35  *
36  * <p>
37  * Regarding the use of time zone ID ordinals in metrics / telemetry:
38  * <p>
39  * For general metrics, we don't want to leak user location information by reporting time zone
40  * IDs. Instead, time zone IDs are consistently identified within a given instance of this class by
41  * a numeric ID (ordinal). This allows comparison of IDs without revealing what those IDs are.
42  * See {@link #isEnhancedMetricsCollectionEnabled()} for the setting that enables actual IDs to be
43  * collected.
44  */
45 public final class MetricsTimeZoneDetectorState {
46 
47     @IntDef(prefix = "DETECTION_MODE_",
48             value = { DETECTION_MODE_UNKNOWN, DETECTION_MODE_MANUAL, DETECTION_MODE_GEO,
49                     DETECTION_MODE_TELEPHONY }
50     )
51     @Retention(RetentionPolicy.SOURCE)
52     @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
53     public @interface DetectionMode {};
54 
55     public static final @DetectionMode int DETECTION_MODE_UNKNOWN = 0;
56     public static final @DetectionMode int DETECTION_MODE_MANUAL = 1;
57     public static final @DetectionMode int DETECTION_MODE_GEO = 2;
58     public static final @DetectionMode int DETECTION_MODE_TELEPHONY = 3;
59 
60     @NonNull private final ConfigurationInternal mConfigurationInternal;
61     private final int mDeviceTimeZoneIdOrdinal;
62     @Nullable private final String mDeviceTimeZoneId;
63     @Nullable private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
64     @Nullable private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
65     @Nullable private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
66 
MetricsTimeZoneDetectorState( @onNull ConfigurationInternal configurationInternal, int deviceTimeZoneIdOrdinal, @Nullable String deviceTimeZoneId, @Nullable MetricsTimeZoneSuggestion latestManualSuggestion, @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion, @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion)67     private MetricsTimeZoneDetectorState(
68             @NonNull ConfigurationInternal configurationInternal,
69             int deviceTimeZoneIdOrdinal,
70             @Nullable String deviceTimeZoneId,
71             @Nullable MetricsTimeZoneSuggestion latestManualSuggestion,
72             @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion,
73             @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) {
74         mConfigurationInternal = Objects.requireNonNull(configurationInternal);
75         mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal;
76         mDeviceTimeZoneId = deviceTimeZoneId;
77         mLatestManualSuggestion = latestManualSuggestion;
78         mLatestTelephonySuggestion = latestTelephonySuggestion;
79         mLatestGeolocationSuggestion = latestGeolocationSuggestion;
80     }
81 
82     /**
83      * Creates {@link MetricsTimeZoneDetectorState} from the supplied parameters, using the {@link
84      * OrdinalGenerator} to generate time zone ID ordinals.
85      */
create( @onNull OrdinalGenerator<String> tzIdOrdinalGenerator, @NonNull ConfigurationInternal configurationInternal, @NonNull String deviceTimeZoneId, @Nullable ManualTimeZoneSuggestion latestManualSuggestion, @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion, @Nullable LocationAlgorithmEvent latestLocationAlgorithmEvent)86     public static MetricsTimeZoneDetectorState create(
87             @NonNull OrdinalGenerator<String> tzIdOrdinalGenerator,
88             @NonNull ConfigurationInternal configurationInternal,
89             @NonNull String deviceTimeZoneId,
90             @Nullable ManualTimeZoneSuggestion latestManualSuggestion,
91             @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
92             @Nullable LocationAlgorithmEvent latestLocationAlgorithmEvent) {
93 
94         boolean includeZoneIds = configurationInternal.isEnhancedMetricsCollectionEnabled();
95         String metricDeviceTimeZoneId = includeZoneIds ? deviceTimeZoneId : null;
96         int deviceTimeZoneIdOrdinal =
97                 tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
98         MetricsTimeZoneSuggestion latestCanonicalManualSuggestion =
99                 createMetricsTimeZoneSuggestion(
100                         tzIdOrdinalGenerator, latestManualSuggestion, includeZoneIds);
101         MetricsTimeZoneSuggestion latestCanonicalTelephonySuggestion =
102                 createMetricsTimeZoneSuggestion(
103                         tzIdOrdinalGenerator, latestTelephonySuggestion, includeZoneIds);
104 
105         MetricsTimeZoneSuggestion latestCanonicalGeolocationSuggestion = null;
106         if (latestLocationAlgorithmEvent != null) {
107             GeolocationTimeZoneSuggestion suggestion = latestLocationAlgorithmEvent.getSuggestion();
108             latestCanonicalGeolocationSuggestion = createMetricsTimeZoneSuggestion(
109                     tzIdOrdinalGenerator, suggestion, includeZoneIds);
110         }
111 
112         return new MetricsTimeZoneDetectorState(
113                 configurationInternal, deviceTimeZoneIdOrdinal, metricDeviceTimeZoneId,
114                 latestCanonicalManualSuggestion, latestCanonicalTelephonySuggestion,
115                 latestCanonicalGeolocationSuggestion);
116     }
117 
118     /** Returns true if the device supports telephony time zone detection. */
isTelephonyDetectionSupported()119     public boolean isTelephonyDetectionSupported() {
120         return mConfigurationInternal.isTelephonyDetectionSupported();
121     }
122 
123     /** Returns true if the device supports geolocation time zone detection. */
isGeoDetectionSupported()124     public boolean isGeoDetectionSupported() {
125         return mConfigurationInternal.isGeoDetectionSupported();
126     }
127 
128     /** Returns true if the device supports telephony time zone detection fallback. */
isTelephonyTimeZoneFallbackSupported()129     public boolean isTelephonyTimeZoneFallbackSupported() {
130         return mConfigurationInternal.isTelephonyFallbackSupported();
131     }
132 
133     /**
134      * Returns {@code true} if location time zone detection should run all the time on supported
135      * devices, even when the user has not enabled it explicitly in settings. Enabled for internal
136      * testing only.
137      */
getGeoDetectionRunInBackgroundEnabled()138     public boolean getGeoDetectionRunInBackgroundEnabled() {
139         return mConfigurationInternal.getGeoDetectionRunInBackgroundEnabledSetting();
140     }
141 
142     /** Returns true if enhanced metric collection is enabled. */
isEnhancedMetricsCollectionEnabled()143     public boolean isEnhancedMetricsCollectionEnabled() {
144         return mConfigurationInternal.isEnhancedMetricsCollectionEnabled();
145     }
146 
147     /** Returns true if user's location can be used generally. */
getUserLocationEnabledSetting()148     public boolean getUserLocationEnabledSetting() {
149         return mConfigurationInternal.getLocationEnabledSetting();
150     }
151 
152     /** Returns the value of the geolocation time zone detection enabled setting. */
getGeoDetectionEnabledSetting()153     public boolean getGeoDetectionEnabledSetting() {
154         return mConfigurationInternal.getGeoDetectionEnabledSetting();
155     }
156 
157     /** Returns the value of the auto time zone detection enabled setting. */
getAutoDetectionEnabledSetting()158     public boolean getAutoDetectionEnabledSetting() {
159         return mConfigurationInternal.getAutoDetectionEnabledSetting();
160     }
161 
162     /**
163      * Returns the detection mode the device is currently using, which can be influenced by various
164      * things besides the user's setting.
165      */
getDetectionMode()166     public @DetectionMode int getDetectionMode() {
167         switch (mConfigurationInternal.getDetectionMode()) {
168             case ConfigurationInternal.DETECTION_MODE_MANUAL:
169                 return DETECTION_MODE_MANUAL;
170             case ConfigurationInternal.DETECTION_MODE_GEO:
171                 return DETECTION_MODE_GEO;
172             case ConfigurationInternal.DETECTION_MODE_TELEPHONY:
173                 return DETECTION_MODE_TELEPHONY;
174             default:
175                 return DETECTION_MODE_UNKNOWN;
176         }
177     }
178 
179     /**
180      * Returns the ordinal for the device's current time zone ID.
181      * See {@link MetricsTimeZoneDetectorState} for information about ordinals.
182      */
getDeviceTimeZoneIdOrdinal()183     public int getDeviceTimeZoneIdOrdinal() {
184         return mDeviceTimeZoneIdOrdinal;
185     }
186 
187     /**
188      * Returns the device's current time zone ID. This will only be populated if {@link
189      * #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link
190      * MetricsTimeZoneDetectorState} for details.
191      */
192     @Nullable
getDeviceTimeZoneId()193     public String getDeviceTimeZoneId() {
194         return mDeviceTimeZoneId;
195     }
196 
197     /**
198      * Returns a canonical form of the last manual suggestion received.
199      */
200     @Nullable
getLatestManualSuggestion()201     public MetricsTimeZoneSuggestion getLatestManualSuggestion() {
202         return mLatestManualSuggestion;
203     }
204 
205     /**
206      * Returns a canonical form of the last telephony suggestion received.
207      */
208     @Nullable
getLatestTelephonySuggestion()209     public MetricsTimeZoneSuggestion getLatestTelephonySuggestion() {
210         return mLatestTelephonySuggestion;
211     }
212 
213     /**
214      * Returns a canonical form of last geolocation suggestion received.
215      */
216     @Nullable
getLatestGeolocationSuggestion()217     public MetricsTimeZoneSuggestion getLatestGeolocationSuggestion() {
218         return mLatestGeolocationSuggestion;
219     }
220 
221     @Override
equals(Object o)222     public boolean equals(Object o) {
223         if (this == o) {
224             return true;
225         }
226         if (o == null || getClass() != o.getClass()) {
227             return false;
228         }
229         MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o;
230         return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal
231                 && Objects.equals(mDeviceTimeZoneId, that.mDeviceTimeZoneId)
232                 && mConfigurationInternal.equals(that.mConfigurationInternal)
233                 && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion)
234                 && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion)
235                 && Objects.equals(mLatestGeolocationSuggestion, that.mLatestGeolocationSuggestion);
236     }
237 
238     @Override
hashCode()239     public int hashCode() {
240         return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, mDeviceTimeZoneId,
241                 mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion);
242     }
243 
244     @Override
toString()245     public String toString() {
246         return "MetricsTimeZoneDetectorState{"
247                 + "mConfigurationInternal=" + mConfigurationInternal
248                 + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
249                 + ", mDeviceTimeZoneId=" + mDeviceTimeZoneId
250                 + ", mLatestManualSuggestion=" + mLatestManualSuggestion
251                 + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
252                 + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
253                 + '}';
254     }
255 
256     @Nullable
createMetricsTimeZoneSuggestion( @onNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @NonNull ManualTimeZoneSuggestion manualSuggestion, boolean includeFullZoneIds)257     private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
258             @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
259             @NonNull ManualTimeZoneSuggestion manualSuggestion,
260             boolean includeFullZoneIds) {
261         if (manualSuggestion == null) {
262             return null;
263         }
264 
265         String suggestionZoneId = manualSuggestion.getZoneId();
266         String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null;
267         int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) };
268         return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals);
269     }
270 
271     @Nullable
createMetricsTimeZoneSuggestion( @onNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @NonNull TelephonyTimeZoneSuggestion telephonySuggestion, boolean includeFullZoneIds)272     private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
273             @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
274             @NonNull TelephonyTimeZoneSuggestion telephonySuggestion,
275             boolean includeFullZoneIds) {
276         if (telephonySuggestion == null) {
277             return null;
278         }
279         String suggestionZoneId = telephonySuggestion.getZoneId();
280         if (suggestionZoneId == null) {
281             return MetricsTimeZoneSuggestion.createUncertain();
282         }
283         String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null;
284         int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) };
285         return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals);
286     }
287 
288     @Nullable
createMetricsTimeZoneSuggestion( @onNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion, boolean includeFullZoneIds)289     private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
290             @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
291             @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion,
292             boolean includeFullZoneIds) {
293         if (geolocationSuggestion == null) {
294             return null;
295         }
296 
297         List<String> zoneIds = geolocationSuggestion.getZoneIds();
298         if (zoneIds == null) {
299             return MetricsTimeZoneSuggestion.createUncertain();
300         }
301         String[] metricZoneIds = includeFullZoneIds ? zoneIds.toArray(new String[0]) : null;
302         int[] zoneIdOrdinals = zoneIdOrdinalGenerator.ordinals(zoneIds);
303         return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals);
304     }
305 
306     /**
307      * A Java class that represents a generic time zone suggestion, i.e. one that is independent of
308      * origin-specific information. This closely matches the metrics atoms.proto
309      * MetricsTimeZoneSuggestion proto definition.
310      */
311     public static final class MetricsTimeZoneSuggestion {
312         @Nullable private final String[] mZoneIds;
313         @Nullable private final int[] mZoneIdOrdinals;
314 
MetricsTimeZoneSuggestion( @ullable String[] zoneIds, @Nullable int[] zoneIdOrdinals)315         private MetricsTimeZoneSuggestion(
316                 @Nullable String[] zoneIds, @Nullable int[] zoneIdOrdinals) {
317             mZoneIds = zoneIds;
318             mZoneIdOrdinals = zoneIdOrdinals;
319         }
320 
321         @NonNull
createUncertain()322         static MetricsTimeZoneSuggestion createUncertain() {
323             return new MetricsTimeZoneSuggestion(null, null);
324         }
325 
326         @NonNull
createCertain( @ullable String[] zoneIds, @NonNull int[] zoneIdOrdinals)327         static MetricsTimeZoneSuggestion createCertain(
328                 @Nullable String[] zoneIds, @NonNull int[] zoneIdOrdinals) {
329             return new MetricsTimeZoneSuggestion(zoneIds, zoneIdOrdinals);
330         }
331 
isCertain()332         public boolean isCertain() {
333             return mZoneIdOrdinals != null;
334         }
335 
336         /**
337          * Returns ordinals for the time zone IDs contained in the suggestion.
338          * See {@link MetricsTimeZoneDetectorState} for information about ordinals.
339          */
340         @Nullable
getZoneIdOrdinals()341         public int[] getZoneIdOrdinals() {
342             return mZoneIdOrdinals;
343         }
344 
345         /**
346          * Returns the time zone IDs contained in the suggestion. This will only be populated if
347          * {@link #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link
348          * MetricsTimeZoneDetectorState} for details.
349          */
350         @Nullable
getZoneIds()351         public String[] getZoneIds() {
352             return mZoneIds;
353         }
354 
355         @Override
equals(Object o)356         public boolean equals(Object o) {
357             if (this == o) {
358                 return true;
359             }
360             if (o == null || getClass() != o.getClass()) {
361                 return false;
362             }
363             MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o;
364             return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals)
365                     && Arrays.equals(mZoneIds, that.mZoneIds);
366         }
367 
368         @Override
hashCode()369         public int hashCode() {
370             int result = Arrays.hashCode(mZoneIds);
371             result = 31 * result + Arrays.hashCode(mZoneIdOrdinals);
372             return result;
373         }
374 
375         @Override
toString()376         public String toString() {
377             return "MetricsTimeZoneSuggestion{"
378                     + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals)
379                     + ", mZoneIds=" + Arrays.toString(mZoneIds)
380                     + '}';
381         }
382     }
383 }
384