Add "enhanced metrics collection" support

Add "enhanced metrics collection" support to the time zone detector service.

This commit doesn't make any actual changes to the metrics code, that
will be done separately. This just adds the infrastructure to support
the server flag to turn the behavior on.

Details:

"Enhanced metric collection" is for deployment to QA and other internal
users only. This initially means the actual time zone IDs will
optionally be recorded in the metrics collected by the device in
addition to the original "time zone ID ordinal" approach used for
general / public users.

Manual testing:

adb shell cmd time_zone_detector dump_metrics
adb shell cmd device_config put system_time enhanced_metrics_collection_enabled true
adb shell cmd time_zone_detector dump_metrics

Bug: 200279201
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/
Change-Id: Ibb480b748fb951f9781b3d6ccc9a14b682ff4a1a
This commit is contained in:
Neil Fuller
2021-11-25 10:30:29 +00:00
parent 0d3682ba0b
commit bae4665077
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)