Merge "Add "enhanced metrics collection" support"

This commit is contained in:
Neil Fuller
2021-12-03 18:05:36 +00:00
committed by Android (Google) Code Review
13 changed files with 389 additions and 55 deletions

View File

@@ -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}.
*

View File

@@ -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;
@@ -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;
}

View File

@@ -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)
@@ -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<>();

View File

@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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);
}
@@ -247,6 +260,7 @@ public final class ConfigurationInternal {
+ ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
+ ", mGeoDetectionSupported=" + mGeoDetectionSupported
+ ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported
+ ", mEnhancedMetricsCollectionEnabled=" + mEnhancedMetricsCollectionEnabled
+ ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting
+ ", mLocationEnabledSetting=" + mLocationEnabledSetting
+ ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting
@@ -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;
@@ -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;
@@ -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.
*/

View File

@@ -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 {
@@ -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;
@@ -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;
@@ -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. */
@@ -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();
@@ -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.
*/
@@ -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)
@@ -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);
}
@@ -200,6 +227,7 @@ public final class MetricsTimeZoneDetectorState {
return "MetricsTimeZoneDetectorState{"
+ "mConfigurationInternal=" + mConfigurationInternal
+ ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
+ ", mDeviceTimeZoneId=" + mDeviceTimeZoneId
+ ", mLatestManualSuggestion=" + mLatestManualSuggestion
+ ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
+ ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
@@ -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;
}
@@ -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);
}
/**
@@ -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) {
@@ -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)
+ '}';
}
}

View File

@@ -62,6 +62,7 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
private static final Set<String> CONFIGURATION_INTERNAL_SERVER_FLAGS_KEYS_TO_WATCH =
Collections.unmodifiableSet(new ArraySet<>(new String[] {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
@@ -296,6 +297,7 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
isTelephonyTimeZoneDetectionFeatureSupported())
.setGeoDetectionFeatureSupported(isGeoTimeZoneDetectionFeatureSupported())
.setTelephonyFallbackSupported(isTelephonyFallbackSupported())
.setEnhancedMetricsCollectionEnabled(isEnhancedMetricsCollectionEnabled())
.setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting())
.setUserConfigAllowed(isUserConfigAllowed(userId))
.setLocationEnabledSetting(getLocationEnabledSetting(userId))
@@ -400,6 +402,17 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
defaultEnabled);
}
/**
* Returns {@code true} if extra metrics / telemetry information can be collected. Used for
* internal testers.
*/
private boolean isEnhancedMetricsCollectionEnabled() {
final boolean defaultEnabled = false;
return mServerFlags.getBoolean(
ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED,
defaultEnabled);
}
@Override
@NonNull
public synchronized String getPrimaryLocationTimeZoneProviderPackageName() {

View File

@@ -364,6 +364,13 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
}
}
@NonNull
MetricsTimeZoneDetectorState generateMetricsState() {
enforceManageTimeZoneDetectorPermission();
return mTimeZoneDetectorStrategy.generateMetricsState();
}
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {

View File

@@ -15,6 +15,7 @@
*/
package com.android.server.timezonedetector;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_DUMP_METRICS;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED;
@@ -28,6 +29,7 @@ import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGES
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
import static com.android.server.timedetector.ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE;
@@ -80,6 +82,8 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
return runSuggestTelephonyTimeZone();
case SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK:
return runEnableTelephonyFallback();
case SHELL_COMMAND_DUMP_METRICS:
return runDumpMetrics();
default: {
return handleDefaultCommands(cmd);
}
@@ -168,14 +172,22 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.println("Suggestion " + suggestion + " injected.");
return 0;
} catch (RuntimeException e) {
pw.println(e.toString());
pw.println(e);
return 1;
}
}
private int runEnableTelephonyFallback() {
mInterface.enableTelephonyFallback();
return 1;
return 0;
}
private int runDumpMetrics() {
final PrintWriter pw = getOutPrintWriter();
MetricsTimeZoneDetectorState metricsState = mInterface.generateMetricsState();
pw.println("MetricsTimeZoneDetectorState:");
pw.println(metricsState.toString());
return 0;
}
@Override
@@ -208,10 +220,10 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.println();
pw.printf(" %s <geolocation suggestion opts>\n",
SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE);
pw.printf(" %s <manual suggestion opts>\n",
SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE);
pw.printf(" %s <telephony suggestion opts>\n",
SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE);
pw.printf(" %s <manual suggestion opts>\n", SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE);
pw.printf(" %s <telephony suggestion opts>\n", SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE);
pw.printf(" %s\n", SHELL_COMMAND_DUMP_METRICS);
pw.printf(" Dumps the service metrics to stdout for inspection.\n");
pw.println();
GeolocationTimeZoneSuggestion.printCommandLineOpts(pw);
pw.println();
@@ -235,6 +247,8 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.printf(" %s\n", KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED);
pw.printf(" Used to enable / disable support for telephony detection fallback. Also see"
+ " the %s command.\n", SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK);
pw.printf(" %s\n", KEY_ENHANCED_METRICS_COLLECTION_ENABLED);
pw.printf(" Used to increase the detail of metrics collected / reported.\n");
pw.println();
pw.printf("[*] To be enabled, the user must still have location = on / auto time zone"
+ " detection = on.\n");

View File

@@ -49,6 +49,7 @@ public class ConfigurationInternalTest {
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -112,6 +113,7 @@ public class ConfigurationInternalTest {
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -177,6 +179,7 @@ public class ConfigurationInternalTest {
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -240,6 +243,7 @@ public class ConfigurationInternalTest {
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)

View File

@@ -0,0 +1,170 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.timezonedetector;
import static com.android.server.timezonedetector.MetricsTimeZoneDetectorState.DETECTION_MODE_GEO;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.UserIdInt;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import com.android.server.timezonedetector.MetricsTimeZoneDetectorState.MetricsTimeZoneSuggestion;
import org.junit.Test;
import java.util.Arrays;
import java.util.function.Function;
/** Tests for {@link MetricsTimeZoneDetectorState}. */
public class MetricsTimeZoneDetectorStateTest {
private static final @UserIdInt int ARBITRARY_USER_ID = 1;
private static final @ElapsedRealtimeLong long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
private static final String DEVICE_TIME_ZONE_ID = "DeviceTimeZoneId";
private static final ManualTimeZoneSuggestion MANUAL_TIME_ZONE_SUGGESTION =
new ManualTimeZoneSuggestion("ManualTimeZoneId");
private static final TelephonyTimeZoneSuggestion TELEPHONY_TIME_ZONE_SUGGESTION =
new TelephonyTimeZoneSuggestion.Builder(0)
.setZoneId("TelephonyZoneId")
.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY)
.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
.build();
private static final GeolocationTimeZoneSuggestion GEOLOCATION_TIME_ZONE_SUGGESTION =
GeolocationTimeZoneSuggestion.createCertainSuggestion(
ARBITRARY_ELAPSED_REALTIME_MILLIS,
Arrays.asList("GeoTimeZoneId1", "GeoTimeZoneId2"));
private final OrdinalGenerator<String> mOrdinalGenerator =
new OrdinalGenerator<>(Function.identity());
@Test
public void enhancedMetricsCollectionEnabled() {
final boolean enhancedMetricsCollectionEnabled = true;
ConfigurationInternal configurationInternal =
createConfigurationInternal(enhancedMetricsCollectionEnabled);
// Create the object.
MetricsTimeZoneDetectorState metricsTimeZoneDetectorState =
MetricsTimeZoneDetectorState.create(mOrdinalGenerator, configurationInternal,
DEVICE_TIME_ZONE_ID, MANUAL_TIME_ZONE_SUGGESTION,
TELEPHONY_TIME_ZONE_SUGGESTION, GEOLOCATION_TIME_ZONE_SUGGESTION);
// Assert the content.
assertCommonConfiguration(configurationInternal, metricsTimeZoneDetectorState);
assertEquals(DEVICE_TIME_ZONE_ID, metricsTimeZoneDetectorState.getDeviceTimeZoneId());
MetricsTimeZoneSuggestion expectedManualSuggestion =
MetricsTimeZoneSuggestion.createCertain(
new String[] { MANUAL_TIME_ZONE_SUGGESTION.getZoneId() },
new int[] { 1 });
assertEquals(expectedManualSuggestion,
metricsTimeZoneDetectorState.getLatestManualSuggestion());
MetricsTimeZoneSuggestion expectedTelephonySuggestion =
MetricsTimeZoneSuggestion.createCertain(
new String[] { TELEPHONY_TIME_ZONE_SUGGESTION.getZoneId() },
new int[] { 2 });
assertEquals(expectedTelephonySuggestion,
metricsTimeZoneDetectorState.getLatestTelephonySuggestion());
MetricsTimeZoneSuggestion expectedGeoSuggestion =
MetricsTimeZoneSuggestion.createCertain(
GEOLOCATION_TIME_ZONE_SUGGESTION.getZoneIds().toArray(new String[0]),
new int[] { 3, 4 });
assertEquals(expectedGeoSuggestion,
metricsTimeZoneDetectorState.getLatestGeolocationSuggestion());
}
@Test
public void enhancedMetricsCollectionDisabled() {
final boolean enhancedMetricsCollectionEnabled = false;
ConfigurationInternal configurationInternal =
createConfigurationInternal(enhancedMetricsCollectionEnabled);
// Create the object.
MetricsTimeZoneDetectorState metricsTimeZoneDetectorState =
MetricsTimeZoneDetectorState.create(mOrdinalGenerator, configurationInternal,
DEVICE_TIME_ZONE_ID, MANUAL_TIME_ZONE_SUGGESTION,
TELEPHONY_TIME_ZONE_SUGGESTION, GEOLOCATION_TIME_ZONE_SUGGESTION);
// Assert the content.
assertCommonConfiguration(configurationInternal, metricsTimeZoneDetectorState);
// When enhancedMetricsCollectionEnabled == false, no time zone IDs should be included.
assertNull(metricsTimeZoneDetectorState.getDeviceTimeZoneId());
final String[] omittedZoneIds = null;
MetricsTimeZoneSuggestion expectedManualSuggestion =
MetricsTimeZoneSuggestion.createCertain(
omittedZoneIds,
new int[] { 1 });
assertEquals(expectedManualSuggestion,
metricsTimeZoneDetectorState.getLatestManualSuggestion());
MetricsTimeZoneSuggestion expectedTelephonySuggestion =
MetricsTimeZoneSuggestion.createCertain(
omittedZoneIds,
new int[] { 2 });
assertEquals(expectedTelephonySuggestion,
metricsTimeZoneDetectorState.getLatestTelephonySuggestion());
MetricsTimeZoneSuggestion expectedGeoSuggestion =
MetricsTimeZoneSuggestion.createCertain(
omittedZoneIds,
new int[] { 3, 4 });
assertEquals(expectedGeoSuggestion,
metricsTimeZoneDetectorState.getLatestGeolocationSuggestion());
}
private static void assertCommonConfiguration(ConfigurationInternal configurationInternal,
MetricsTimeZoneDetectorState metricsTimeZoneDetectorState) {
assertEquals(configurationInternal.isTelephonyDetectionSupported(),
metricsTimeZoneDetectorState.isTelephonyDetectionSupported());
assertEquals(configurationInternal.isGeoDetectionSupported(),
metricsTimeZoneDetectorState.isGeoDetectionSupported());
assertEquals(configurationInternal.getAutoDetectionEnabledSetting(),
metricsTimeZoneDetectorState.getAutoDetectionEnabledSetting());
assertEquals(configurationInternal.getLocationEnabledSetting(),
metricsTimeZoneDetectorState.getUserLocationEnabledSetting());
assertEquals(configurationInternal.getGeoDetectionEnabledSetting(),
metricsTimeZoneDetectorState.getGeoDetectionEnabledSetting());
assertEquals(configurationInternal.isEnhancedMetricsCollectionEnabled(),
metricsTimeZoneDetectorState.isEnhancedMetricsCollectionEnabled());
assertEquals(0, metricsTimeZoneDetectorState.getDeviceTimeZoneIdOrdinal());
assertEquals(DETECTION_MODE_GEO, metricsTimeZoneDetectorState.getDetectionMode());
}
private static ConfigurationInternal createConfigurationInternal(
boolean enhancedMetricsCollectionEnabled) {
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setEnhancedMetricsCollectionEnabled(enhancedMetricsCollectionEnabled)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
.build();
}
}

View File

@@ -379,6 +379,7 @@ public class TimeZoneDetectorServiceTest {
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(autoDetectionEnabled)
.setLocationEnabledSetting(geoDetectionEnabled)

View File

@@ -87,10 +87,11 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(false)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -98,10 +99,11 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -109,10 +111,11 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_AUTO_DETECT_NOT_SUPPORTED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -120,10 +123,11 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_AUTO_DISABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -134,6 +138,7 @@ public class TimeZoneDetectorStrategyImplTest {
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
@@ -145,6 +150,7 @@ public class TimeZoneDetectorStrategyImplTest {
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
@@ -955,8 +961,20 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
public void testGenerateMetricsState() {
ConfigurationInternal expectedInternalConfig = CONFIG_AUTO_DISABLED_GEO_DISABLED;
public void testGenerateMetricsState_enhancedMetricsCollection() {
testGenerateMetricsState(true);
}
@Test
public void testGenerateMetricsState_notEnhancedMetricsCollection() {
testGenerateMetricsState(false);
}
private void testGenerateMetricsState(boolean enhancedMetricsCollection) {
ConfigurationInternal expectedInternalConfig =
new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED_GEO_DISABLED)
.setEnhancedMetricsCollectionEnabled(enhancedMetricsCollection)
.build();
String expectedDeviceTimeZoneId = "InitialZoneId";
Script script = new Script()
@@ -1028,7 +1046,7 @@ public class TimeZoneDetectorStrategyImplTest {
tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,
expectedManualSuggestion, expectedTelephonySuggestion,
expectedGeolocationTimeZoneSuggestion);
// Rely on MetricsTimeZoneDetectorState.equals() for time zone ID ordinal comparisons.
// Rely on MetricsTimeZoneDetectorState.equals() for time zone ID / ID ordinal comparisons.
assertEquals(expectedState, actualState);
}

View File

@@ -47,6 +47,7 @@ final class TestSupport {
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
.setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(geoDetectionEnabledSetting)