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
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index ceab02f..aac23d8 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -103,6 +103,14 @@
     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}.
      *
      * @hide
diff --git a/core/proto/android/app/time_zone_detector.proto b/core/proto/android/app/time_zone_detector.proto
index b33ca1d..b52aa82 100644
--- a/core/proto/android/app/time_zone_detector.proto
+++ b/core/proto/android/app/time_zone_detector.proto
@@ -32,16 +32,8 @@
 }
 
 /*
- * 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 @@
   // 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;
 }
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index d24a3df..cf0e350 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -66,6 +66,7 @@
             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 @@
             "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<>();
 
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 65f077e..2291777 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -40,6 +40,7 @@
     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 @@
         mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
         mGeoDetectionSupported = builder.mGeoDetectionSupported;
         mTelephonyFallbackSupported = builder.mTelephonyFallbackSupported;
+        mEnhancedMetricsCollectionEnabled = builder.mEnhancedMetricsCollectionEnabled;
         mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting;
 
         mUserId = builder.mUserId;
@@ -81,6 +83,15 @@
         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 @@
                 && 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 @@
     @Override
     public int hashCode() {
         return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
-                mGeoDetectionSupported, mTelephonyFallbackSupported, mAutoDetectionEnabledSetting,
+                mGeoDetectionSupported, mTelephonyFallbackSupported,
+                mEnhancedMetricsCollectionEnabled, mAutoDetectionEnabledSetting,
                 mLocationEnabledSetting, mGeoDetectionEnabledSetting);
     }
 
@@ -247,6 +260,7 @@
                 + ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
                 + ", mGeoDetectionSupported=" + mGeoDetectionSupported
                 + ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported
+                + ", mEnhancedMetricsCollectionEnabled=" + mEnhancedMetricsCollectionEnabled
                 + ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting
                 + ", mLocationEnabledSetting=" + mLocationEnabledSetting
                 + ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting
@@ -264,6 +278,7 @@
         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 @@
             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;
@@ -323,6 +339,14 @@
         }
 
         /**
+         * 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.
          */
         public Builder setAutoDetectionEnabledSetting(boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
index f156f8c..ecac267 100644
--- a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -34,11 +34,13 @@
  * 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 @@
 
     @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 @@
     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 @@
             @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 @@
         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,7 +158,7 @@
     }
 
     /**
-     * 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() {
@@ -150,6 +166,16 @@
     }
 
     /**
+     * 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.
      */
     @Nullable
@@ -183,6 +209,7 @@
         }
         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 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal,
+        return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, mDeviceTimeZoneId,
                 mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion);
     }
 
@@ -200,6 +227,7 @@
         return "MetricsTimeZoneDetectorState{"
                 + "mConfigurationInternal=" + mConfigurationInternal
                 + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
+                + ", mDeviceTimeZoneId=" + mDeviceTimeZoneId
                 + ", mLatestManualSuggestion=" + mLatestManualSuggestion
                 + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
                 + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
@@ -209,34 +237,40 @@
     @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 @@
         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 @@
      * 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 @@
                 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)
                     + '}';
         }
     }
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
index 02ea433..4612f65 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
@@ -62,6 +62,7 @@
     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 @@
                         isTelephonyTimeZoneDetectionFeatureSupported())
                 .setGeoDetectionFeatureSupported(isGeoTimeZoneDetectionFeatureSupported())
                 .setTelephonyFallbackSupported(isTelephonyFallbackSupported())
+                .setEnhancedMetricsCollectionEnabled(isEnhancedMetricsCollectionEnabled())
                 .setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting())
                 .setUserConfigAllowed(isUserConfigAllowed(userId))
                 .setLocationEnabledSetting(getLocationEnabledSetting(userId))
@@ -400,6 +402,17 @@
                 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() {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 14784cf..f75608e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -364,6 +364,13 @@
         }
     }
 
+    @NonNull
+    MetricsTimeZoneDetectorState generateMetricsState() {
+        enforceManageTimeZoneDetectorPermission();
+
+        return mTimeZoneDetectorStrategy.generateMetricsState();
+    }
+
     @Override
     protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
             @Nullable String[] args) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index 2b912ad..8535b3d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -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_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 @@
                 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 @@
             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 @@
         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 @@
         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");
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 6ee6020c..a963785 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -49,6 +49,7 @@
                 .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setTelephonyFallbackSupported(false)
+                .setEnhancedMetricsCollectionEnabled(false)
                 .setAutoDetectionEnabledSetting(true)
                 .setLocationEnabledSetting(true)
                 .setGeoDetectionEnabledSetting(true)
@@ -112,6 +113,7 @@
                 .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setTelephonyFallbackSupported(false)
+                .setEnhancedMetricsCollectionEnabled(false)
                 .setAutoDetectionEnabledSetting(true)
                 .setLocationEnabledSetting(true)
                 .setGeoDetectionEnabledSetting(true)
@@ -177,6 +179,7 @@
                 .setTelephonyDetectionFeatureSupported(false)
                 .setGeoDetectionFeatureSupported(false)
                 .setTelephonyFallbackSupported(false)
+                .setEnhancedMetricsCollectionEnabled(false)
                 .setAutoDetectionEnabledSetting(true)
                 .setLocationEnabledSetting(true)
                 .setGeoDetectionEnabledSetting(true)
@@ -240,6 +243,7 @@
                 .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(false)
                 .setTelephonyFallbackSupported(false)
+                .setEnhancedMetricsCollectionEnabled(false)
                 .setAutoDetectionEnabledSetting(true)
                 .setLocationEnabledSetting(true)
                 .setGeoDetectionEnabledSetting(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
new file mode 100644
index 0000000..9029ac5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
@@ -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();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 193b2e3..e0e5ba0 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -379,6 +379,7 @@
                 .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setTelephonyFallbackSupported(false)
+                .setEnhancedMetricsCollectionEnabled(false)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionEnabledSetting(autoDetectionEnabled)
                 .setLocationEnabledSetting(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index ef1b4f5..27f7814 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -87,10 +87,11 @@
 
     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 @@
 
     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 @@
 
     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 @@
 
     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 @@
                     .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(true)
                     .setTelephonyFallbackSupported(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabledSetting(true)
                     .setLocationEnabledSetting(true)
@@ -145,6 +150,7 @@
                     .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(true)
                     .setTelephonyFallbackSupported(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabledSetting(true)
                     .setLocationEnabledSetting(true)
@@ -955,8 +961,20 @@
     }
 
     @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 @@
                         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);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index a2df313..9d01310 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -47,6 +47,7 @@
                 .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setTelephonyFallbackSupported(false)
+                .setEnhancedMetricsCollectionEnabled(false)
                 .setAutoDetectionEnabledSetting(true)
                 .setLocationEnabledSetting(true)
                 .setGeoDetectionEnabledSetting(geoDetectionEnabledSetting)