Loading core/java/android/app/timezonedetector/TimeZoneDetector.java +8 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,14 @@ public interface TimeZoneDetector { */ String SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK = "enable_telephony_fallback"; /** * A shell command that dumps a {@link * com.android.server.timezonedetector.MetricsTimeZoneDetectorState} object to stdout for * debugging. * @hide */ String SHELL_COMMAND_DUMP_METRICS = "dump_metrics"; /** * A shared utility method to create a {@link ManualTimeZoneSuggestion}. * Loading core/proto/android/app/time_zone_detector.proto +22 −11 Original line number Diff line number Diff line Loading @@ -32,16 +32,8 @@ message GeolocationTimeZoneSuggestionProto { } /* * An obfuscated and simplified time zone suggestion for metrics use. * * The suggestion's time zone IDs (which relate to location) are obfuscated by * mapping them to an ordinal. When the ordinal is assigned consistently across * several objects (i.e. so the same time zone ID is always mapped to the same * ordinal), this allows comparisons between those objects. For example, we can * answer "did these two suggestions agree?", "does the suggestion match the * device's current time zone?", without leaking knowledge of location. Ordinals * are also significantly more compact than full IANA TZDB IDs, albeit highly * unstable and of limited use. * A generic-form time zone suggestion for metrics use. Required to be a superset of the * MetricsTimeZoneSuggestion proto defined in atoms.proto to ensure binary compatibility. */ message MetricsTimeZoneSuggestion { option (android.msg_privacy).dest = DEST_AUTOMATIC; Loading @@ -55,5 +47,24 @@ message MetricsTimeZoneSuggestion { // The ordinals for time zone(s) in the suggestion. Always empty for // UNCERTAIN, and can be empty for CERTAIN, for example when the device is in // a disputed area / on an ocean. repeated uint32 time_zone_ordinals = 2; // // The suggestion's time zone IDs (which relate to location) are obfuscated by // mapping them to an ordinal. When the ordinal is assigned consistently across // several objects (i.e. so the same time zone ID is always mapped to the same // ordinal), this allows comparisons between those objects. For example, we can // answer "did these two suggestions agree?", "does the suggestion match the // device's current time zone?", without leaking knowledge of location. Ordinals // are also significantly more compact than full IANA TZDB IDs, albeit unstable // and of limited use. repeated int32 time_zone_ordinals = 2; // The actual time zone ID(s) in the suggestion. Similar to time_zone_ordinals // but contains the actual string IDs. // // This information is only captured / reported for some devices based on the // value of a server side flag, i.e. it could be enabled for internal testers. // Therefore the list can be empty even when time_zone_ordinals is populated. // // When enabled, see time_zone_ordinals for the expected number of values. repeated string time_zone_ids = 3; } services/core/java/com/android/server/timedetector/ServerFlags.java +8 −1 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ public final class ServerFlags { KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE, KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE, KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED, KEY_ENHANCED_METRICS_COLLECTION_ENABLED, }) @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) @Retention(RetentionPolicy.SOURCE) Loading Loading @@ -156,12 +157,18 @@ public final class ServerFlags { "time_detector_origin_priorities_override"; /** * The key to override the time detector lower bound configuration. The values is the number of * The key to override the time detector lower bound configuration. The value is the number of * milliseconds since the beginning of the Unix epoch. */ public static final @DeviceConfigKey String KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE = "time_detector_lower_bound_millis_override"; /** * The key to allow extra metrics / telemetry information to be collected from internal testers. */ public static final @DeviceConfigKey String KEY_ENHANCED_METRICS_COLLECTION_ENABLED = "enhanced_metrics_collection_enabled"; @GuardedBy("mListeners") private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>(); Loading services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +25 −1 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ public final class ConfigurationInternal { private final boolean mTelephonyDetectionSupported; private final boolean mGeoDetectionSupported; private final boolean mTelephonyFallbackSupported; private final boolean mEnhancedMetricsCollectionEnabled; private final boolean mAutoDetectionEnabledSetting; private final @UserIdInt int mUserId; private final boolean mUserConfigAllowed; Loading @@ -50,6 +51,7 @@ public final class ConfigurationInternal { mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported; mGeoDetectionSupported = builder.mGeoDetectionSupported; mTelephonyFallbackSupported = builder.mTelephonyFallbackSupported; mEnhancedMetricsCollectionEnabled = builder.mEnhancedMetricsCollectionEnabled; mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting; mUserId = builder.mUserId; Loading Loading @@ -81,6 +83,15 @@ public final class ConfigurationInternal { return mTelephonyFallbackSupported; } /** * Returns {@code true} if the device can collect / report extra metrics information for QA * / testers. These metrics might involve logging more expensive or more revealing data that * would not be collected from the set of public users. */ public boolean isEnhancedMetricsCollectionEnabled() { return mEnhancedMetricsCollectionEnabled; } /** Returns the value of the auto time zone detection enabled setting. */ public boolean getAutoDetectionEnabledSetting() { return mAutoDetectionEnabledSetting; Loading Loading @@ -227,6 +238,7 @@ public final class ConfigurationInternal { && mTelephonyDetectionSupported == that.mTelephonyDetectionSupported && mGeoDetectionSupported == that.mGeoDetectionSupported && mTelephonyFallbackSupported == that.mTelephonyFallbackSupported && mEnhancedMetricsCollectionEnabled == that.mEnhancedMetricsCollectionEnabled && mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting && mLocationEnabledSetting == that.mLocationEnabledSetting && mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting; Loading @@ -235,7 +247,8 @@ public final class ConfigurationInternal { @Override public int hashCode() { return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported, mGeoDetectionSupported, mTelephonyFallbackSupported, mAutoDetectionEnabledSetting, mGeoDetectionSupported, mTelephonyFallbackSupported, mEnhancedMetricsCollectionEnabled, mAutoDetectionEnabledSetting, mLocationEnabledSetting, mGeoDetectionEnabledSetting); } Loading @@ -247,6 +260,7 @@ public final class ConfigurationInternal { + ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported + ", mGeoDetectionSupported=" + mGeoDetectionSupported + ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported + ", mEnhancedMetricsCollectionEnabled=" + mEnhancedMetricsCollectionEnabled + ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting + ", mLocationEnabledSetting=" + mLocationEnabledSetting + ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting Loading @@ -264,6 +278,7 @@ public final class ConfigurationInternal { private boolean mTelephonyDetectionSupported; private boolean mGeoDetectionSupported; private boolean mTelephonyFallbackSupported; private boolean mEnhancedMetricsCollectionEnabled; private boolean mAutoDetectionEnabledSetting; private boolean mLocationEnabledSetting; private boolean mGeoDetectionEnabledSetting; Loading @@ -284,6 +299,7 @@ public final class ConfigurationInternal { this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported; this.mTelephonyFallbackSupported = toCopy.mTelephonyFallbackSupported; this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported; this.mEnhancedMetricsCollectionEnabled = toCopy.mEnhancedMetricsCollectionEnabled; this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting; this.mLocationEnabledSetting = toCopy.mLocationEnabledSetting; this.mGeoDetectionEnabledSetting = toCopy.mGeoDetectionEnabledSetting; Loading Loading @@ -322,6 +338,14 @@ public final class ConfigurationInternal { return this; } /** * Sets the value for enhanced metrics collection. */ public Builder setEnhancedMetricsCollectionEnabled(boolean enabled) { mEnhancedMetricsCollectionEnabled = enabled; return this; } /** * Sets the value of the automatic time zone detection enabled setting for this device. */ Loading services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java +85 −29 Original line number Diff line number Diff line Loading @@ -34,11 +34,13 @@ import java.util.Objects; * A class that provides time zone detector state information for metrics. * * <p> * Regarding time zone ID ordinals: * Regarding the use of time zone ID ordinals in metrics / telemetry: * <p> * We don't want to leak user location information by reporting time zone IDs. Instead, time zone * IDs are consistently identified within a given instance of this class by a numeric ID. This * allows comparison of IDs without revealing what those IDs are. * For general metrics, we don't want to leak user location information by reporting time zone * IDs. Instead, time zone IDs are consistently identified within a given instance of this class by * a numeric ID (ordinal). This allows comparison of IDs without revealing what those IDs are. * See {@link #isEnhancedMetricsCollectionEnabled()} for the setting that enables actual IDs to be * collected. */ public final class MetricsTimeZoneDetectorState { Loading @@ -54,6 +56,7 @@ public final class MetricsTimeZoneDetectorState { @NonNull private final ConfigurationInternal mConfigurationInternal; private final int mDeviceTimeZoneIdOrdinal; @Nullable private final String mDeviceTimeZoneId; @Nullable private final MetricsTimeZoneSuggestion mLatestManualSuggestion; @Nullable private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion; @Nullable private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion; Loading @@ -61,11 +64,13 @@ public final class MetricsTimeZoneDetectorState { private MetricsTimeZoneDetectorState( @NonNull ConfigurationInternal configurationInternal, int deviceTimeZoneIdOrdinal, @Nullable String deviceTimeZoneId, @Nullable MetricsTimeZoneSuggestion latestManualSuggestion, @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion, @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) { mConfigurationInternal = Objects.requireNonNull(configurationInternal); mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal; mDeviceTimeZoneId = deviceTimeZoneId; mLatestManualSuggestion = latestManualSuggestion; mLatestTelephonySuggestion = latestTelephonySuggestion; mLatestGeolocationSuggestion = latestGeolocationSuggestion; Loading @@ -83,18 +88,24 @@ public final class MetricsTimeZoneDetectorState { @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion, @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) { boolean includeZoneIds = configurationInternal.isEnhancedMetricsCollectionEnabled(); String metricDeviceTimeZoneId = includeZoneIds ? deviceTimeZoneId : null; int deviceTimeZoneIdOrdinal = tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId)); MetricsTimeZoneSuggestion latestCanonicalManualSuggestion = createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion); createMetricsTimeZoneSuggestion( tzIdOrdinalGenerator, latestManualSuggestion, includeZoneIds); MetricsTimeZoneSuggestion latestCanonicalTelephonySuggestion = createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion); createMetricsTimeZoneSuggestion( tzIdOrdinalGenerator, latestTelephonySuggestion, includeZoneIds); MetricsTimeZoneSuggestion latestCanonicalGeolocationSuggestion = createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion); createMetricsTimeZoneSuggestion( tzIdOrdinalGenerator, latestGeolocationSuggestion, includeZoneIds); return new MetricsTimeZoneDetectorState( configurationInternal, deviceTimeZoneIdOrdinal, latestCanonicalManualSuggestion, latestCanonicalTelephonySuggestion, latestCanonicalGeolocationSuggestion); configurationInternal, deviceTimeZoneIdOrdinal, metricDeviceTimeZoneId, latestCanonicalManualSuggestion, latestCanonicalTelephonySuggestion, latestCanonicalGeolocationSuggestion); } /** Returns true if the device supports telephony time zone detection. */ Loading @@ -112,6 +123,11 @@ public final class MetricsTimeZoneDetectorState { return mConfigurationInternal.isTelephonyFallbackSupported(); } /** Returns true if enhanced metric collection is enabled. */ public boolean isEnhancedMetricsCollectionEnabled() { return mConfigurationInternal.isEnhancedMetricsCollectionEnabled(); } /** Returns true if user's location can be used generally. */ public boolean getUserLocationEnabledSetting() { return mConfigurationInternal.getLocationEnabledSetting(); Loading Loading @@ -142,13 +158,23 @@ public final class MetricsTimeZoneDetectorState { } /** * Returns the ordinal for the device's currently set time zone ID. * Returns the ordinal for the device's current time zone ID. * See {@link MetricsTimeZoneDetectorState} for information about ordinals. */ public int getDeviceTimeZoneIdOrdinal() { return mDeviceTimeZoneIdOrdinal; } /** * Returns the device's current time zone ID. This will only be populated if {@link * #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link * MetricsTimeZoneDetectorState} for details. */ @Nullable public String getDeviceTimeZoneId() { return mDeviceTimeZoneId; } /** * Returns a canonical form of the last manual suggestion received. */ Loading Loading @@ -183,6 +209,7 @@ public final class MetricsTimeZoneDetectorState { } MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o; return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal && Objects.equals(mDeviceTimeZoneId, that.mDeviceTimeZoneId) && mConfigurationInternal.equals(that.mConfigurationInternal) && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion) && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion) Loading @@ -191,7 +218,7 @@ public final class MetricsTimeZoneDetectorState { @Override public int hashCode() { return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, mDeviceTimeZoneId, mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion); } Loading @@ -200,6 +227,7 @@ public final class MetricsTimeZoneDetectorState { return "MetricsTimeZoneDetectorState{" + "mConfigurationInternal=" + mConfigurationInternal + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal + ", mDeviceTimeZoneId=" + mDeviceTimeZoneId + ", mLatestManualSuggestion=" + mLatestManualSuggestion + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion Loading @@ -209,34 +237,40 @@ public final class MetricsTimeZoneDetectorState { @Nullable private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @NonNull ManualTimeZoneSuggestion manualSuggestion) { @NonNull ManualTimeZoneSuggestion manualSuggestion, boolean includeFullZoneIds) { if (manualSuggestion == null) { return null; } int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId()); return MetricsTimeZoneSuggestion.createCertain( new int[] { zoneIdOrdinal }); String suggestionZoneId = manualSuggestion.getZoneId(); String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null; int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) }; return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals); } @Nullable private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) { @NonNull TelephonyTimeZoneSuggestion telephonySuggestion, boolean includeFullZoneIds) { if (telephonySuggestion == null) { return null; } if (telephonySuggestion.getZoneId() == null) { String suggestionZoneId = telephonySuggestion.getZoneId(); if (suggestionZoneId == null) { return MetricsTimeZoneSuggestion.createUncertain(); } int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId()); return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal }); String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null; int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) }; return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals); } @Nullable private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) { @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion, boolean includeFullZoneIds) { if (geolocationSuggestion == null) { return null; } Loading @@ -245,7 +279,9 @@ public final class MetricsTimeZoneDetectorState { if (zoneIds == null) { return MetricsTimeZoneSuggestion.createUncertain(); } return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds)); String[] metricZoneIds = includeFullZoneIds ? zoneIds.toArray(new String[0]) : null; int[] zoneIdOrdinals = zoneIdOrdinalGenerator.ordinals(zoneIds); return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals); } /** Loading @@ -254,33 +290,49 @@ public final class MetricsTimeZoneDetectorState { * MetricsTimeZoneSuggestion proto definition. */ public static final class MetricsTimeZoneSuggestion { @Nullable private final int[] mZoneIdOrdinals; @Nullable private final String[] mZoneIds; @Nullable private final int[] mZoneIdOrdinals; MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) { private MetricsTimeZoneSuggestion( @Nullable String[] zoneIds, @Nullable int[] zoneIdOrdinals) { mZoneIds = zoneIds; mZoneIdOrdinals = zoneIdOrdinals; } @NonNull static MetricsTimeZoneSuggestion createUncertain() { return new MetricsTimeZoneSuggestion(null); return new MetricsTimeZoneSuggestion(null, null); } @NonNull static MetricsTimeZoneSuggestion createCertain( @NonNull int[] zoneIdOrdinals) { return new MetricsTimeZoneSuggestion(zoneIdOrdinals); @Nullable String[] zoneIds, @NonNull int[] zoneIdOrdinals) { return new MetricsTimeZoneSuggestion(zoneIds, zoneIdOrdinals); } public boolean isCertain() { return mZoneIdOrdinals != null; } /** * Returns ordinals for the time zone IDs contained in the suggestion. * See {@link MetricsTimeZoneDetectorState} for information about ordinals. */ @Nullable public int[] getZoneIdOrdinals() { return mZoneIdOrdinals; } /** * Returns the time zone IDs contained in the suggestion. This will only be populated if * {@link #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link * MetricsTimeZoneDetectorState} for details. */ @Nullable public String[] getZoneIds() { return mZoneIds; } @Override public boolean equals(Object o) { if (this == o) { Loading @@ -290,18 +342,22 @@ public final class MetricsTimeZoneDetectorState { return false; } MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o; return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals); return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals) && Arrays.equals(mZoneIds, that.mZoneIds); } @Override public int hashCode() { return Arrays.hashCode(mZoneIdOrdinals); int result = Arrays.hashCode(mZoneIds); result = 31 * result + Arrays.hashCode(mZoneIdOrdinals); return result; } @Override public String toString() { return "MetricsTimeZoneSuggestion{" + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals) + ", mZoneIds=" + Arrays.toString(mZoneIds) + '}'; } } Loading Loading
core/java/android/app/timezonedetector/TimeZoneDetector.java +8 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,14 @@ public interface TimeZoneDetector { */ String SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK = "enable_telephony_fallback"; /** * A shell command that dumps a {@link * com.android.server.timezonedetector.MetricsTimeZoneDetectorState} object to stdout for * debugging. * @hide */ String SHELL_COMMAND_DUMP_METRICS = "dump_metrics"; /** * A shared utility method to create a {@link ManualTimeZoneSuggestion}. * Loading
core/proto/android/app/time_zone_detector.proto +22 −11 Original line number Diff line number Diff line Loading @@ -32,16 +32,8 @@ message GeolocationTimeZoneSuggestionProto { } /* * An obfuscated and simplified time zone suggestion for metrics use. * * The suggestion's time zone IDs (which relate to location) are obfuscated by * mapping them to an ordinal. When the ordinal is assigned consistently across * several objects (i.e. so the same time zone ID is always mapped to the same * ordinal), this allows comparisons between those objects. For example, we can * answer "did these two suggestions agree?", "does the suggestion match the * device's current time zone?", without leaking knowledge of location. Ordinals * are also significantly more compact than full IANA TZDB IDs, albeit highly * unstable and of limited use. * A generic-form time zone suggestion for metrics use. Required to be a superset of the * MetricsTimeZoneSuggestion proto defined in atoms.proto to ensure binary compatibility. */ message MetricsTimeZoneSuggestion { option (android.msg_privacy).dest = DEST_AUTOMATIC; Loading @@ -55,5 +47,24 @@ message MetricsTimeZoneSuggestion { // The ordinals for time zone(s) in the suggestion. Always empty for // UNCERTAIN, and can be empty for CERTAIN, for example when the device is in // a disputed area / on an ocean. repeated uint32 time_zone_ordinals = 2; // // The suggestion's time zone IDs (which relate to location) are obfuscated by // mapping them to an ordinal. When the ordinal is assigned consistently across // several objects (i.e. so the same time zone ID is always mapped to the same // ordinal), this allows comparisons between those objects. For example, we can // answer "did these two suggestions agree?", "does the suggestion match the // device's current time zone?", without leaking knowledge of location. Ordinals // are also significantly more compact than full IANA TZDB IDs, albeit unstable // and of limited use. repeated int32 time_zone_ordinals = 2; // The actual time zone ID(s) in the suggestion. Similar to time_zone_ordinals // but contains the actual string IDs. // // This information is only captured / reported for some devices based on the // value of a server side flag, i.e. it could be enabled for internal testers. // Therefore the list can be empty even when time_zone_ordinals is populated. // // When enabled, see time_zone_ordinals for the expected number of values. repeated string time_zone_ids = 3; }
services/core/java/com/android/server/timedetector/ServerFlags.java +8 −1 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ public final class ServerFlags { KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE, KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE, KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED, KEY_ENHANCED_METRICS_COLLECTION_ENABLED, }) @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) @Retention(RetentionPolicy.SOURCE) Loading Loading @@ -156,12 +157,18 @@ public final class ServerFlags { "time_detector_origin_priorities_override"; /** * The key to override the time detector lower bound configuration. The values is the number of * The key to override the time detector lower bound configuration. The value is the number of * milliseconds since the beginning of the Unix epoch. */ public static final @DeviceConfigKey String KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE = "time_detector_lower_bound_millis_override"; /** * The key to allow extra metrics / telemetry information to be collected from internal testers. */ public static final @DeviceConfigKey String KEY_ENHANCED_METRICS_COLLECTION_ENABLED = "enhanced_metrics_collection_enabled"; @GuardedBy("mListeners") private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>(); Loading
services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +25 −1 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ public final class ConfigurationInternal { private final boolean mTelephonyDetectionSupported; private final boolean mGeoDetectionSupported; private final boolean mTelephonyFallbackSupported; private final boolean mEnhancedMetricsCollectionEnabled; private final boolean mAutoDetectionEnabledSetting; private final @UserIdInt int mUserId; private final boolean mUserConfigAllowed; Loading @@ -50,6 +51,7 @@ public final class ConfigurationInternal { mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported; mGeoDetectionSupported = builder.mGeoDetectionSupported; mTelephonyFallbackSupported = builder.mTelephonyFallbackSupported; mEnhancedMetricsCollectionEnabled = builder.mEnhancedMetricsCollectionEnabled; mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting; mUserId = builder.mUserId; Loading Loading @@ -81,6 +83,15 @@ public final class ConfigurationInternal { return mTelephonyFallbackSupported; } /** * Returns {@code true} if the device can collect / report extra metrics information for QA * / testers. These metrics might involve logging more expensive or more revealing data that * would not be collected from the set of public users. */ public boolean isEnhancedMetricsCollectionEnabled() { return mEnhancedMetricsCollectionEnabled; } /** Returns the value of the auto time zone detection enabled setting. */ public boolean getAutoDetectionEnabledSetting() { return mAutoDetectionEnabledSetting; Loading Loading @@ -227,6 +238,7 @@ public final class ConfigurationInternal { && mTelephonyDetectionSupported == that.mTelephonyDetectionSupported && mGeoDetectionSupported == that.mGeoDetectionSupported && mTelephonyFallbackSupported == that.mTelephonyFallbackSupported && mEnhancedMetricsCollectionEnabled == that.mEnhancedMetricsCollectionEnabled && mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting && mLocationEnabledSetting == that.mLocationEnabledSetting && mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting; Loading @@ -235,7 +247,8 @@ public final class ConfigurationInternal { @Override public int hashCode() { return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported, mGeoDetectionSupported, mTelephonyFallbackSupported, mAutoDetectionEnabledSetting, mGeoDetectionSupported, mTelephonyFallbackSupported, mEnhancedMetricsCollectionEnabled, mAutoDetectionEnabledSetting, mLocationEnabledSetting, mGeoDetectionEnabledSetting); } Loading @@ -247,6 +260,7 @@ public final class ConfigurationInternal { + ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported + ", mGeoDetectionSupported=" + mGeoDetectionSupported + ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported + ", mEnhancedMetricsCollectionEnabled=" + mEnhancedMetricsCollectionEnabled + ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting + ", mLocationEnabledSetting=" + mLocationEnabledSetting + ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting Loading @@ -264,6 +278,7 @@ public final class ConfigurationInternal { private boolean mTelephonyDetectionSupported; private boolean mGeoDetectionSupported; private boolean mTelephonyFallbackSupported; private boolean mEnhancedMetricsCollectionEnabled; private boolean mAutoDetectionEnabledSetting; private boolean mLocationEnabledSetting; private boolean mGeoDetectionEnabledSetting; Loading @@ -284,6 +299,7 @@ public final class ConfigurationInternal { this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported; this.mTelephonyFallbackSupported = toCopy.mTelephonyFallbackSupported; this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported; this.mEnhancedMetricsCollectionEnabled = toCopy.mEnhancedMetricsCollectionEnabled; this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting; this.mLocationEnabledSetting = toCopy.mLocationEnabledSetting; this.mGeoDetectionEnabledSetting = toCopy.mGeoDetectionEnabledSetting; Loading Loading @@ -322,6 +338,14 @@ public final class ConfigurationInternal { return this; } /** * Sets the value for enhanced metrics collection. */ public Builder setEnhancedMetricsCollectionEnabled(boolean enabled) { mEnhancedMetricsCollectionEnabled = enabled; return this; } /** * Sets the value of the automatic time zone detection enabled setting for this device. */ Loading
services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java +85 −29 Original line number Diff line number Diff line Loading @@ -34,11 +34,13 @@ import java.util.Objects; * A class that provides time zone detector state information for metrics. * * <p> * Regarding time zone ID ordinals: * Regarding the use of time zone ID ordinals in metrics / telemetry: * <p> * We don't want to leak user location information by reporting time zone IDs. Instead, time zone * IDs are consistently identified within a given instance of this class by a numeric ID. This * allows comparison of IDs without revealing what those IDs are. * For general metrics, we don't want to leak user location information by reporting time zone * IDs. Instead, time zone IDs are consistently identified within a given instance of this class by * a numeric ID (ordinal). This allows comparison of IDs without revealing what those IDs are. * See {@link #isEnhancedMetricsCollectionEnabled()} for the setting that enables actual IDs to be * collected. */ public final class MetricsTimeZoneDetectorState { Loading @@ -54,6 +56,7 @@ public final class MetricsTimeZoneDetectorState { @NonNull private final ConfigurationInternal mConfigurationInternal; private final int mDeviceTimeZoneIdOrdinal; @Nullable private final String mDeviceTimeZoneId; @Nullable private final MetricsTimeZoneSuggestion mLatestManualSuggestion; @Nullable private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion; @Nullable private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion; Loading @@ -61,11 +64,13 @@ public final class MetricsTimeZoneDetectorState { private MetricsTimeZoneDetectorState( @NonNull ConfigurationInternal configurationInternal, int deviceTimeZoneIdOrdinal, @Nullable String deviceTimeZoneId, @Nullable MetricsTimeZoneSuggestion latestManualSuggestion, @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion, @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) { mConfigurationInternal = Objects.requireNonNull(configurationInternal); mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal; mDeviceTimeZoneId = deviceTimeZoneId; mLatestManualSuggestion = latestManualSuggestion; mLatestTelephonySuggestion = latestTelephonySuggestion; mLatestGeolocationSuggestion = latestGeolocationSuggestion; Loading @@ -83,18 +88,24 @@ public final class MetricsTimeZoneDetectorState { @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion, @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) { boolean includeZoneIds = configurationInternal.isEnhancedMetricsCollectionEnabled(); String metricDeviceTimeZoneId = includeZoneIds ? deviceTimeZoneId : null; int deviceTimeZoneIdOrdinal = tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId)); MetricsTimeZoneSuggestion latestCanonicalManualSuggestion = createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion); createMetricsTimeZoneSuggestion( tzIdOrdinalGenerator, latestManualSuggestion, includeZoneIds); MetricsTimeZoneSuggestion latestCanonicalTelephonySuggestion = createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion); createMetricsTimeZoneSuggestion( tzIdOrdinalGenerator, latestTelephonySuggestion, includeZoneIds); MetricsTimeZoneSuggestion latestCanonicalGeolocationSuggestion = createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion); createMetricsTimeZoneSuggestion( tzIdOrdinalGenerator, latestGeolocationSuggestion, includeZoneIds); return new MetricsTimeZoneDetectorState( configurationInternal, deviceTimeZoneIdOrdinal, latestCanonicalManualSuggestion, latestCanonicalTelephonySuggestion, latestCanonicalGeolocationSuggestion); configurationInternal, deviceTimeZoneIdOrdinal, metricDeviceTimeZoneId, latestCanonicalManualSuggestion, latestCanonicalTelephonySuggestion, latestCanonicalGeolocationSuggestion); } /** Returns true if the device supports telephony time zone detection. */ Loading @@ -112,6 +123,11 @@ public final class MetricsTimeZoneDetectorState { return mConfigurationInternal.isTelephonyFallbackSupported(); } /** Returns true if enhanced metric collection is enabled. */ public boolean isEnhancedMetricsCollectionEnabled() { return mConfigurationInternal.isEnhancedMetricsCollectionEnabled(); } /** Returns true if user's location can be used generally. */ public boolean getUserLocationEnabledSetting() { return mConfigurationInternal.getLocationEnabledSetting(); Loading Loading @@ -142,13 +158,23 @@ public final class MetricsTimeZoneDetectorState { } /** * Returns the ordinal for the device's currently set time zone ID. * Returns the ordinal for the device's current time zone ID. * See {@link MetricsTimeZoneDetectorState} for information about ordinals. */ public int getDeviceTimeZoneIdOrdinal() { return mDeviceTimeZoneIdOrdinal; } /** * Returns the device's current time zone ID. This will only be populated if {@link * #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link * MetricsTimeZoneDetectorState} for details. */ @Nullable public String getDeviceTimeZoneId() { return mDeviceTimeZoneId; } /** * Returns a canonical form of the last manual suggestion received. */ Loading Loading @@ -183,6 +209,7 @@ public final class MetricsTimeZoneDetectorState { } MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o; return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal && Objects.equals(mDeviceTimeZoneId, that.mDeviceTimeZoneId) && mConfigurationInternal.equals(that.mConfigurationInternal) && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion) && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion) Loading @@ -191,7 +218,7 @@ public final class MetricsTimeZoneDetectorState { @Override public int hashCode() { return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, mDeviceTimeZoneId, mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion); } Loading @@ -200,6 +227,7 @@ public final class MetricsTimeZoneDetectorState { return "MetricsTimeZoneDetectorState{" + "mConfigurationInternal=" + mConfigurationInternal + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal + ", mDeviceTimeZoneId=" + mDeviceTimeZoneId + ", mLatestManualSuggestion=" + mLatestManualSuggestion + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion Loading @@ -209,34 +237,40 @@ public final class MetricsTimeZoneDetectorState { @Nullable private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @NonNull ManualTimeZoneSuggestion manualSuggestion) { @NonNull ManualTimeZoneSuggestion manualSuggestion, boolean includeFullZoneIds) { if (manualSuggestion == null) { return null; } int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId()); return MetricsTimeZoneSuggestion.createCertain( new int[] { zoneIdOrdinal }); String suggestionZoneId = manualSuggestion.getZoneId(); String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null; int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) }; return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals); } @Nullable private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) { @NonNull TelephonyTimeZoneSuggestion telephonySuggestion, boolean includeFullZoneIds) { if (telephonySuggestion == null) { return null; } if (telephonySuggestion.getZoneId() == null) { String suggestionZoneId = telephonySuggestion.getZoneId(); if (suggestionZoneId == null) { return MetricsTimeZoneSuggestion.createUncertain(); } int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId()); return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal }); String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null; int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) }; return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals); } @Nullable private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) { @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion, boolean includeFullZoneIds) { if (geolocationSuggestion == null) { return null; } Loading @@ -245,7 +279,9 @@ public final class MetricsTimeZoneDetectorState { if (zoneIds == null) { return MetricsTimeZoneSuggestion.createUncertain(); } return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds)); String[] metricZoneIds = includeFullZoneIds ? zoneIds.toArray(new String[0]) : null; int[] zoneIdOrdinals = zoneIdOrdinalGenerator.ordinals(zoneIds); return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals); } /** Loading @@ -254,33 +290,49 @@ public final class MetricsTimeZoneDetectorState { * MetricsTimeZoneSuggestion proto definition. */ public static final class MetricsTimeZoneSuggestion { @Nullable private final int[] mZoneIdOrdinals; @Nullable private final String[] mZoneIds; @Nullable private final int[] mZoneIdOrdinals; MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) { private MetricsTimeZoneSuggestion( @Nullable String[] zoneIds, @Nullable int[] zoneIdOrdinals) { mZoneIds = zoneIds; mZoneIdOrdinals = zoneIdOrdinals; } @NonNull static MetricsTimeZoneSuggestion createUncertain() { return new MetricsTimeZoneSuggestion(null); return new MetricsTimeZoneSuggestion(null, null); } @NonNull static MetricsTimeZoneSuggestion createCertain( @NonNull int[] zoneIdOrdinals) { return new MetricsTimeZoneSuggestion(zoneIdOrdinals); @Nullable String[] zoneIds, @NonNull int[] zoneIdOrdinals) { return new MetricsTimeZoneSuggestion(zoneIds, zoneIdOrdinals); } public boolean isCertain() { return mZoneIdOrdinals != null; } /** * Returns ordinals for the time zone IDs contained in the suggestion. * See {@link MetricsTimeZoneDetectorState} for information about ordinals. */ @Nullable public int[] getZoneIdOrdinals() { return mZoneIdOrdinals; } /** * Returns the time zone IDs contained in the suggestion. This will only be populated if * {@link #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link * MetricsTimeZoneDetectorState} for details. */ @Nullable public String[] getZoneIds() { return mZoneIds; } @Override public boolean equals(Object o) { if (this == o) { Loading @@ -290,18 +342,22 @@ public final class MetricsTimeZoneDetectorState { return false; } MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o; return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals); return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals) && Arrays.equals(mZoneIds, that.mZoneIds); } @Override public int hashCode() { return Arrays.hashCode(mZoneIdOrdinals); int result = Arrays.hashCode(mZoneIds); result = 31 * result + Arrays.hashCode(mZoneIdOrdinals); return result; } @Override public String toString() { return "MetricsTimeZoneSuggestion{" + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals) + ", mZoneIds=" + Arrays.toString(mZoneIds) + '}'; } } Loading