Merge "Pass interaction type to FalsingManager.isFalse"
diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp
index cbced0a..f2f8854 100644
--- a/cmds/idmap2/tests/ResultTests.cpp
+++ b/cmds/idmap2/tests/ResultTests.cpp
@@ -260,7 +260,8 @@
 
 struct NoCopyContainer {
   uint32_t value;  // NOLINT(misc-non-private-member-variables-in-classes)
-  DISALLOW_COPY_AND_ASSIGN(NoCopyContainer);
+  NoCopyContainer(const NoCopyContainer&) = delete;
+  NoCopyContainer& operator=(const NoCopyContainer&) = delete;
 };
 
 Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) {
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index 7f43640..fa135b1 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -83,6 +83,8 @@
     private final AppPredictionSessionId mSessionId;
     private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
 
+    private final IBinder mToken = new Binder();
+
     /**
      * Creates a new Prediction client.
      * <p>
@@ -98,7 +100,7 @@
         mSessionId = new AppPredictionSessionId(
                 context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
         try {
-            mPredictionManager.createPredictionSession(predictionContext, mSessionId);
+            mPredictionManager.createPredictionSession(predictionContext, mSessionId, mToken);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to create predictor", e);
             e.rethrowAsRuntimeException();
diff --git a/core/java/android/app/prediction/IPredictionManager.aidl b/core/java/android/app/prediction/IPredictionManager.aidl
index 587e3fd..863fc6f9 100644
--- a/core/java/android/app/prediction/IPredictionManager.aidl
+++ b/core/java/android/app/prediction/IPredictionManager.aidl
@@ -29,7 +29,7 @@
 interface IPredictionManager {
 
     void createPredictionSession(in AppPredictionContext context,
-            in AppPredictionSessionId sessionId);
+            in AppPredictionSessionId sessionId, in IBinder token);
 
     void notifyAppTargetEvent(in AppPredictionSessionId sessionId, in AppTargetEvent event);
 
diff --git a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl b/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl
index af77fe0..6d0fe72 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl
@@ -20,5 +20,5 @@
 
 /** {@hide} */
 oneway interface ITimeZoneConfigurationListener {
-    void onChange(in TimeZoneConfiguration configuration);
+    void onChange();
 }
\ No newline at end of file
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index 6e93af6..4f7e1f6 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -32,17 +32,15 @@
  * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
  * for more complete documentation.
  *
- *
  * {@hide}
  */
 interface ITimeZoneDetectorService {
   TimeZoneCapabilities getCapabilities();
-
-  TimeZoneConfiguration getConfiguration();
-  boolean updateConfiguration(in TimeZoneConfiguration configuration);
   void addConfigurationListener(ITimeZoneConfigurationListener listener);
   void removeConfigurationListener(ITimeZoneConfigurationListener listener);
 
+  boolean updateConfiguration(in TimeZoneConfiguration configuration);
+
   boolean suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
   void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion);
 }
diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
index cc0af3f..09fffe9 100644
--- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
+++ b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
@@ -16,9 +16,12 @@
 
 package android.app.timezonedetector;
 
+import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED;
+import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.UserIdInt;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -38,9 +41,9 @@
  *
  * <p>Actions have associated methods, see the documentation for each action for details.
  *
- * <p>For configuration capabilities, the associated current configuration value can be retrieved
- * using {@link TimeZoneDetector#getConfiguration()} and may be changed using
- * {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)}.
+ * <p>For configuration settings capabilities, the associated settings value can be found via
+ * {@link #getConfiguration()} and may be changed using {@link
+ * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} (if the user's capabilities allow).
  *
  * <p>Note: Capabilities are independent of app permissions required to call the associated APIs.
  *
@@ -60,7 +63,8 @@
     public static final int CAPABILITY_NOT_SUPPORTED = 10;
 
     /**
-     * Indicates that a capability is supported on this device, but not allowed for the user.
+     * Indicates that a capability is supported on this device, but not allowed for the user, e.g.
+     * if the capability relates to the ability to modify settings the user is not able to.
      * This could be because of the user's type (e.g. maybe it applies to the primary user only) or
      * device policy. Depending on the capability, this could mean the associated UI
      * should be hidden, or displayed but disabled.
@@ -68,9 +72,11 @@
     public static final int CAPABILITY_NOT_ALLOWED = 20;
 
     /**
-     * Indicates that a capability is possessed but not applicable, e.g. if it is configuration,
-     * the current configuration or device state renders it irrelevant. The associated UI may be
-     * hidden, disabled, or left visible (but ineffective) depending on requirements.
+     * Indicates that a capability is possessed but not currently applicable, e.g. if the
+     * capability relates to the ability to modify settings, the user has the ability to modify
+     * it, but it is currently rendered irrelevant by other settings or other device state (flags,
+     * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but
+     * ineffective) depending on requirements.
      */
     public static final int CAPABILITY_NOT_APPLICABLE = 30;
 
@@ -89,13 +95,13 @@
             };
 
 
-    private final @UserIdInt int mUserId;
+    @NonNull private final TimeZoneConfiguration mConfiguration;
     private final @CapabilityState int mConfigureAutoDetectionEnabled;
     private final @CapabilityState int mConfigureGeoDetectionEnabled;
     private final @CapabilityState int mSuggestManualTimeZone;
 
     private TimeZoneCapabilities(@NonNull Builder builder) {
-        this.mUserId = builder.mUserId;
+        this.mConfiguration = Objects.requireNonNull(builder.mConfiguration);
         this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled;
         this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled;
         this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone;
@@ -103,7 +109,8 @@
 
     @NonNull
     private static TimeZoneCapabilities createFromParcel(Parcel in) {
-        return new TimeZoneCapabilities.Builder(in.readInt())
+        return new TimeZoneCapabilities.Builder()
+                .setConfiguration(in.readParcelable(null))
                 .setConfigureAutoDetectionEnabled(in.readInt())
                 .setConfigureGeoDetectionEnabled(in.readInt())
                 .setSuggestManualTimeZone(in.readInt())
@@ -112,21 +119,24 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mUserId);
+        dest.writeParcelable(mConfiguration, flags);
         dest.writeInt(mConfigureAutoDetectionEnabled);
         dest.writeInt(mConfigureGeoDetectionEnabled);
         dest.writeInt(mSuggestManualTimeZone);
     }
 
-    /** Returns the user ID the capabilities are for. */
-    public @UserIdInt int getUserId() {
-        return mUserId;
+    /**
+     * Returns the user's time zone behavior configuration.
+     */
+    public @NonNull TimeZoneConfiguration getConfiguration() {
+        return mConfiguration;
     }
 
     /**
-     * Returns the user's capability state for controlling whether automatic time zone detection is
-     * enabled via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
-     * TimeZoneConfiguration#isAutoDetectionEnabled()}.
+     * Returns the capability state associated with the user's ability to modify the automatic time
+     * zone detection setting. The setting can be updated via {@link
+     * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link
+     * #getConfiguration()}.
      */
     @CapabilityState
     public int getConfigureAutoDetectionEnabled() {
@@ -134,9 +144,10 @@
     }
 
     /**
-     * Returns the user's capability state for controlling whether geolocation can be used to detect
-     * time zone via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
-     * TimeZoneConfiguration#isGeoDetectionEnabled()}.
+     * Returns the capability state associated with the user's ability to modify the geolocation
+     * detection setting. The setting can be updated via {@link
+     * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link
+     * #getConfiguration()}.
      */
     @CapabilityState
     public int getConfigureGeoDetectionEnabled() {
@@ -144,8 +155,8 @@
     }
 
     /**
-     * Returns the user's capability state for manually setting the time zone on a device via
-     * {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
+     * Returns the capability state associated with the user's ability to manually set the time zone
+     * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
      *
      * <p>The suggestion will be ignored in all cases unless the value is {@link
      * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
@@ -155,6 +166,38 @@
         return mSuggestManualTimeZone;
     }
 
+    /**
+     * Constructs a new {@link TimeZoneConfiguration} from an {@code oldConfiguration} and a set of
+     * {@code requestedChanges}, if the current capabilities allow. The new configuration is
+     * returned and the capabilities are left unchanged. If the capabilities do not permit one or
+     * more of the changes then {@code null} is returned.
+     */
+    @Nullable
+    public TimeZoneConfiguration applyUpdate(TimeZoneConfiguration requestedChanges) {
+        if (requestedChanges.getUserId() != mConfiguration.getUserId()) {
+            throw new IllegalArgumentException("User does not match:"
+                    + " this=" + mConfiguration + ", other=" + requestedChanges);
+        }
+
+        TimeZoneConfiguration.Builder newConfigBuilder =
+                new TimeZoneConfiguration.Builder(mConfiguration);
+        if (requestedChanges.hasSetting(SETTING_AUTO_DETECTION_ENABLED)) {
+            if (getConfigureAutoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) {
+                return null;
+            }
+            newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled());
+        }
+
+        if (requestedChanges.hasSetting(SETTING_GEO_DETECTION_ENABLED)) {
+            if (getConfigureGeoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) {
+                return null;
+            }
+            newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled());
+        }
+
+        return newConfigBuilder.build();
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -169,7 +212,7 @@
             return false;
         }
         TimeZoneCapabilities that = (TimeZoneCapabilities) o;
-        return mUserId == that.mUserId
+        return Objects.equals(mConfiguration, that.mConfiguration)
                 && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled
                 && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled
                 && mSuggestManualTimeZone == that.mSuggestManualTimeZone;
@@ -177,7 +220,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUserId,
+        return Objects.hash(mConfiguration,
                 mConfigureAutoDetectionEnabled,
                 mConfigureGeoDetectionEnabled,
                 mSuggestManualTimeZone);
@@ -186,7 +229,7 @@
     @Override
     public String toString() {
         return "TimeZoneDetectorCapabilities{"
-                + "mUserId=" + mUserId
+                + "mConfiguration=" + mConfiguration
                 + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled
                 + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled
                 + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone
@@ -196,16 +239,18 @@
     /** @hide */
     public static class Builder {
 
-        private final @UserIdInt int mUserId;
+        private TimeZoneConfiguration mConfiguration;
         private @CapabilityState int mConfigureAutoDetectionEnabled;
         private @CapabilityState int mConfigureGeoDetectionEnabled;
         private @CapabilityState int mSuggestManualTimeZone;
 
-        /**
-         * Creates a new Builder with no properties set.
-         */
-        public Builder(@UserIdInt int userId) {
-            mUserId = userId;
+        /** Sets the user-visible configuration settings. */
+        public Builder setConfiguration(@NonNull TimeZoneConfiguration configuration) {
+            if (!configuration.isComplete()) {
+                throw new IllegalArgumentException(configuration + " is not complete");
+            }
+            this.mConfiguration = configuration;
+            return this;
         }
 
         /** Sets the state for the automatic time zone detection enabled config. */
diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
index 6f84ee2..95db0a2 100644
--- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
+++ b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.StringDef;
+import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,21 +28,20 @@
 import java.util.Objects;
 
 /**
- * Configuration that controls the behavior of the time zone detector associated with a specific
- * user.
+ * User visible settings that control the behavior of the time zone detector / manual time zone
+ * entry.
  *
- * <p>Configuration consists of a set of known properties. When reading configuration via
- * {@link TimeZoneDetector#getConfiguration()} values for all known properties will be provided. In
- * some cases, such as when the configuration relies on optional hardware, the values may be
- * meaningless / defaulted to safe values.
+ * <p>When reading the configuration, values for all settings will be provided. In some cases, such
+ * as when the device behavior relies on optional hardware / OEM configuration, or the value of
+ * several settings, the device behavior may not be directly affected by the setting value.
  *
- * <p>Configuration properties can be left absent when updating configuration via {@link
- * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those values will not be
- * changed. Not all configuration properties can be modified by all users. See {@link
- * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities}.
+ * <p>Settings can be left absent when updating configuration via {@link
+ * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those settings will not be
+ * changed. Not all configuration settings can be modified by all users: see {@link
+ * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities} for details.
  *
- * <p>See {@link #isComplete()} to tell if all known properties are present, and {@link
- * #hasProperty(String)} with {@code PROPERTY_} constants for testing individual properties.
+ * <p>See {@link #hasSetting(String)} with {@code PROPERTY_} constants for testing for the presence
+ * of individual settings.
  *
  * @hide
  */
@@ -59,80 +59,82 @@
             };
 
     /** All configuration properties */
-    @StringDef(PROPERTY_AUTO_DETECTION_ENABLED)
+    @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED })
     @Retention(RetentionPolicy.SOURCE)
-    @interface Property {}
+    @interface Setting {}
 
     /** See {@link TimeZoneConfiguration#isAutoDetectionEnabled()} for details. */
-    @Property
-    public static final String PROPERTY_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
+    @Setting
+    public static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
 
     /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */
-    @Property
-    public static final String PROPERTY_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
+    @Setting
+    public static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
 
-    private final Bundle mBundle;
+    private final @UserIdInt int mUserId;
+    @NonNull private final Bundle mBundle;
 
     private TimeZoneConfiguration(Builder builder) {
-        this.mBundle = builder.mBundle;
+        this.mUserId = builder.mUserId;
+        this.mBundle = Objects.requireNonNull(builder.mBundle);
     }
 
     private static TimeZoneConfiguration createFromParcel(Parcel in) {
-        return new TimeZoneConfiguration.Builder()
+        return new TimeZoneConfiguration.Builder(in.readInt())
                 .setPropertyBundleInternal(in.readBundle())
                 .build();
     }
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mUserId);
         dest.writeBundle(mBundle);
     }
 
-    /** Returns {@code true} if all known properties are set. */
-    public boolean isComplete() {
-        return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
-                && hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
+    /** Returns the ID of the user this configuration is associated with. */
+    public @UserIdInt int getUserId() {
+        return mUserId;
     }
 
-    /** Returns true if the specified property is set. */
-    public boolean hasProperty(@Property String property) {
-        return mBundle.containsKey(property);
+    /** Returns {@code true} if all known settings are present. */
+    public boolean isComplete() {
+        return hasSetting(SETTING_AUTO_DETECTION_ENABLED)
+                && hasSetting(SETTING_GEO_DETECTION_ENABLED);
+    }
+
+    /** Returns true if the specified setting is set. */
+    public boolean hasSetting(@Setting String setting) {
+        return mBundle.containsKey(setting);
     }
 
     /**
-     * Returns the value of the {@link #PROPERTY_AUTO_DETECTION_ENABLED} property. This
+     * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This
      * controls whether a device will attempt to determine the time zone automatically using
-     * contextual information.
+     * contextual information if the device supports auto detection.
      *
-     * @throws IllegalStateException if the field has not been set
+     * <p>This setting is global and can be updated by some users.
+     *
+     * @throws IllegalStateException if the setting has not been set
      */
     public boolean isAutoDetectionEnabled() {
-        if (!mBundle.containsKey(PROPERTY_AUTO_DETECTION_ENABLED)) {
-            throw new IllegalStateException(PROPERTY_AUTO_DETECTION_ENABLED + " is not set");
-        }
-        return mBundle.getBoolean(PROPERTY_AUTO_DETECTION_ENABLED);
+        enforceSettingPresent(SETTING_AUTO_DETECTION_ENABLED);
+        return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED);
     }
 
     /**
-     * Returns the value of the {@link #PROPERTY_GEO_DETECTION_ENABLED} property. This
-     * controls whether a device can use location to determine time zone. Only used when
-     * {@link #isAutoDetectionEnabled()} is true.
+     * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This
+     * controls whether a device can use geolocation to determine time zone. Only used when
+     * {@link #isAutoDetectionEnabled()} is {@code true} and when the user has allowed their
+     * location to be used.
      *
-     * @throws IllegalStateException if the field has not been set
+     * <p>This setting is user-scoped and can be updated by some users.
+     * See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabled()}.
+     *
+     * @throws IllegalStateException if the setting has not been set
      */
     public boolean isGeoDetectionEnabled() {
-        if (!mBundle.containsKey(PROPERTY_GEO_DETECTION_ENABLED)) {
-            throw new IllegalStateException(PROPERTY_GEO_DETECTION_ENABLED + " is not set");
-        }
-        return mBundle.getBoolean(PROPERTY_GEO_DETECTION_ENABLED);
-    }
-
-    /**
-     * Convenience method to merge this with another. The argument configuration properties have
-     * precedence.
-     */
-    public TimeZoneConfiguration with(TimeZoneConfiguration other) {
-        return new Builder(this).mergeProperties(other).build();
+        enforceSettingPresent(SETTING_GEO_DETECTION_ENABLED);
+        return mBundle.getBoolean(SETTING_GEO_DETECTION_ENABLED);
     }
 
     @Override
@@ -149,43 +151,61 @@
             return false;
         }
         TimeZoneConfiguration that = (TimeZoneConfiguration) o;
-        return mBundle.kindofEquals(that.mBundle);
+        return mUserId == that.mUserId
+                && mBundle.kindofEquals(that.mBundle);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mBundle);
+        return Objects.hash(mUserId, mBundle);
     }
 
     @Override
     public String toString() {
         return "TimeZoneDetectorConfiguration{"
+                + "mUserId=" + mUserId
                 + "mBundle=" + mBundle
                 + '}';
     }
 
+    private void enforceSettingPresent(@Setting String setting) {
+        if (!mBundle.containsKey(setting)) {
+            throw new IllegalStateException(setting + " is not set");
+        }
+    }
+
     /** @hide */
     public static class Builder {
 
-        private Bundle mBundle = new Bundle();
+        private final @UserIdInt int mUserId;
+        private final Bundle mBundle = new Bundle();
 
         /**
-         * Creates a new Builder with no properties set.
+         * Creates a new Builder for a userId with no settings held.
          */
-        public Builder() {}
+        public Builder(@UserIdInt int userId) {
+            mUserId = userId;
+        }
 
         /**
-         * Creates a new Builder by copying properties from an existing instance.
+         * Creates a new Builder by copying the user ID and settings from an existing instance.
          */
         public Builder(TimeZoneConfiguration toCopy) {
+            this.mUserId = toCopy.mUserId;
             mergeProperties(toCopy);
         }
 
         /**
-         * Merges {@code other} properties into this instances, replacing existing values in this
-         * where the properties appear in both.
+         * Merges {@code other} settings into this instances, replacing existing values in this
+         * where the settings appear in both.
          */
         public Builder mergeProperties(TimeZoneConfiguration other) {
+            if (mUserId != other.mUserId) {
+                throw new IllegalArgumentException(
+                        "Cannot merge configurations for different user IDs."
+                                + " this.mUserId=" + this.mUserId
+                                + ", other.mUserId=" + other.mUserId);
+            }
             this.mBundle.putAll(other.mBundle);
             return this;
         }
@@ -195,15 +215,19 @@
             return this;
         }
 
-        /** Sets the desired state of the automatic time zone detection property. */
+        /**
+         * Sets the state of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting.
+         */
         public Builder setAutoDetectionEnabled(boolean enabled) {
-            this.mBundle.putBoolean(PROPERTY_AUTO_DETECTION_ENABLED, enabled);
+            this.mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled);
             return this;
         }
 
-        /** Sets the desired state of the geolocation time zone detection enabled property. */
+        /**
+         * Sets the state of the {@link #SETTING_GEO_DETECTION_ENABLED} setting.
+         */
         public Builder setGeoDetectionEnabled(boolean enabled) {
-            this.mBundle.putBoolean(PROPERTY_GEO_DETECTION_ENABLED, enabled);
+            this.mBundle.putBoolean(SETTING_GEO_DETECTION_ENABLED, enabled);
             return this;
         }
 
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 7885613..2b1cbf2 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -37,37 +37,29 @@
     TimeZoneCapabilities getCapabilities();
 
     /**
-     * Returns the current user's complete time zone configuration. See {@link
-     * TimeZoneConfiguration}.
-     */
-    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-    @NonNull
-    TimeZoneConfiguration getConfiguration();
-
-    /**
      * Modifies the time zone detection configuration.
      *
-     * <p>Configuration properties vary in scope: some may be device-wide, others may be specific to
-     * the current user.
+     * <p>Configuration settings vary in scope: some may be global (affect all users), others may be
+     * specific to the current user.
      *
-     * <p>The ability to modify configuration properties can be subject to restrictions. For
+     * <p>The ability to modify configuration settings can be subject to restrictions. For
      * example, they may be determined by device hardware, general policy (i.e. only the primary
-     * user can set them), or by a managed device policy. See {@link #getCapabilities()} to obtain
+     * user can set them), or by a managed device policy. Use {@link #getCapabilities()} to obtain
      * information at runtime about the user's capabilities.
      *
-     * <p>Attempts to set configuration with capabilities that are {@link
+     * <p>Attempts to modify configuration settings with capabilities that are {@link
      * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link
      * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false}
-     * will be returned. Setting configuration with capabilities that are {@link
+     * will be returned. Modifying configuration settings with capabilities that are {@link
      * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link
      * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link
      * TimeZoneCapabilities} for further details.
      *
-     * <p>If the configuration is not "complete", then only the specified properties will be
-     * updated (where the user's capabilities allow) and other settings will be left unchanged. See
-     * {@link TimeZoneConfiguration#isComplete()}.
+     * <p>If the supplied configuration only has some values set, then only the specified settings
+     * will be updated (where the user's capabilities allow) and other settings will be left
+     * unchanged.
      *
-     * @return {@code true} if all the configuration properties specified have been set to the
+     * @return {@code true} if all the configuration settings specified have been set to the
      *   new values, {@code false} if none have
      */
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -76,14 +68,20 @@
     /**
      * An interface that can be used to listen for changes to the time zone detector configuration.
      */
+    @FunctionalInterface
     interface TimeZoneConfigurationListener {
-        /** Called when the configuration changes. There are no guarantees about the thread used. */
-        void onChange(@NonNull TimeZoneConfiguration configuration);
+        /**
+         * Called when something about the time zone configuration on the device has changed.
+         * This could be because the current user has changed, one of the device's relevant settings
+         * has changed, or something that could affect a user's capabilities has changed.
+         * There are no guarantees about the thread used.
+         */
+        void onChange();
     }
 
     /**
-     * Registers a listener that will be informed when the configuration changes. The complete
-     * configuration is passed to the listener, not just the properties that have changed.
+     * Registers a listener that will be informed when something about the time zone configuration
+     * changes.
      */
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener);
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
index 0770aff..4c69732 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
@@ -57,19 +57,6 @@
     }
 
     @Override
-    @NonNull
-    public TimeZoneConfiguration getConfiguration() {
-        if (DEBUG) {
-            Log.d(TAG, "getConfiguration called");
-        }
-        try {
-            return mITimeZoneDetectorService.getConfiguration();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @Override
     public boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration) {
         if (DEBUG) {
             Log.d(TAG, "updateConfiguration called: " + configuration);
@@ -94,8 +81,8 @@
                 ITimeZoneConfigurationListener iListener =
                         new ITimeZoneConfigurationListener.Stub() {
                     @Override
-                    public void onChange(@NonNull TimeZoneConfiguration configuration) {
-                        notifyConfigurationListeners(configuration);
+                    public void onChange() {
+                        notifyConfigurationListeners();
                     }
                 };
                 mConfigurationReceiver = iListener;
@@ -116,14 +103,14 @@
         }
     }
 
-    private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) {
+    private void notifyConfigurationListeners() {
         final ArraySet<TimeZoneConfigurationListener> configurationListeners;
         synchronized (this) {
             configurationListeners = new ArraySet<>(mConfigurationListeners);
         }
         int size = configurationListeners.size();
         for (int i = 0; i < size; i++) {
-            configurationListeners.valueAt(i).onChange(configuration);
+            configurationListeners.valueAt(i).onChange();
         }
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c302def..03cf0cf 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6404,6 +6404,17 @@
         public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY;
 
         /**
+         * The current location time zone detection enabled state for the user.
+         *
+         * See {@link
+         * android.app.timezonedetector.TimeZoneDetector#getCapabilities} for access. See {@link
+         * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update.
+         * @hide
+         */
+        public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED =
+                "location_time_zone_detection_enabled";
+
+        /**
          * The accuracy in meters used for coarsening location for clients with only the coarse
          * location permission.
          *
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 73d148c..0834b2d 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -36,7 +36,18 @@
             Consts.TAG_WM),
     WM_DEBUG_ADD_REMOVE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
-    WM_DEBUG_FOCUS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+    WM_DEBUG_CONFIGURATION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_SWITCH(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_CONTAINERS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_FOCUS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_IMMERSIVE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_LOCKTASK(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
     WM_DEBUG_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
     WM_SHOW_TRANSACTIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 23af70a..cbcbe7f 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -613,7 +613,7 @@
     }
 
     // Do not change sched policy cgroup after boot complete.
-    rc = androidSetThreadPriority(pid, pri, !boot_completed);
+    rc = androidSetThreadPriorityAndPolicy(pid, pri, !boot_completed);
     if (rc != 0) {
         if (rc == INVALID_OPERATION) {
             signalExceptionForPriorityError(env, errno, pid);
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
index 72391f4..db127c6 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
@@ -22,6 +22,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 
 import org.junit.Test;
 
@@ -31,11 +32,22 @@
 
     @Test
     public void testEquals() {
-        TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
+        TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        TimeZoneConfiguration configuration2 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(false)
+                .setGeoDetectionEnabled(false)
+                .build();
+
+        TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder()
+                .setConfiguration(configuration1)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
-        TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
+        TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder()
+                .setConfiguration(configuration1)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
@@ -45,6 +57,20 @@
             assertEquals(one, two);
         }
 
+        builder2.setConfiguration(configuration2);
+        {
+            TimeZoneCapabilities one = builder1.build();
+            TimeZoneCapabilities two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder1.setConfiguration(configuration2);
+        {
+            TimeZoneCapabilities one = builder1.build();
+            TimeZoneCapabilities two = builder2.build();
+            assertEquals(one, two);
+        }
+
         builder2.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
         {
             TimeZoneCapabilities one = builder1.build();
@@ -90,7 +116,12 @@
 
     @Test
     public void testParcelable() {
-        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
+        TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder()
+                .setConfiguration(configuration)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
@@ -105,4 +136,51 @@
         builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
         assertRoundTripParcelable(builder.build());
     }
+
+    @Test
+    public void testApplyUpdate_permitted() {
+        TimeZoneConfiguration oldConfiguration =
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                        .setAutoDetectionEnabled(true)
+                        .setGeoDetectionEnabled(true)
+                        .build();
+        TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder()
+                .setConfiguration(oldConfiguration)
+                .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setSuggestManualTimeZone(CAPABILITY_POSSESSED)
+                .build();
+        assertEquals(oldConfiguration, capabilities.getConfiguration());
+
+        TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(false)
+                .build();
+
+        TimeZoneConfiguration expected = new TimeZoneConfiguration.Builder(oldConfiguration)
+                .setAutoDetectionEnabled(false)
+                .build();
+        assertEquals(expected, capabilities.applyUpdate(configChange));
+    }
+
+    @Test
+    public void testApplyUpdate_notPermitted() {
+        TimeZoneConfiguration oldConfiguration =
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                        .setAutoDetectionEnabled(true)
+                        .setGeoDetectionEnabled(true)
+                        .build();
+        TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder()
+                .setConfiguration(oldConfiguration)
+                .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
+                .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED)
+                .build();
+        assertEquals(oldConfiguration, capabilities.getConfiguration());
+
+        TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(false)
+                .build();
+
+        assertNull(capabilities.applyUpdate(configChange));
+    }
 }
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
index 00dc73e..faf908d 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
@@ -27,11 +27,14 @@
 
 public class TimeZoneConfigurationTest {
 
+    private static final int ARBITRARY_USER_ID = 9876;
+
     @Test
     public void testBuilder_copyConstructor() {
-        TimeZoneConfiguration.Builder builder1 = new TimeZoneConfiguration.Builder()
-                .setAutoDetectionEnabled(true)
-                .setGeoDetectionEnabled(true);
+        TimeZoneConfiguration.Builder builder1 =
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                        .setAutoDetectionEnabled(true)
+                        .setGeoDetectionEnabled(true);
         TimeZoneConfiguration configuration1 = builder1.build();
 
         TimeZoneConfiguration configuration2 =
@@ -41,28 +44,28 @@
     }
 
     @Test
-    public void testIsComplete() {
-        TimeZoneConfiguration.Builder builder =
-                new TimeZoneConfiguration.Builder();
-        assertFalse(builder.build().isComplete());
+    public void testIntrospectionMethods() {
+        TimeZoneConfiguration empty = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID).build();
+        assertFalse(empty.isComplete());
+        assertFalse(empty.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED));
 
-        builder.setAutoDetectionEnabled(true);
-        assertFalse(builder.build().isComplete());
-
-        builder.setGeoDetectionEnabled(true);
-        assertTrue(builder.build().isComplete());
+        TimeZoneConfiguration completeConfig = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        assertTrue(completeConfig.isComplete());
+        assertTrue(completeConfig.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED));
     }
 
     @Test
     public void testBuilder_mergeProperties() {
-        TimeZoneConfiguration configuration1 =
-                new TimeZoneConfiguration.Builder()
-                        .setAutoDetectionEnabled(true)
-                        .build();
+        TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .build();
 
         {
             TimeZoneConfiguration mergedEmptyAnd1 =
-                    new TimeZoneConfiguration.Builder()
+                    new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
                             .mergeProperties(configuration1)
                             .build();
             assertEquals(configuration1, mergedEmptyAnd1);
@@ -70,7 +73,7 @@
 
         {
             TimeZoneConfiguration configuration2 =
-                    new TimeZoneConfiguration.Builder()
+                    new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
                             .setAutoDetectionEnabled(false)
                             .build();
 
@@ -87,14 +90,22 @@
     @Test
     public void testEquals() {
         TimeZoneConfiguration.Builder builder1 =
-                new TimeZoneConfiguration.Builder();
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID);
         {
             TimeZoneConfiguration one = builder1.build();
             assertEquals(one, one);
         }
 
+        {
+            TimeZoneConfiguration.Builder differentUserBuilder =
+                    new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID + 1);
+            TimeZoneConfiguration one = builder1.build();
+            TimeZoneConfiguration two = differentUserBuilder.build();
+            assertNotEquals(one, two);
+        }
+
         TimeZoneConfiguration.Builder builder2 =
-                new TimeZoneConfiguration.Builder();
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID);
         {
             TimeZoneConfiguration one = builder1.build();
             TimeZoneConfiguration two = builder2.build();
@@ -148,7 +159,7 @@
     @Test
     public void testParcelable() {
         TimeZoneConfiguration.Builder builder =
-                new TimeZoneConfiguration.Builder();
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID);
         assertRoundTripParcelable(builder.build());
 
         builder.setAutoDetectionEnabled(true);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 73296987..03f7be7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -13,6 +13,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-2121056984": {
+      "message": "%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "-2109936758": {
       "message": "removeAppToken make exiting: %s",
       "level": "VERBOSE",
@@ -43,6 +49,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-2029985709": {
+      "message": "setFocusedTask: taskId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-2024464438": {
       "message": "app-onAnimationFinished(): mOuter=%s",
       "level": "DEBUG",
@@ -139,6 +151,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "-1868048288": {
+      "message": "Updating to new configuration after starting activity.",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityStarter.java"
+    },
     "-1862269827": {
       "message": "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
       "level": "VERBOSE",
@@ -163,6 +181,18 @@
       "group": "WM_DEBUG_RESIZE",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-1810446914": {
+      "message": "Trying to update display configuration for system\/invalid process.",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
+    "-1791031393": {
+      "message": "Ensuring correct configuration: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1782453012": {
       "message": "Checking theme of starting window: 0x%x",
       "level": "VERBOSE",
@@ -211,6 +241,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "-1699018375": {
+      "message": "Adding activity %s to task %s callers: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
     "-1698815688": {
       "message": "Resetting app token %s of replacing window marks.",
       "level": "DEBUG",
@@ -229,12 +265,30 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1638958146": {
+      "message": "Removing activity %s from task=%s adding to task=%s Callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
+    },
     "-1632122349": {
       "message": "Changing surface while display frozen: %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1630752478": {
+      "message": "removeLockedTask: removed %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
+    "-1598452494": {
+      "message": "activityDestroyedLocked: r=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_CONTAINERS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1596995693": {
       "message": "startAnimation",
       "level": "DEBUG",
@@ -307,6 +361,18 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "-1495062622": {
+      "message": "Can't report activity moved to display - client not running, activityRecord=%s, displayId=%d",
+      "level": "WARN",
+      "group": "WM_DEBUG_SWITCH",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
+    "-1492881555": {
+      "message": "Starting activity when config will change = %b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityStarter.java"
+    },
     "-1471946192": {
       "message": "Marking app token %s with replacing child windows.",
       "level": "DEBUG",
@@ -379,6 +445,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-1305755880": {
+      "message": "Initial config: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-1292329638": {
       "message": "Added starting %s: startingWindow=%s startingView=%s",
       "level": "VERBOSE",
@@ -445,6 +517,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "-1155279885": {
+      "message": "Frontmost changed immersion: %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_IMMERSIVE",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-1144293044": {
       "message": "SURFACE SET FREEZE LAYER: %s",
       "level": "INFO",
@@ -475,6 +553,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "-1115019498": {
+      "message": "Configuration & display unchanged in %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1113134997": {
       "message": "Attempted to add application window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -559,12 +643,24 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-929676529": {
+      "message": "Configuration changes for %s, allChanges=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-928291778": {
       "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/AppTransition.java"
     },
+    "-927199900": {
+      "message": "Updating global configuration to: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-916108501": {
       "message": "Adding %s to %s",
       "level": "VERBOSE",
@@ -619,12 +715,24 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-846078709": {
+      "message": "Configuration doesn't matter in finishing %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-809771899": {
       "message": "findFocusedWindow: Reached focused app=%s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "-804217032": {
+      "message": "Skipping config check (will change): %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-793346159": {
       "message": "New transit into wallpaper: %s",
       "level": "VERBOSE",
@@ -673,6 +781,18 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-743431900": {
+      "message": "Configuration no differences in %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
+    "-716565534": {
+      "message": "moveActivityStackToFront: unfocusable activity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-714291355": {
       "message": "Losing delayed focus: %s",
       "level": "INFO",
@@ -739,6 +859,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "-593535526": {
+      "message": "Binding proc %s with config %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/am\/ActivityManagerService.java"
+    },
     "-583031528": {
       "message": "%s",
       "level": "INFO",
@@ -757,6 +883,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-548282316": {
+      "message": "setLockTaskMode: Locking to %s Callers=%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "-547111355": {
       "message": "hideIme Control target: %s ",
       "level": "DEBUG",
@@ -781,6 +913,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-503656156": {
+      "message": "Update process config of %s to new config %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-496681057": {
       "message": "Attempted to get remove mode of a display that does not exist: %d",
       "level": "WARN",
@@ -799,6 +937,12 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
+    "-449118559": {
+      "message": "Trying to update display configuration for invalid process, pid=%d",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-445944810": {
       "message": "finish(%b): mCanceled=%b",
       "level": "DEBUG",
@@ -835,6 +979,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
     },
+    "-401282500": {
+      "message": "destroyIfPossible: r=%s destroy returned removed=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_CONTAINERS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-395922585": {
       "message": "InsetsSource setWin %s",
       "level": "DEBUG",
@@ -907,18 +1057,42 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-317194205": {
+      "message": "clearLockedTasks: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "-303497363": {
       "message": "reparent: moving activity=%s to task=%d at %d",
       "level": "INFO",
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-272719931": {
+      "message": "startLockTaskModeLocked: %s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
+    "-260960989": {
+      "message": "Removing and adding activity %s to stack at top callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
     "-251259736": {
       "message": "No longer freezing: %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-235225312": {
+      "message": "Skipping config check for initializing activity: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-198463978": {
       "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
       "level": "VERBOSE",
@@ -943,6 +1117,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
     },
+    "-168799453": {
+      "message": "Allowing features %d:0x%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-167822951": {
       "message": "Attempted to add starting window to token with already existing starting window",
       "level": "WARN",
@@ -979,6 +1159,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-90559682": {
+      "message": "Config is skipping already pausing %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-87705714": {
       "message": "findFocusedWindow: focusedApp=null using new focus @ %s",
       "level": "VERBOSE",
@@ -1303,6 +1489,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "355940361": {
+      "message": "Config is destroying non-running %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "371641947": {
       "message": "Window Manager Crash %s",
       "level": "WTF",
@@ -1315,6 +1507,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "374506950": {
+      "message": "Reporting activity moved to display, activityRecord=%s, displayId=%d, config=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SWITCH",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "374972436": {
       "message": "performEnableScreen: Waiting for anim complete",
       "level": "INFO",
@@ -1411,6 +1609,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "556758086": {
+      "message": "Applying new update lock state '%s' for %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_IMMERSIVE",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "557227556": {
       "message": "onAnimationFinished(): Notify animation finished:",
       "level": "DEBUG",
@@ -1531,12 +1735,6 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "676824470": {
-      "message": "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
-      "level": "ERROR",
-      "group": "TEST_GROUP",
-      "at": "com\/android\/server\/wm\/ProtoLogGroup.java"
-    },
     "685047360": {
       "message": "Resizing window %s",
       "level": "VERBOSE",
@@ -1561,6 +1759,18 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "715749922": {
+      "message": "Allowlisting %d:%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
+    "736692676": {
+      "message": "Config is relaunching %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "745391677": {
       "message": "  CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x \/ %s",
       "level": "INFO",
@@ -1615,6 +1825,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "869266572": {
+      "message": "Removing activity %s from stack, reason= %s callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "873914452": {
       "message": "goodToGo()",
       "level": "DEBUG",
@@ -1645,12 +1861,30 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
+    "950074526": {
+      "message": "setLockTaskMode: Can't lock due to auth",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "954470154": {
       "message": "FORCED DISPLAY SCALING DISABLED",
       "level": "INFO",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "956374481": {
+      "message": "removeLockedTask: task=%s last task, reverting locktask mode. Callers=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
+    "969323241": {
+      "message": "Sending new config to %s, config: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "972354148": {
       "message": "\tcontainer=%s",
       "level": "DEBUG",
@@ -1663,12 +1897,24 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1040675582": {
+      "message": "Can't report activity configuration update - client not running, activityRecord=%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1046922686": {
       "message": "requestScrollCapture: caught exception dispatching callback: %s",
       "level": "WARN",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1049367566": {
+      "message": "Sending to proc %s new config %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/WindowProcessController.java"
+    },
     "1051545910": {
       "message": "Exit animation finished in %s: remove=%b",
       "level": "VERBOSE",
@@ -1681,6 +1927,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
     },
+    "1088929964": {
+      "message": "onLockTaskPackagesUpdated: starting new locktask task=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "1089714158": {
       "message": "  FREEZE %s: DESTROY",
       "level": "INFO",
@@ -1789,6 +2041,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1337596507": {
+      "message": "Sending to proc %s new compat %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/CompatModePackages.java"
+    },
     "1346895820": {
       "message": "ScreenRotation still animating: type: %d\nmDisplayAnimator: %s\nmEnterBlackFrameAnimator: %s\nmRotateScreenAnimator: %s\nmScreenshotRotationAnimator: %s",
       "level": "VERBOSE",
@@ -1801,6 +2059,12 @@
       "group": "WM_DEBUG_FOCUS",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "1360551978": {
+      "message": "Trying to update display configuration for non-existing displayId=%d",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "1364498663": {
       "message": "notifyAppResumed: wasStopped=%b %s",
       "level": "VERBOSE",
@@ -1819,6 +2083,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
     },
+    "1401295262": {
+      "message": "Mode default, asking user",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "1401700824": {
       "message": "Window drawn win=%s",
       "level": "DEBUG",
@@ -1927,6 +2197,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1522489371": {
+      "message": "moveActivityStackToFront: activity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1525976603": {
       "message": "cancelAnimation(): reason=%s",
       "level": "DEBUG",
@@ -1951,6 +2227,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1576607724": {
+      "message": "Report configuration: %s %s %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "1577579529": {
       "message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b",
       "level": "ERROR",
@@ -1981,6 +2263,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1635062046": {
+      "message": "Skipping config check invisible: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1635462459": {
       "message": "onMovedByResize: Moving %s",
       "level": "DEBUG",
@@ -2023,6 +2311,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1679569477": {
+      "message": "Configuration doesn't matter not running %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1720229827": {
       "message": "Creating animation bounds layer",
       "level": "INFO",
@@ -2077,12 +2371,30 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1789603530": {
+      "message": "Removing activity %s hasSavedState=%b stateNotNeeded=%s finishing=%b state=%s callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1822843721": {
       "message": "Aborted starting %s: startingData=%s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1824105730": {
+      "message": "setLockTaskAuth: task=%s mLockTaskAuth=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
+    "1829094918": {
+      "message": "onLockTaskPackagesUpdated: removing %s mLockTaskAuth()=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "1831008694": {
       "message": "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s surfaceInsets=%s",
       "level": "DEBUG",
@@ -2161,6 +2473,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
+    "1975793405": {
+      "message": "setFocusedStack: stackId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "1984470582": {
       "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
       "level": "DEBUG",
@@ -2173,6 +2491,12 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/WindowAnimator.java"
     },
+    "1995093920": {
+      "message": "Checking to restart %s: changed=0x%s, handles=0x%s, mLastReportedConfiguration=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "2016061474": {
       "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
       "level": "VERBOSE",
@@ -2191,6 +2515,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "2022322588": {
+      "message": "Adding activity %s to stack to task %s callers: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
     "2022422429": {
       "message": "createAnimationAdapter(): container=%s",
       "level": "DEBUG",
@@ -2269,6 +2599,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "2134999275": {
+      "message": "moveActivityStackToFront: already on top, activity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "2137411379": {
       "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b Callers=%s",
       "level": "VERBOSE",
@@ -2277,9 +2613,6 @@
     }
   },
   "groups": {
-    "TEST_GROUP": {
-      "tag": "WindowManagetProtoLogTest"
-    },
     "WM_DEBUG_ADD_REMOVE": {
       "tag": "WindowManager"
     },
@@ -2292,6 +2625,12 @@
     "WM_DEBUG_BOOT": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_CONFIGURATION": {
+      "tag": "WindowManager"
+    },
+    "WM_DEBUG_CONTAINERS": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_DRAW": {
       "tag": "WindowManager"
     },
@@ -2304,9 +2643,15 @@
     "WM_DEBUG_IME": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_IMMERSIVE": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_KEEP_SCREEN_ON": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_LOCKTASK": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_ORIENTATION": {
       "tag": "WindowManager"
     },
@@ -2325,6 +2670,9 @@
     "WM_DEBUG_STARTING_WINDOW": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_SWITCH": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_WINDOW_MOVEMENT": {
       "tag": "WindowManager"
     },
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 3b22fdb..38e1a48 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -299,10 +299,10 @@
         // The glass pane is used to view touch events before passed to the notification list.
         // This allows us to initialize gesture listeners and detect when to close the notifications
         glassPane.setOnTouchListener((v, event) -> {
-            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+            if (isClosingAction(event)) {
                 mNotificationListAtEndAtTimeOfTouch = false;
             }
-            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            if (isOpeningAction(event)) {
                 mFirstTouchDownOnGlassPane = event.getRawX();
                 mNotificationListAtEndAtTimeOfTouch = mNotificationListAtEnd;
                 // Reset the tracker when there is a touch down on the glass pane.
@@ -355,8 +355,7 @@
             if (rect != null) {
                 clippedHeight = rect.bottom;
             }
-            if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
-                    && mIsSwipingVerticallyToClose) {
+            if (!handled && isClosingAction(event) && mIsSwipingVerticallyToClose) {
                 if (getSettleClosePercentage() < getPercentageFromEndingEdge() && isTracking) {
                     animatePanel(DEFAULT_FLING_VELOCITY, false);
                 } else if (clippedHeight != getLayout().getHeight() && isTracking) {
@@ -369,7 +368,7 @@
             // Updating the mNotificationListAtEndAtTimeOfTouch state has to be done after
             // the event has been passed to the closeGestureDetector above, such that the
             // closeGestureDetector sees the up event before the state has changed.
-            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+            if (isClosingAction(event)) {
                 mNotificationListAtEndAtTimeOfTouch = false;
             }
             return handled || isTracking;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
index 45808a8..bde31f1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
@@ -191,6 +191,38 @@
         }
     }
 
+    /** Checks if a {@link MotionEvent} is an action to open the panel.
+     * @param e {@link MotionEvent} to check.
+     * @return true only if opening action.
+     */
+    protected boolean isOpeningAction(MotionEvent e) {
+        if (mAnimateDirection == POSITIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_DOWN;
+        }
+
+        if (mAnimateDirection == NEGATIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_UP;
+        }
+
+        return false;
+    }
+
+    /** Checks if a {@link MotionEvent} is an action to close the panel.
+     * @param e {@link MotionEvent} to check.
+     * @return true only if closing action.
+     */
+    protected boolean isClosingAction(MotionEvent e) {
+        if (mAnimateDirection == POSITIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_UP;
+        }
+
+        if (mAnimateDirection == NEGATIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_DOWN;
+        }
+
+        return false;
+    }
+
     /* ***************************************************************************************** *
      * Panel Animation
      * ***************************************************************************************** */
@@ -243,8 +275,7 @@
      * Depending on certain conditions, determines whether to fully expand or collapse the panel.
      */
     protected void maybeCompleteAnimation(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_UP
-                && isPanelVisible()) {
+        if (isClosingAction(event) && isPanelVisible()) {
             if (mSettleClosePercentage < mPercentageFromEndingEdge) {
                 animatePanel(DEFAULT_FLING_VELOCITY, false);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index e38dce0..8364b48 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -262,11 +262,11 @@
             onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
         } else if (isLongPress) {
             requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
-                    null /* onPulseSupressedListener */);
+                    null /* onPulseSuppressedListener */);
         } else if (isWakeLockScreen) {
             if (wakeEvent) {
                 requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
-                        null /* onPulseSupressedListener */);
+                        null /* onPulseSuppressedListener */);
             }
         } else {
             proximityCheckThenCall((result) -> {
@@ -536,7 +536,7 @@
             if (PULSE_ACTION.equals(intent.getAction())) {
                 if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
                 requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
-                        null /* onPulseSupressedListener */);
+                        null /* onPulseSuppressedListener */);
             }
             if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
                 mMachine.requestState(DozeMachine.State.FINISH);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 214088c..7037403 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -36,8 +36,8 @@
  * Activity to show the PIP menu to control PIP.
  */
 public class PipMenuActivity extends Activity implements PipManager.Listener {
-    private static final boolean DEBUG = false;
     private static final String TAG = "PipMenuActivity";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
 
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index b587dd3..d127172 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -594,15 +594,23 @@
                     if (pointerIndex < 0) {
                         return;
                     }
-                    final float deltaX =
-                            mReceivedPointerTracker.getReceivedPointerDownX(pointerId)
-                                    - rawEvent.getX(pointerIndex);
-                    final float deltaY =
-                            mReceivedPointerTracker.getReceivedPointerDownY(pointerId)
-                                    - rawEvent.getY(pointerIndex);
-                    final double moveDelta = Math.hypot(deltaX, deltaY);
-                    if (moveDelta < mTouchSlop) {
-                        return;
+                    // Require both fingers to have moved a certain amount before starting a drag.
+                    for (int index = 0; index < event.getPointerCount(); ++index) {
+                        int id = event.getPointerId(index);
+                        if (!mReceivedPointerTracker.isReceivedPointerDown(id)) {
+                            // Something is wrong with the event stream.
+                            Slog.e(LOG_TAG, "Invalid pointer id: " + id);
+                        }
+                        final float deltaX =
+                                mReceivedPointerTracker.getReceivedPointerDownX(id)
+                                        - rawEvent.getX(index);
+                        final float deltaY =
+                                mReceivedPointerTracker.getReceivedPointerDownY(id)
+                                        - rawEvent.getY(index);
+                        final double moveDelta = Math.hypot(deltaX, deltaY);
+                        if (moveDelta < mTouchSlop) {
+                            return;
+                        }
                     }
                 }
                 // More than one pointer so the user is not touch exploring
@@ -612,12 +620,20 @@
                 if (isDraggingGesture(event)) {
                     // Two pointers moving in the same direction within
                     // a given distance perform a drag.
-                    mState.startDragging();
                     computeDraggingPointerIdIfNeeded(event);
                     pointerIdBits = 1 << mDraggingPointerId;
                     event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
-                    mDispatcher.sendMotionEvent(
-                            event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+                    MotionEvent downEvent = computeDownEventForDrag(event);
+                    if (downEvent != null) {
+                        mDispatcher.sendMotionEvent(
+                                downEvent, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+                        mDispatcher.sendMotionEvent(
+                                event, ACTION_MOVE, rawEvent, pointerIdBits, policyFlags);
+                    } else {
+                        mDispatcher.sendMotionEvent(
+                                event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+                    }
+                    mState.startDragging();
                 } else {
                     // Two pointers moving arbitrary are delegated to the view hierarchy.
                     mState.startDelegating();
@@ -1004,6 +1020,49 @@
         return distance;
     }
 
+    /**
+     * Creates a down event using the down coordinates of the dragging pointer and other information
+     * from the supplied event. The supplied event's down time is adjusted to reflect the time when
+     * the dragging pointer initially went down.
+     */
+    private MotionEvent computeDownEventForDrag(MotionEvent event) {
+        // Creating a down event only  makes sense if we haven't started touch exploring yet.
+        if (mState.isTouchExploring()
+                || mDraggingPointerId == INVALID_POINTER_ID
+                || event == null) {
+            return null;
+        }
+        final float x = mReceivedPointerTracker.getReceivedPointerDownX(mDraggingPointerId);
+        final float y = mReceivedPointerTracker.getReceivedPointerDownY(mDraggingPointerId);
+        final long time = mReceivedPointerTracker.getReceivedPointerDownTime(mDraggingPointerId);
+        MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1];
+        coords[0] = new MotionEvent.PointerCoords();
+        coords[0].x = x;
+        coords[0].y = y;
+        MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1];
+        properties[0] = new MotionEvent.PointerProperties();
+        properties[0].id = mDraggingPointerId;
+        properties[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
+        MotionEvent downEvent =
+                MotionEvent.obtain(
+                        time,
+                        time,
+                        ACTION_DOWN,
+                        1,
+                        properties,
+                        coords,
+                        event.getMetaState(),
+                        event.getButtonState(),
+                        event.getXPrecision(),
+                        event.getYPrecision(),
+                        event.getDeviceId(),
+                        event.getEdgeFlags(),
+                        event.getSource(),
+                        event.getFlags());
+        event.setDownTime(time);
+        return downEvent;
+    }
+
     public TouchState getState() {
         return mState;
     }
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
index 1c4db12..59ba82e 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.util.Slog;
@@ -108,9 +109,9 @@
 
         @Override
         public void createPredictionSession(@NonNull AppPredictionContext context,
-                @NonNull AppPredictionSessionId sessionId) {
-            runForUserLocked("createPredictionSession", sessionId,
-                    (service) -> service.onCreatePredictionSessionLocked(context, sessionId));
+                @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) {
+            runForUserLocked("createPredictionSession", sessionId, (service) ->
+                    service.onCreatePredictionSessionLocked(context, sessionId, token));
         }
 
         @Override
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 7ee607c..735f420 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -30,6 +30,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ServiceInfo;
+import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.provider.DeviceConfig;
@@ -44,8 +45,6 @@
 import com.android.server.infra.AbstractPerUserSystemService;
 import com.android.server.people.PeopleServiceInternal;
 
-import java.util.function.Consumer;
-
 /**
  * Per-user instance of {@link AppPredictionManagerService}.
  */
@@ -112,17 +111,24 @@
      */
     @GuardedBy("mLock")
     public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
-            @NonNull AppPredictionSessionId sessionId) {
-        if (!mSessionInfos.containsKey(sessionId)) {
-            mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
-                    DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
-                            PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false),
-                    this::removeAppPredictionSessionInfo));
-        }
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.onCreatePredictionSession(context, sessionId), true);
-        if (!serviceExists) {
-            mSessionInfos.remove(sessionId);
+            @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) {
+        final boolean usesPeopleService = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+                PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false);
+        final boolean serviceExists = resolveService(sessionId, false,
+                usesPeopleService, s -> s.onCreatePredictionSession(context, sessionId));
+        if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
+            final AppPredictionSessionInfo sessionInfo = new AppPredictionSessionInfo(
+                    sessionId, context, usesPeopleService, token, () -> {
+                synchronized (mLock) {
+                    onDestroyPredictionSessionLocked(sessionId);
+                }
+            });
+            if (sessionInfo.linkToDeath()) {
+                mSessionInfos.put(sessionId, sessionInfo);
+            } else {
+                // destroy the session if calling process is already dead
+                onDestroyPredictionSessionLocked(sessionId);
+            }
         }
     }
 
@@ -132,7 +138,10 @@
     @GuardedBy("mLock")
     public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull AppTargetEvent event) {
-        resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event), false);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
+                s -> s.notifyAppTargetEvent(sessionId, event));
     }
 
     /**
@@ -141,8 +150,10 @@
     @GuardedBy("mLock")
     public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-        resolveService(sessionId, s ->
-                s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds), false);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
+                s -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
     }
 
     /**
@@ -151,7 +162,10 @@
     @GuardedBy("mLock")
     public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
-        resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback), true);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, true, sessionInfo.mUsesPeopleService,
+                s -> s.sortAppTargets(sessionId, targets, callback));
     }
 
     /**
@@ -160,10 +174,12 @@
     @GuardedBy("mLock")
     public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.registerPredictionUpdates(sessionId, callback), false);
         final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (serviceExists && sessionInfo != null) {
+        if (sessionInfo == null) return;
+        final boolean serviceExists = resolveService(sessionId, false,
+                sessionInfo.mUsesPeopleService,
+                s -> s.registerPredictionUpdates(sessionId, callback));
+        if (serviceExists) {
             sessionInfo.addCallbackLocked(callback);
         }
     }
@@ -174,10 +190,12 @@
     @GuardedBy("mLock")
     public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.unregisterPredictionUpdates(sessionId, callback), false);
         final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (serviceExists && sessionInfo != null) {
+        if (sessionInfo == null) return;
+        final boolean serviceExists = resolveService(sessionId, false,
+                sessionInfo.mUsesPeopleService,
+                s -> s.unregisterPredictionUpdates(sessionId, callback));
+        if (serviceExists) {
             sessionInfo.removeCallbackLocked(callback);
         }
     }
@@ -187,7 +205,10 @@
      */
     @GuardedBy("mLock")
     public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
-        resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId), true);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, true, sessionInfo.mUsesPeopleService,
+                s -> s.requestPredictionUpdate(sessionId));
     }
 
     /**
@@ -195,12 +216,14 @@
      */
     @GuardedBy("mLock")
     public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.onDestroyPredictionSession(sessionId), false);
-        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (serviceExists && sessionInfo != null) {
-            sessionInfo.destroy();
+        if (isDebug()) {
+            Slog.d(TAG, "onDestroyPredictionSessionLocked(): sessionId=" + sessionId);
         }
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.remove(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
+                s -> s.onDestroyPredictionSession(sessionId));
+        sessionInfo.destroy();
     }
 
     @Override
@@ -291,27 +314,18 @@
         }
 
         for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) {
-            sessionInfo.resurrectSessionLocked(this);
-        }
-    }
-
-    private void removeAppPredictionSessionInfo(AppPredictionSessionId sessionId) {
-        if (isDebug()) {
-            Slog.d(TAG, "removeAppPredictionSessionInfo(): sessionId=" + sessionId);
-        }
-        synchronized (mLock) {
-            mSessionInfos.remove(sessionId);
+            sessionInfo.resurrectSessionLocked(this, sessionInfo.mToken);
         }
     }
 
     @GuardedBy("mLock")
     @Nullable
-    protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId,
-            @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb,
-            boolean sendImmediately) {
-        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (sessionInfo == null) return false;
-        if (sessionInfo.mUsesPeopleService) {
+    protected boolean resolveService(
+            @NonNull final AppPredictionSessionId sessionId,
+            boolean sendImmediately,
+            boolean usesPeopleService,
+            @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) {
+        if (usesPeopleService) {
             final IPredictionService service =
                     LocalServices.getService(PeopleServiceInternal.class);
             if (service != null) {
@@ -368,7 +382,9 @@
         private final AppPredictionContext mPredictionContext;
         private final boolean mUsesPeopleService;
         @NonNull
-        private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
+        final IBinder mToken;
+        @NonNull
+        final IBinder.DeathRecipient mDeathRecipient;
 
         private final RemoteCallbackList<IPredictionCallback> mCallbacks =
                 new RemoteCallbackList<IPredictionCallback>() {
@@ -388,14 +404,16 @@
                 @NonNull final AppPredictionSessionId id,
                 @NonNull final AppPredictionContext predictionContext,
                 final boolean usesPeopleService,
-                @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+                @NonNull final IBinder token,
+                @NonNull final IBinder.DeathRecipient deathRecipient) {
             if (DEBUG) {
                 Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
             }
             mSessionId = id;
             mPredictionContext = predictionContext;
             mUsesPeopleService = usesPeopleService;
-            mRemoveSessionInfoAction = removeSessionInfoAction;
+            mToken = token;
+            mDeathRecipient = deathRecipient;
         }
 
         void addCallbackLocked(IPredictionCallback callback) {
@@ -414,23 +432,38 @@
             mCallbacks.unregister(callback);
         }
 
+        boolean linkToDeath() {
+            try {
+                mToken.linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Caller is dead before session can be started, sessionId: "
+                            + mSessionId);
+                }
+                return false;
+            }
+            return true;
+        }
+
         void destroy() {
             if (DEBUG) {
                 Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId
                         + " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks.");
             }
+            if (mToken != null) {
+                mToken.unlinkToDeath(mDeathRecipient, 0);
+            }
             mCallbacks.kill();
-            mRemoveSessionInfoAction.accept(mSessionId);
         }
 
-        void resurrectSessionLocked(AppPredictionPerUserService service) {
+        void resurrectSessionLocked(AppPredictionPerUserService service, IBinder token) {
             int callbackCount = mCallbacks.getRegisteredCallbackCount();
             if (DEBUG) {
                 Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
                         + ") for session Id=" + mSessionId + " and "
                         + callbackCount + " callbacks.");
             }
-            service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId);
+            service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId, token);
             mCallbacks.broadcast(
                     callback -> service.registerPredictionUpdatesLocked(mSessionId, callback));
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e1e3d0..2d803437 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -83,6 +83,7 @@
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
@@ -117,7 +118,6 @@
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
@@ -319,6 +319,7 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
@@ -4946,9 +4947,8 @@
                 notifyPackageUse(instr.mClass.getPackageName(),
                                  PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
             }
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
-                    + processName + " with config "
-                    + app.getWindowProcessController().getConfiguration());
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
+                    processName, app.getWindowProcessController().getConfiguration());
             ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
             app.compat = compatibilityInfoForPackage(appInfo);
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 3f949ba..653323d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -25,7 +25,7 @@
  * A helper class to build {@link HdmiCecMessage} from various cec commands.
  */
 public class HdmiCecMessageBuilder {
-    private static final int OSD_NAME_MAX_LENGTH = 13;
+    private static final int OSD_NAME_MAX_LENGTH = 14;
 
     private HdmiCecMessageBuilder() {}
 
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 05aa315..06105bf 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -314,6 +314,14 @@
         }
 
         @Override
+        public final <Listener> void onOperationFailure(ListenerOperation<Listener> operation,
+                Exception e) {
+            synchronized (mLock) {
+                super.onOperationFailure(operation, e);
+            }
+        }
+
+        @Override
         public final LocationRequest getRequest() {
             return mProviderLocationRequest;
         }
diff --git a/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
new file mode 100644
index 0000000..1500cfa
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 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 android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.UserHandle;
+
+/**
+ * An interface to wrap various difficult-to-intercept calls that services make to access / manage
+ * caller identity, e.g. {@link Binder#clearCallingIdentity()}.
+ */
+public interface CallerIdentityInjector {
+
+    /** A singleton for the real implementation of {@link CallerIdentityInjector}. */
+    CallerIdentityInjector REAL = new Real();
+
+    /** A {@link UserHandle#getCallingUserId()} call. */
+    @UserIdInt int getCallingUserId();
+
+    /** A {@link Binder#clearCallingIdentity()} call. */
+    long clearCallingIdentity();
+
+    /** A {@link Binder#restoreCallingIdentity(long)} ()} call. */
+    void restoreCallingIdentity(long token);
+
+    /** The real implementation of {@link CallerIdentityInjector}. */
+    class Real implements CallerIdentityInjector {
+
+        protected Real() {
+        }
+
+        @Override
+        public int getCallingUserId() {
+            return UserHandle.getCallingUserId();
+        }
+
+        @Override
+        public long clearCallingIdentity() {
+            return Binder.clearCallingIdentity();
+        }
+
+        @Override
+        public void restoreCallingIdentity(long token) {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
new file mode 100644
index 0000000..4c7b1f3
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 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;
+
+/**
+ * A listener used to receive notification that time zone configuration has changed.
+ */
+@FunctionalInterface
+public interface ConfigurationChangeListener {
+    /** Called when the current user or a configuration value has changed. */
+    void onChange();
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
new file mode 100644
index 0000000..aee3d8d
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2020 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 android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.timezonedetector.TimeZoneCapabilities;
+import android.app.timezonedetector.TimeZoneConfiguration;
+
+import java.util.Objects;
+
+/**
+ * Holds all configuration values that affect time zone behavior and some associated logic, e.g.
+ * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link
+ * #createCapabilities()}.
+ */
+public final class ConfigurationInternal {
+
+    private final @UserIdInt int mUserId;
+    private final boolean mUserConfigAllowed;
+    private final boolean mAutoDetectionSupported;
+    private final boolean mAutoDetectionEnabled;
+    private final boolean mLocationEnabled;
+    private final boolean mGeoDetectionEnabled;
+
+    private ConfigurationInternal(Builder builder) {
+        mUserId = builder.mUserId;
+        mUserConfigAllowed = builder.mUserConfigAllowed;
+        mAutoDetectionSupported = builder.mAutoDetectionSupported;
+        mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
+        mLocationEnabled = builder.mLocationEnabled;
+        mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
+    }
+
+    /** Returns the ID of the user this configuration is associated with. */
+    public @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
+    /** Returns true if the user allowed to modify time zone configuration. */
+    public boolean isUserConfigAllowed() {
+        return mUserConfigAllowed;
+    }
+
+    /** Returns true if the device supports some form of auto time zone detection. */
+    public boolean isAutoDetectionSupported() {
+        return mAutoDetectionSupported;
+    }
+
+    /** Returns the value of the auto time zone detection enabled setting. */
+    public boolean getAutoDetectionEnabledSetting() {
+        return mAutoDetectionEnabled;
+    }
+
+    /**
+     * Returns true if auto time zone detection behavior is actually enabled, which can be distinct
+     * from the raw setting value. */
+    public boolean getAutoDetectionEnabledBehavior() {
+        return mAutoDetectionSupported && mAutoDetectionEnabled;
+    }
+
+    /** Returns true if user's location can be used generally. */
+    public boolean isLocationEnabled() {
+        return mLocationEnabled;
+    }
+
+    /** Returns the value of the geolocation time zone detection enabled setting. */
+    public boolean getGeoDetectionEnabledSetting() {
+        return mGeoDetectionEnabled;
+    }
+
+    /**
+     * Returns true if geolocation time zone detection behavior is actually enabled, which can be
+     * distinct from the raw setting value.
+     */
+    public boolean getGeoDetectionEnabledBehavior() {
+        if (getAutoDetectionEnabledBehavior()) {
+            return mLocationEnabled && mGeoDetectionEnabled;
+        }
+        return false;
+    }
+
+    /** Creates a {@link TimeZoneCapabilities} object using the configuration values. */
+    public TimeZoneCapabilities createCapabilities() {
+        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder()
+                .setConfiguration(asConfiguration());
+
+        boolean allowConfigDateTime = isUserConfigAllowed();
+
+        // Automatic time zone detection is only supported on devices if there is a telephony
+        // network available or geolocation time zone detection is possible.
+        boolean deviceHasTimeZoneDetection = isAutoDetectionSupported();
+
+        final int configureAutoDetectionEnabledCapability;
+        if (!deviceHasTimeZoneDetection) {
+            configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
+        } else if (!allowConfigDateTime) {
+            configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
+        } else {
+            configureAutoDetectionEnabledCapability = CAPABILITY_POSSESSED;
+        }
+        builder.setConfigureAutoDetectionEnabled(configureAutoDetectionEnabledCapability);
+
+        final int configureGeolocationDetectionEnabledCapability;
+        if (!deviceHasTimeZoneDetection) {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
+        } else if (!allowConfigDateTime) {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
+        } else if (!isLocationEnabled()) {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE;
+        } else {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED;
+        }
+        builder.setConfigureGeoDetectionEnabled(configureGeolocationDetectionEnabledCapability);
+
+        // The ability to make manual time zone suggestions can also be restricted by policy. With
+        // the current logic above, this could lead to a situation where a device hardware does not
+        // support auto detection, the device has been forced into "auto" mode by an admin and the
+        // user is unable to disable auto detection.
+        final int suggestManualTimeZoneCapability;
+        if (!allowConfigDateTime) {
+            suggestManualTimeZoneCapability = CAPABILITY_NOT_ALLOWED;
+        } else if (getAutoDetectionEnabledBehavior()) {
+            suggestManualTimeZoneCapability = CAPABILITY_NOT_APPLICABLE;
+        } else {
+            suggestManualTimeZoneCapability = CAPABILITY_POSSESSED;
+        }
+        builder.setSuggestManualTimeZone(suggestManualTimeZoneCapability);
+
+        return builder.build();
+    }
+
+    /** Returns a {@link TimeZoneConfiguration} from the configuration values. */
+    public TimeZoneConfiguration asConfiguration() {
+        return new TimeZoneConfiguration.Builder(mUserId)
+                .setAutoDetectionEnabled(getAutoDetectionEnabledSetting())
+                .setGeoDetectionEnabled(getGeoDetectionEnabledSetting())
+                .build();
+    }
+
+    /**
+     * Merges the configuration values from this with any properties set in {@code
+     * newConfiguration}. The new configuration has precedence. Used to apply user updates to
+     * internal configuration.
+     */
+    public ConfigurationInternal merge(TimeZoneConfiguration newConfiguration) {
+        Builder builder = new Builder(this);
+        if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)) {
+            builder.setAutoDetectionEnabled(newConfiguration.isAutoDetectionEnabled());
+        }
+        if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED)) {
+            builder.setGeoDetectionEnabled(newConfiguration.isGeoDetectionEnabled());
+        }
+        return builder.build();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        ConfigurationInternal that = (ConfigurationInternal) o;
+        return mUserId == that.mUserId
+                && mUserConfigAllowed == that.mUserConfigAllowed
+                && mAutoDetectionSupported == that.mAutoDetectionSupported
+                && mAutoDetectionEnabled == that.mAutoDetectionEnabled
+                && mLocationEnabled == that.mLocationEnabled
+                && mGeoDetectionEnabled == that.mGeoDetectionEnabled;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported,
+                mAutoDetectionEnabled, mLocationEnabled, mGeoDetectionEnabled);
+    }
+
+    @Override
+    public String toString() {
+        return "TimeZoneDetectorConfiguration{"
+                + "mUserId=" + mUserId
+                + "mUserConfigAllowed=" + mUserConfigAllowed
+                + "mAutoDetectionSupported=" + mAutoDetectionSupported
+                + "mAutoDetectionEnabled=" + mAutoDetectionEnabled
+                + "mLocationEnabled=" + mLocationEnabled
+                + "mGeoDetectionEnabled=" + mGeoDetectionEnabled
+                + '}';
+    }
+
+    /**
+     * A Builder for {@link ConfigurationInternal}.
+     */
+    public static class Builder {
+
+        private final @UserIdInt int mUserId;
+        private boolean mUserConfigAllowed;
+        private boolean mAutoDetectionSupported;
+        private boolean mAutoDetectionEnabled;
+        private boolean mLocationEnabled;
+        private boolean mGeoDetectionEnabled;
+
+        /**
+         * Creates a new Builder with only the userId set.
+         */
+        public Builder(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Creates a new Builder by copying values from an existing instance.
+         */
+        public Builder(ConfigurationInternal toCopy) {
+            this.mUserId = toCopy.mUserId;
+            this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
+            this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
+            this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
+            this.mLocationEnabled = toCopy.mLocationEnabled;
+            this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled;
+        }
+
+        /**
+         * Sets whether the user is allowed to configure time zone settings on this device.
+         */
+        public Builder setUserConfigAllowed(boolean configAllowed) {
+            mUserConfigAllowed = configAllowed;
+            return this;
+        }
+
+        /**
+         * Sets whether automatic time zone detection is supported on this device.
+         */
+        public Builder setAutoDetectionSupported(boolean supported) {
+            mAutoDetectionSupported = supported;
+            return this;
+        }
+
+        /**
+         * Sets the value of the automatic time zone detection enabled setting for this device.
+         */
+        public Builder setAutoDetectionEnabled(boolean enabled) {
+            mAutoDetectionEnabled = enabled;
+            return this;
+        }
+
+        /**
+         * Sets the value of the location mode setting for this user.
+         */
+        public Builder setLocationEnabled(boolean enabled) {
+            mLocationEnabled = enabled;
+            return this;
+        }
+
+        /**
+         * Sets the value of the geolocation time zone detection setting for this user.
+         */
+        public Builder setGeoDetectionEnabled(boolean enabled) {
+            mGeoDetectionEnabled = enabled;
+            return this;
+        }
+
+        /** Returns a new {@link ConfigurationInternal}. */
+        @NonNull
+        public ConfigurationInternal build() {
+            return new ConfigurationInternal(this);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 0ca36e0..d640323 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -16,24 +16,30 @@
 
 package com.android.server.timezonedetector;
 
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.content.Intent.ACTION_USER_SWITCHED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.location.LocationManager;
 import android.net.ConnectivityManager;
+import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
 
 import java.util.Objects;
 
@@ -42,103 +48,87 @@
  */
 public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
 
+    private static final String LOG_TAG = "TimeZoneDetectorCallbackImpl";
     private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
-    private final Context mContext;
-    private final ContentResolver mCr;
-    private final UserManager mUserManager;
+    @NonNull private final Context mContext;
+    @NonNull private final Handler mHandler;
+    @NonNull private final ContentResolver mCr;
+    @NonNull private final UserManager mUserManager;
+    @NonNull private final boolean mGeoDetectionFeatureEnabled;
+    @NonNull private final LocationManager mLocationManager;
+    // @NonNull after setConfigChangeListener() is called.
+    private ConfigurationChangeListener mConfigChangeListener;
 
-    TimeZoneDetectorCallbackImpl(Context context) {
-        mContext = context;
+    TimeZoneDetectorCallbackImpl(@NonNull Context context, @NonNull Handler handler,
+            boolean geoDetectionFeatureEnabled) {
+        mContext = Objects.requireNonNull(context);
+        mHandler = Objects.requireNonNull(handler);
         mCr = context.getContentResolver();
         mUserManager = context.getSystemService(UserManager.class);
+        mLocationManager = context.getSystemService(LocationManager.class);
+        mGeoDetectionFeatureEnabled = geoDetectionFeatureEnabled;
+
+        // Wire up the change listener. All invocations are performed on the mHandler thread.
+
+        // Listen for the user changing / the user's location mode changing.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_USER_SWITCHED);
+        filter.addAction(LocationManager.MODE_CHANGED_ACTION);
+        mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                handleConfigChangeOnHandlerThread();
+            }
+        }, filter, null, mHandler);
+
+        // Add async callbacks for global settings being changed.
+        ContentResolver contentResolver = mContext.getContentResolver();
+        contentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
+                new ContentObserver(mHandler) {
+                    public void onChange(boolean selfChange) {
+                        handleConfigChangeOnHandlerThread();
+                    }
+                });
+
+        // Add async callbacks for user scoped location settings being changed.
+        contentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED),
+                true,
+                new ContentObserver(mHandler) {
+                    public void onChange(boolean selfChange) {
+                        handleConfigChangeOnHandlerThread();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
+    private void handleConfigChangeOnHandlerThread() {
+        if (mConfigChangeListener == null) {
+            Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+        }
+        mConfigChangeListener.onChange();
     }
 
     @Override
-    public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-        UserHandle userHandle = UserHandle.of(userId);
-        boolean disallowConfigDateTime =
-                mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
-
-        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userId);
-
-        // Automatic time zone detection is only supported (currently) on devices if there is a
-        // telephony network available.
-        if (!deviceHasTelephonyNetwork()) {
-            builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED);
-        } else if (disallowConfigDateTime) {
-            builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
-        } else {
-            builder.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED);
-        }
-
-        // TODO(b/149014708) Replace this with real logic when the settings storage is fully
-        // implemented.
-        builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED);
-
-        // The ability to make manual time zone suggestions can also be restricted by policy. With
-        // the current logic above, this could lead to a situation where a device hardware does not
-        // support auto detection, the device has been forced into "auto" mode by an admin and the
-        // user is unable to disable auto detection.
-        if (disallowConfigDateTime) {
-            builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
-        } else if (isAutoDetectionEnabled()) {
-            builder.setSuggestManualTimeZone(CAPABILITY_NOT_APPLICABLE);
-        } else {
-            builder.setSuggestManualTimeZone(CAPABILITY_POSSESSED);
-        }
-        return builder.build();
+    public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
+        mConfigChangeListener = Objects.requireNonNull(listener);
     }
 
     @Override
-    public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-        return new TimeZoneConfiguration.Builder()
+    public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
+        return new ConfigurationInternal.Builder(userId)
+                .setUserConfigAllowed(isUserConfigAllowed(userId))
+                .setAutoDetectionSupported(isAutoDetectionSupported())
                 .setAutoDetectionEnabled(isAutoDetectionEnabled())
-                .setGeoDetectionEnabled(isGeoDetectionEnabled())
+                .setLocationEnabled(isLocationEnabled(userId))
+                .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
                 .build();
     }
 
     @Override
-    public void setConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
-        Objects.requireNonNull(configuration);
-        if (!configuration.isComplete()) {
-            throw new IllegalArgumentException("configuration=" + configuration + " not complete");
-        }
-
-        // Avoid writing auto detection config for devices that do not support auto time zone
-        // detection: if we wrote it down then we'd set the default explicitly. That might influence
-        // what happens on later releases that do support auto detection on the same hardware.
-        if (isAutoDetectionSupported()) {
-            final int autoEnabledValue = configuration.isAutoDetectionEnabled() ? 1 : 0;
-            Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, autoEnabledValue);
-
-            final boolean geoTzDetectionEnabledValue = configuration.isGeoDetectionEnabled();
-            // TODO(b/149014708) Write this down to user-scoped settings once implemented.
-        }
-    }
-
-    @Override
-    public boolean isAutoDetectionEnabled() {
-        // To ensure that TimeZoneConfiguration is "complete" for simplicity, devices that do not
-        // support auto detection have safe, hard coded configuration values that make it look like
-        // auto detection is turned off. It is therefore important that false is returned from this
-        // method for devices that do not support auto time zone detection. Such devices will not
-        // have a UI to turn the auto detection on/off. Returning true could prevent the user
-        // entering information manually. On devices that do support auto time detection the default
-        // is to turn auto detection on.
-        if (isAutoDetectionSupported()) {
-            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isGeoDetectionEnabled() {
-        // TODO(b/149014708) Read this from user-scoped settings once implemented. The user's
-        //  location toggle will act as an override for this setting, i.e. so that the setting will
-        //  return false if the location toggle is disabled.
-        return false;
+    public @UserIdInt int getCurrentUserId() {
+        return LocalServices.getService(ActivityManagerInternal.class).getCurrentUserId();
     }
 
     @Override
@@ -165,8 +155,55 @@
         alarmManager.setTimeZone(zoneId);
     }
 
+    @Override
+    public void storeConfiguration(TimeZoneConfiguration configuration) {
+        Objects.requireNonNull(configuration);
+
+        // Avoid writing the auto detection enabled setting for devices that do not support auto
+        // time zone detection: if we wrote it down then we'd set the value explicitly, which would
+        // prevent detecting "default" later. That might influence what happens on later releases
+        // that support new types of auto detection on the same hardware.
+        if (isAutoDetectionSupported()) {
+            final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
+            setAutoDetectionEnabled(autoDetectionEnabled);
+
+            final int userId = configuration.getUserId();
+            final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
+            setGeoDetectionEnabled(userId, geoTzDetectionEnabled);
+        }
+    }
+
+    private boolean isUserConfigAllowed(@UserIdInt int userId) {
+        UserHandle userHandle = UserHandle.of(userId);
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
+    }
+
     private boolean isAutoDetectionSupported() {
-        return deviceHasTelephonyNetwork();
+        return deviceHasTelephonyNetwork() || mGeoDetectionFeatureEnabled;
+    }
+
+    private boolean isAutoDetectionEnabled() {
+        return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
+    }
+
+    private void setAutoDetectionEnabled(boolean enabled) {
+        Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, enabled ? 1 : 0);
+    }
+
+    private boolean isLocationEnabled(@UserIdInt int userId) {
+        return mLocationManager.isLocationEnabledForUser(UserHandle.of(userId));
+    }
+
+    private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
+        final boolean locationEnabled = isLocationEnabled(userId);
+        return Settings.Secure.getIntForUser(mCr,
+                Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
+                locationEnabled ? 1 : 0 /* defaultValue */, userId) != 0;
+    }
+
+    private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) {
+        Settings.Secure.putIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
+                enabled ? 1 : 0, userId);
     }
 
     private boolean deviceHasTelephonyNetwork() {
@@ -174,4 +211,4 @@
         return mContext.getSystemService(ConnectivityManager.class)
                 .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index fb7a73d..2d50390 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -27,6 +27,12 @@
  */
 public interface TimeZoneDetectorInternal extends Dumpable.Container {
 
+    /** Adds a listener that will be invoked when time zone detection configuration is changed. */
+    void addConfigurationListener(ConfigurationChangeListener listener);
+
+    /** Returns the {@link ConfigurationInternal} for the current user. */
+    ConfigurationInternal getCurrentUserConfigurationInternal();
+
     /**
      * Suggests the current time zone, determined using geolocation, to the detector. The
      * detector may ignore the signal based on system settings, whether better information is
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 15412a0..f0ce827 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 import android.os.Handler;
 
-import com.android.internal.annotations.VisibleForTesting;
-
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -34,18 +34,26 @@
     @NonNull private final Context mContext;
     @NonNull private final Handler mHandler;
     @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+    @NonNull private final List<ConfigurationChangeListener> mConfigurationListeners =
+            new ArrayList<>();
 
-    static TimeZoneDetectorInternalImpl create(@NonNull Context context, @NonNull Handler handler,
-            @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
-        return new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy);
-    }
-
-    @VisibleForTesting
     public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler,
             @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
         mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);
+
+        // Wire up a change listener so that any downstream listeners can be notified when
+        // the configuration changes for any reason.
+        mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged);
+    }
+
+    private void handleConfigurationChanged() {
+        synchronized (mConfigurationListeners) {
+            for (ConfigurationChangeListener listener : mConfigurationListeners) {
+                listener.onChange();
+            }
+        }
     }
 
     @Override
@@ -54,6 +62,19 @@
     }
 
     @Override
+    public void addConfigurationListener(ConfigurationChangeListener listener) {
+        synchronized (mConfigurationListeners) {
+            mConfigurationListeners.add(Objects.requireNonNull(listener));
+        }
+    }
+
+    @Override
+    @NonNull
+    public ConfigurationInternal getCurrentUserConfigurationInternal() {
+        return mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal();
+    }
+
+    @Override
     public void suggestGeolocationTimeZone(
             @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
         Objects.requireNonNull(timeZoneSuggestion);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index d81f949..7501d9f 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -24,32 +24,24 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
-import com.android.server.timezonedetector.TimeZoneDetectorStrategy.StrategyListener;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.Objects;
 
 /**
@@ -65,6 +57,9 @@
 
     private static final String TAG = "TimeZoneDetectorService";
 
+    /** A compile time switch for enabling / disabling geolocation-based time zone detection. */
+    private static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
+
     /**
      * Handles the service lifecycle for {@link TimeZoneDetectorService} and
      * {@link TimeZoneDetectorInternalImpl}.
@@ -80,18 +75,20 @@
             // Obtain / create the shared dependencies.
             Context context = getContext();
             Handler handler = FgThread.getHandler();
+
             TimeZoneDetectorStrategy timeZoneDetectorStrategy =
-                    TimeZoneDetectorStrategyImpl.create(context);
+                    TimeZoneDetectorStrategyImpl.create(
+                            context, handler, GEOLOCATION_TIME_ZONE_DETECTION_ENABLED);
 
             // Create and publish the local service for use by internal callers.
             TimeZoneDetectorInternal internal =
-                    TimeZoneDetectorInternalImpl.create(context, handler, timeZoneDetectorStrategy);
+                    new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy);
             publishLocalService(TimeZoneDetectorInternal.class, internal);
 
             // Publish the binder service so it can be accessed from other (appropriately
             // permissioned) processes.
-            TimeZoneDetectorService service =
-                    TimeZoneDetectorService.create(context, handler, timeZoneDetectorStrategy);
+            TimeZoneDetectorService service = TimeZoneDetectorService.create(
+                    context, handler, timeZoneDetectorStrategy);
             publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
         }
     }
@@ -103,52 +100,38 @@
     private final Handler mHandler;
 
     @NonNull
+    private final CallerIdentityInjector mCallerIdentityInjector;
+
+    @NonNull
     private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
 
-    /**
-     * This sparse array acts as a map from userId to listeners running as that userId. User scoped
-     * as time zone detection configuration is partially user-specific, so different users can
-     * get different configuration.
-     */
     @GuardedBy("mConfigurationListeners")
     @NonNull
-    private final SparseArray<ArrayList<ITimeZoneConfigurationListener>> mConfigurationListeners =
-            new SparseArray<>();
+    private final ArrayList<ITimeZoneConfigurationListener> mConfigurationListeners =
+            new ArrayList<>();
 
     private static TimeZoneDetectorService create(
             @NonNull Context context, @NonNull Handler handler,
             @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
 
-        TimeZoneDetectorService service =
-                new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
-
-        ContentResolver contentResolver = context.getContentResolver();
-        contentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
-                new ContentObserver(handler) {
-                    public void onChange(boolean selfChange) {
-                        service.handleAutoTimeZoneConfigChanged();
-                    }
-                });
-        // TODO(b/149014708) Listen for changes to geolocation time zone detection enabled config.
-        //  This should also include listening to the current user and the current user's location
-        //  toggle since the config is user-scoped and the location toggle overrides the geolocation
-        //  time zone enabled setting.
+        CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
+        TimeZoneDetectorService service = new TimeZoneDetectorService(
+                context, handler, callerIdentityInjector, timeZoneDetectorStrategy);
         return service;
     }
 
     @VisibleForTesting
     public TimeZoneDetectorService(@NonNull Context context, @NonNull Handler handler,
+            @NonNull CallerIdentityInjector callerIdentityInjector,
             @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
+        mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector);
         mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);
-        mTimeZoneDetectorStrategy.setStrategyListener(new StrategyListener() {
-            @Override
-            public void onConfigurationChanged() {
-                handleConfigurationChanged();
-            }
-        });
+
+        // Wire up a change listener so that ITimeZoneConfigurationListeners can be notified when
+        // the configuration changes for any reason.
+        mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged);
     }
 
     @Override
@@ -156,26 +139,12 @@
     public TimeZoneCapabilities getCapabilities() {
         enforceManageTimeZoneDetectorConfigurationPermission();
 
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
+        int userId = mCallerIdentityInjector.getCallingUserId();
+        long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
-            return mTimeZoneDetectorStrategy.getCapabilities(userId);
+            return mTimeZoneDetectorStrategy.getConfigurationInternal(userId).createCapabilities();
         } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    @Override
-    @NonNull
-    public TimeZoneConfiguration getConfiguration() {
-        enforceManageTimeZoneDetectorConfigurationPermission();
-
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
-        try {
-            return mTimeZoneDetectorStrategy.getConfiguration(userId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -184,12 +153,16 @@
         enforceManageTimeZoneDetectorConfigurationPermission();
         Objects.requireNonNull(configuration);
 
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
+        int callingUserId = mCallerIdentityInjector.getCallingUserId();
+        if (callingUserId != configuration.getUserId()) {
+            return false;
+        }
+
+        long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
-            return mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration);
+            return mTimeZoneDetectorStrategy.updateConfiguration(configuration);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -197,25 +170,17 @@
     public void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
         enforceManageTimeZoneDetectorConfigurationPermission();
         Objects.requireNonNull(listener);
-        int userId = UserHandle.getCallingUserId();
 
         synchronized (mConfigurationListeners) {
-            ArrayList<ITimeZoneConfigurationListener> listeners =
-                    mConfigurationListeners.get(userId);
-            if (listeners != null && listeners.contains(listener)) {
+            if (mConfigurationListeners.contains(listener)) {
                 return;
             }
             try {
-                if (listeners == null) {
-                    listeners = new ArrayList<>(1);
-                    mConfigurationListeners.put(userId, listeners);
-                }
-
                 // Ensure the reference to the listener will be removed if the client process dies.
                 listener.asBinder().linkToDeath(this, 0 /* flags */);
 
                 // Only add the listener if we can linkToDeath().
-                listeners.add(listener);
+                mConfigurationListeners.add(listener);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e);
             }
@@ -226,19 +191,16 @@
     public void removeConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
         enforceManageTimeZoneDetectorConfigurationPermission();
         Objects.requireNonNull(listener);
-        int userId = UserHandle.getCallingUserId();
 
         synchronized (mConfigurationListeners) {
             boolean removedListener = false;
-            ArrayList<ITimeZoneConfigurationListener> userListeners =
-                    mConfigurationListeners.get(userId);
-            if (userListeners.remove(listener)) {
+            if (mConfigurationListeners.remove(listener)) {
                 // Stop listening for the client process to die.
                 listener.asBinder().unlinkToDeath(this, 0 /* flags */);
                 removedListener = true;
             }
             if (!removedListener) {
-                Slog.w(TAG, "Client asked to remove listenener=" + listener
+                Slog.w(TAG, "Client asked to remove listener=" + listener
                         + ", but no listeners were removed."
                         + " mConfigurationListeners=" + mConfigurationListeners);
             }
@@ -259,19 +221,14 @@
     public void binderDied(IBinder who) {
         synchronized (mConfigurationListeners) {
             boolean removedListener = false;
-            final int userCount = mConfigurationListeners.size();
-            for (int i = 0; i < userCount; i++) {
-                ArrayList<ITimeZoneConfigurationListener> userListeners =
-                        mConfigurationListeners.valueAt(i);
-                Iterator<ITimeZoneConfigurationListener> userListenerIterator =
-                        userListeners.iterator();
-                while (userListenerIterator.hasNext()) {
-                    ITimeZoneConfigurationListener userListener = userListenerIterator.next();
-                    if (userListener.asBinder().equals(who)) {
-                        userListenerIterator.remove();
-                        removedListener = true;
-                        break;
-                    }
+            final int listenerCount = mConfigurationListeners.size();
+            for (int listenerIndex = listenerCount - 1; listenerIndex >= 0; listenerIndex--) {
+                ITimeZoneConfigurationListener listener =
+                        mConfigurationListeners.get(listenerIndex);
+                if (listener.asBinder().equals(who)) {
+                    mConfigurationListeners.remove(listenerIndex);
+                    removedListener = true;
+                    break;
                 }
             }
             if (!removedListener) {
@@ -283,42 +240,25 @@
     }
 
     void handleConfigurationChanged() {
-        // Note: we could trigger an async time zone detection operation here via a call to
-        // handleAutoTimeZoneConfigChanged(), but that is triggered in response to the underlying
-        // setting value changing so it is currently unnecessary. If we get to a point where all
-        // configuration changes are guaranteed to happen in response to an updateConfiguration()
-        // call, then we can remove that path and call it here instead.
-
         // Configuration has changed, but each user may have a different view of the configuration.
         // It's possible that this will cause unnecessary notifications but that shouldn't be a
         // problem.
         synchronized (mConfigurationListeners) {
-            final int userCount = mConfigurationListeners.size();
-            for (int userIndex = 0; userIndex < userCount; userIndex++) {
-                int userId = mConfigurationListeners.keyAt(userIndex);
-                TimeZoneConfiguration configuration =
-                        mTimeZoneDetectorStrategy.getConfiguration(userId);
-
-                ArrayList<ITimeZoneConfigurationListener> listeners =
-                        mConfigurationListeners.valueAt(userIndex);
-                final int listenerCount = listeners.size();
-                for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) {
-                    ITimeZoneConfigurationListener listener = listeners.get(listenerIndex);
-                    try {
-                        listener.onChange(configuration);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Unable to notify listener=" + listener
-                                + " for userId=" + userId
-                                + " of updated configuration=" + configuration, e);
-                    }
+            final int listenerCount = mConfigurationListeners.size();
+            for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) {
+                ITimeZoneConfigurationListener listener =
+                        mConfigurationListeners.get(listenerIndex);
+                try {
+                    listener.onChange();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to notify listener=" + listener, e);
                 }
             }
         }
     }
 
     /** Provided for command-line access. This is not exposed as a binder API. */
-    void suggestGeolocationTimeZone(
-            @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
+    void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
         enforceSuggestGeolocationTimeZonePermission();
         Objects.requireNonNull(timeZoneSuggestion);
 
@@ -331,12 +271,12 @@
         enforceSuggestManualTimeZonePermission();
         Objects.requireNonNull(timeZoneSuggestion);
 
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
+        int userId = mCallerIdentityInjector.getCallingUserId();
+        long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -358,12 +298,6 @@
         ipw.flush();
     }
 
-    /** Internal method for handling the auto time zone configuration being changed. */
-    @VisibleForTesting
-    public void handleAutoTimeZoneConfigChanged() {
-        mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneConfigChanged);
-    }
-
     private void enforceManageTimeZoneDetectorConfigurationPermission() {
         // TODO Switch to a dedicated MANAGE_TIME_AND_ZONE_CONFIGURATION permission.
         mContext.enforceCallingPermission(
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index c5b7e39..f944c56 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -19,51 +19,84 @@
 import android.annotation.UserIdInt;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.util.IndentingPrintWriter;
 
 /**
- * The interface for the class that implements the time detection algorithm used by the
- * {@link TimeZoneDetectorService}.
+ * The interface for the class that is responsible for setting the time zone on a device, used by
+ * {@link TimeZoneDetectorService} and {@link TimeZoneDetectorInternal}.
  *
- * <p>The strategy uses suggestions to decide whether to modify the device's time zone setting
- * and what to set it to.
+ * <p>The strategy receives suggestions, which it may use to modify the device's time zone setting.
+ * Suggestions are acted on or ignored as needed, depending on previously received suggestions and
+ * the current user's configuration (see {@link ConfigurationInternal}).
  *
- * <p>Most calls will be handled by a single thread, but that is not true for all calls. For example
- * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread concurrently
- * with other operations so implementations must still handle thread safety.
+ * <p>Devices can have zero, one or two automatic time zone detection algorithm available at any
+ * point in time.
+ *
+ * <p>The two automatic detection algorithms supported are "telephony" and "geolocation". Algorithm
+ * availability and use depends on several factors:
+ * <ul>
+ * <li>Telephony is only available on devices with a telephony stack.
+ * <li>Geolocation is also optional and configured at image creation time. When enabled on a
+ * device, its availability depends on the current user's settings, so switching between users can
+ * change the automatic algorithm used by the device.</li>
+ * </ul>
+ *
+ * <p>If there are no automatic time zone detections algorithms available then the user can usually
+ * change the device time zone manually. Under most circumstances the current user can turn
+ * automatic time zone detection on or off, or choose the algorithm via settings.
+ *
+ * <p>Telephony detection is independent of the current user. The device keeps track of the most
+ * recent telephony suggestion from each slotIndex. When telephony detection is in use, the highest
+ * scoring suggestion is used to set the device time zone based on a scoring algorithm. If several
+ * slotIndexes provide the same score then the slotIndex with the lowest numeric value "wins". If
+ * the situation changes and it is no longer possible to be confident about the time zone,
+ * slotIndexes must have an empty suggestion submitted in order to "withdraw" their previous
+ * suggestion otherwise it will remain in use.
+ *
+ * <p>Geolocation detection is dependent on the current user and their settings. The device retains
+ * at most one geolocation suggestion. Generally, use of a device's location is dependent on the
+ * user's "location toggle", but even when that is enabled the user may choose to enable / disable
+ * the use of geolocation for device time zone detection. If the current user changes to one that
+ * does not have geolocation detection enabled, or the user turns off geolocation detection, then
+ * the strategy discards the latest geolocation suggestion. Devices that lose a location fix must
+ * have an empty suggestion submitted in order to "withdraw" their previous suggestion otherwise it
+ * will remain in use.
+ *
+ * <p>Threading:
+ *
+ * <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
+ * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug
+ * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread
+ * concurrently with other operations.
  *
  * @hide
  */
 public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
 
-    /** A listener for strategy events. */
-    interface StrategyListener {
-        /**
-         * Invoked when configuration has been changed.
-         */
-        void onConfigurationChanged();
-    }
+    /**
+     * Sets a listener that will be triggered whenever time zone detection configuration is
+     * changed.
+     */
+    void addConfigChangeListener(@NonNull ConfigurationChangeListener listener);
 
-    /** Sets the listener that enables the strategy to communicate with the surrounding service. */
-    void setStrategyListener(@NonNull StrategyListener listener);
-
-    /** Returns the user's time zone capabilities. */
+    /** Returns the user's time zone configuration. */
     @NonNull
-    TimeZoneCapabilities getCapabilities(@UserIdInt int userId);
+    ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
 
     /**
-     * Returns the configuration that controls time zone detector behavior.
+     * Returns the configuration that controls time zone detector behavior for the current user.
      */
     @NonNull
-    TimeZoneConfiguration getConfiguration(@UserIdInt int userId);
+    ConfigurationInternal getCurrentUserConfigurationInternal();
 
     /**
-     * Updates the configuration settings that control time zone detector behavior.
+     * Updates the configuration properties that control a device's time zone behavior.
+     *
+     * <p>This method returns {@code true} if the configuration was changed,
+     * {@code false} otherwise.
      */
-    boolean updateConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration);
+    boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration);
 
     /**
      * Suggests zero, one or more time zones for the device, or withdraws a previous suggestion if
@@ -85,9 +118,4 @@
      * suggestion.
      */
     void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
-
-    /**
-     * Called when there has been a change to the automatic time zone detection configuration.
-     */
-    void handleAutoTimeZoneConfigChanged();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index d1369a2..8a42b18 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -20,10 +20,7 @@
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
 import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
-import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_AUTO_DETECTION_ENABLED;
-import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_GEO_DETECTION_ENABLED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,6 +30,7 @@
 import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.content.Context;
+import android.os.Handler;
 import android.util.IndentingPrintWriter;
 import android.util.LocalLog;
 import android.util.Slog;
@@ -45,15 +43,7 @@
 import java.util.Objects;
 
 /**
- * An implementation of {@link TimeZoneDetectorStrategy} that handle telephony and manual
- * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time
- * zone detection" setting.
- *
- * <p>For automatic detection, it keeps track of the most recent telephony suggestion from each
- * slotIndex and it uses the best suggestion based on a scoring algorithm. If several slotIndexes
- * provide the same score then the slotIndex with the lowest numeric value "wins". If the situation
- * changes and it is no longer possible to be confident about the time zone, slotIndexes must have
- * an empty suggestion submitted in order to "withdraw" their previous suggestion.
+ * The real implementation of {@link TimeZoneDetectorStrategy}.
  *
  * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
  */
@@ -61,48 +51,27 @@
 
     /**
      * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device configuration / settings
-     * / system properties. It can be faked for testing different scenarios.
+     * / system properties. It can be faked for testing.
      *
      * <p>Note: Because the settings / system properties-derived values can currently be modified
-     * independently and from different threads (and processes!), their use are prone to race
-     * conditions. That will be true until the responsibility for setting their values is moved to
-     * {@link TimeZoneDetectorStrategyImpl} (which is thread safe).
+     * independently and from different threads (and processes!), their use is prone to race
+     * conditions.
      */
     @VisibleForTesting
     public interface Callback {
 
         /**
-         * Returns the capabilities for the user.
+         * Sets a {@link ConfigurationChangeListener} that will be invoked when there are any
+         * changes that could affect time zone detection. This is invoked during system server
+         * setup.
          */
-        @NonNull
-        TimeZoneCapabilities getCapabilities(@UserIdInt int userId);
+        void setConfigChangeListener(@NonNull ConfigurationChangeListener listener);
 
-        /**
-         * Returns the configuration for the user.
-         * @param userId
-         */
-        @NonNull
-        TimeZoneConfiguration getConfiguration(int userId);
+        /** Returns the current user at the instant it is called. */
+        @UserIdInt int getCurrentUserId();
 
-        /**
-         * Sets the configuration for the user. This method handles storage only, the configuration
-         * must have been validated by the caller and be complete.
-         *
-         * @throws IllegalArgumentException if {@link TimeZoneConfiguration#isComplete()}
-         *     returns {@code false}
-         */
-        void setConfiguration(@UserIdInt int userId, @NonNull TimeZoneConfiguration configuration);
-
-        /**
-         * Returns true if automatic time zone detection is currently enabled.
-         */
-        boolean isAutoDetectionEnabled();
-
-        /**
-         * Returns whether geolocation can be used for time zone detection when {@link
-         * #isAutoDetectionEnabled()} returns {@code true}.
-         */
-        boolean isGeoDetectionEnabled();
+        /** Returns the {@link ConfigurationInternal} for the specified user. */
+        ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
 
         /**
          * Returns true if the device has had an explicit time zone set.
@@ -118,6 +87,13 @@
          * Sets the device's time zone.
          */
         void setDeviceTimeZone(@NonNull String zoneId);
+
+        /**
+         * Stores the configuration properties contained in {@code newConfiguration}.
+         * All checks about user capabilities must be done by the caller and
+         * {@link TimeZoneConfiguration#isComplete()} must be {@code true}.
+         */
+        void storeConfiguration(TimeZoneConfiguration newConfiguration);
     }
 
     private static final String LOG_TAG = "TimeZoneDetectorStrategy";
@@ -189,9 +165,9 @@
     @NonNull
     private final Callback mCallback;
 
-    /** Non-null after {@link #setStrategyListener(StrategyListener)} is called. */
-    @Nullable
-    private StrategyListener mListener;
+    @GuardedBy("this")
+    @NonNull
+    private List<ConfigurationChangeListener> mConfigChangeListeners = new ArrayList<>();
 
     /**
      * A log that records the decisions / decision metadata that affected the device's time zone.
@@ -211,7 +187,8 @@
             new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     /**
-     * The latest geolocation suggestion received.
+     * The latest geolocation suggestion received. If the user disabled geolocation time zone
+     * detection then the latest suggestion is cleared.
      */
     @GuardedBy("this")
     private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
@@ -223,113 +200,120 @@
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
      */
-    public static TimeZoneDetectorStrategyImpl create(Context context) {
-        Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
-        return new TimeZoneDetectorStrategyImpl(timeZoneDetectionServiceHelper);
+    public static TimeZoneDetectorStrategyImpl create(
+            @NonNull Context context, @NonNull Handler handler,
+            boolean geolocationTimeZoneDetectionEnabled) {
+
+        TimeZoneDetectorCallbackImpl callback = new TimeZoneDetectorCallbackImpl(
+                context, handler, geolocationTimeZoneDetectionEnabled);
+        return new TimeZoneDetectorStrategyImpl(callback);
     }
 
     @VisibleForTesting
-    public TimeZoneDetectorStrategyImpl(Callback callback) {
+    public TimeZoneDetectorStrategyImpl(@NonNull Callback callback) {
         mCallback = Objects.requireNonNull(callback);
+        mCallback.setConfigChangeListener(this::handleConfigChanged);
     }
 
     /**
-     * Sets a listener that allows the strategy to communicate with the surrounding service. This
-     * must be called before the instance is used and must only be called once.
+     * Adds a listener that allows the strategy to communicate with the surrounding service /
+     * internal. This must be called before the instance is used.
      */
     @Override
-    public synchronized void setStrategyListener(@NonNull StrategyListener listener) {
-        if (mListener != null) {
-            throw new IllegalStateException("Strategy already has a listener");
-        }
-        mListener = Objects.requireNonNull(listener);
+    public synchronized void addConfigChangeListener(
+            @NonNull ConfigurationChangeListener listener) {
+        Objects.requireNonNull(listener);
+        mConfigChangeListeners.add(listener);
     }
 
     @Override
     @NonNull
-    public synchronized TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-        return mCallback.getCapabilities(userId);
+    public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
+        return mCallback.getConfigurationInternal(userId);
     }
 
     @Override
     @NonNull
-    public synchronized TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-        return mCallback.getConfiguration(userId);
+    public synchronized ConfigurationInternal getCurrentUserConfigurationInternal() {
+        int currentUserId = mCallback.getCurrentUserId();
+        return getConfigurationInternal(currentUserId);
     }
 
     @Override
     public synchronized boolean updateConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configurationChanges) {
-        Objects.requireNonNull(configurationChanges);
+            @NonNull TimeZoneConfiguration requestedConfiguration) {
+        Objects.requireNonNull(requestedConfiguration);
 
-        // Validate the requested configuration changes before applying any of them.
-        TimeZoneCapabilities capabilities = mCallback.getCapabilities(userId);
-        boolean canManageTimeZoneDetection =
-                capabilities.getConfigureAutoDetectionEnabled() >= CAPABILITY_NOT_APPLICABLE;
-        if (!canManageTimeZoneDetection
-                && containsAutoTimeDetectionProperties(configurationChanges)) {
+        int userId = requestedConfiguration.getUserId();
+        TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities();
+
+        // Create a new configuration builder, and copy across the mutable properties users are
+        // able to modify. Other properties are therefore ignored.
+        final TimeZoneConfiguration newConfiguration =
+                capabilities.applyUpdate(requestedConfiguration);
+        if (newConfiguration == null) {
+            // The changes could not be made due to
             return false;
         }
 
-        // Create a complete configuration by merging the existing and new (possibly partial)
-        // configuration.
-        final TimeZoneConfiguration oldConfiguration = mCallback.getConfiguration(userId);
-        final TimeZoneConfiguration newConfiguration =
-                new TimeZoneConfiguration.Builder(oldConfiguration)
-                        .mergeProperties(configurationChanges)
-                        .build();
+        // Store the configuration / notify as needed. This will cause the mCallback to invoke
+        // handleConfigChanged() asynchronously.
+        mCallback.storeConfiguration(newConfiguration);
 
-        // Set the configuration / notify as needed.
-        boolean configurationChanged = !oldConfiguration.equals(newConfiguration);
-        if (configurationChanged) {
-            mCallback.setConfiguration(userId, newConfiguration);
-
-            String logMsg = "Configuration changed:"
-                    + "oldConfiguration=" + oldConfiguration
-                    + ", configuration=" + configurationChanges
-                    + ", newConfiguration=" + newConfiguration;
-            mTimeZoneChangesLog.log(logMsg);
-            if (DBG) {
-                Slog.d(LOG_TAG, logMsg);
-            }
-            mListener.onConfigurationChanged();
+        TimeZoneConfiguration oldConfiguration = capabilities.getConfiguration();
+        String logMsg = "Configuration changed:"
+                + " oldConfiguration=" + oldConfiguration
+                + ", newConfiguration=" + newConfiguration;
+        mTimeZoneChangesLog.log(logMsg);
+        if (DBG) {
+            Slog.d(LOG_TAG, logMsg);
         }
         return true;
     }
 
-    private static boolean containsAutoTimeDetectionProperties(
-            @NonNull TimeZoneConfiguration configuration) {
-        return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
-                || configuration.hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
-    }
-
     @Override
     public synchronized void suggestGeolocationTimeZone(
             @NonNull GeolocationTimeZoneSuggestion suggestion) {
+
+        int currentUserId = mCallback.getCurrentUserId();
+        ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
         if (DBG) {
-            Slog.d(LOG_TAG, "Geolocation suggestion received. newSuggestion=" + suggestion);
+            Slog.d(LOG_TAG, "Geolocation suggestion received."
+                    + " currentUserConfig=" + currentUserConfig
+                    + " newSuggestion=" + suggestion);
         }
-
         Objects.requireNonNull(suggestion);
-        mLatestGeoLocationSuggestion.set(suggestion);
 
-        // Now perform auto time zone detection. The new suggestion may be used to modify the time
-        // zone setting.
-        if (mCallback.isGeoDetectionEnabled()) {
+        if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
+            // Only store a geolocation suggestion if geolocation detection is currently enabled.
+            mLatestGeoLocationSuggestion.set(suggestion);
+
+            // Now perform auto time zone detection. The new suggestion may be used to modify the
+            // time zone setting.
             String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
-            doAutoTimeZoneDetection(reason);
+            doAutoTimeZoneDetection(currentUserConfig, reason);
         }
     }
 
     @Override
     public synchronized boolean suggestManualTimeZone(
             @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) {
+
+        int currentUserId = mCallback.getCurrentUserId();
+        if (userId != currentUserId) {
+            Slog.w(LOG_TAG, "Manual suggestion received but user != current user, userId=" + userId
+                    + " suggestion=" + suggestion);
+
+            // Only listen to changes from the current user.
+            return false;
+        }
+
         Objects.requireNonNull(suggestion);
 
         String timeZoneId = suggestion.getZoneId();
         String cause = "Manual time suggestion received: suggestion=" + suggestion;
 
-        TimeZoneCapabilities capabilities = mCallback.getCapabilities(userId);
+        TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities();
         if (capabilities.getSuggestManualTimeZone() != CAPABILITY_POSSESSED) {
             Slog.i(LOG_TAG, "User does not have the capability needed to set the time zone manually"
                     + ", capabilities=" + capabilities
@@ -345,8 +329,12 @@
     @Override
     public synchronized void suggestTelephonyTimeZone(
             @NonNull TelephonyTimeZoneSuggestion suggestion) {
+
+        int currentUserId = mCallback.getCurrentUserId();
+        ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
         if (DBG) {
-            Slog.d(LOG_TAG, "Telephony suggestion received. newSuggestion=" + suggestion);
+            Slog.d(LOG_TAG, "Telephony suggestion received. currentUserConfig=" + currentUserConfig
+                    + " newSuggestion=" + suggestion);
         }
         Objects.requireNonNull(suggestion);
 
@@ -360,9 +348,9 @@
 
         // Now perform auto time zone detection. The new suggestion may be used to modify the time
         // zone setting.
-        if (!mCallback.isGeoDetectionEnabled()) {
+        if (!currentUserConfig.getGeoDetectionEnabledBehavior()) {
             String reason = "New telephony time zone suggested. suggestion=" + suggestion;
-            doAutoTimeZoneDetection(reason);
+            doAutoTimeZoneDetection(currentUserConfig, reason);
         }
     }
 
@@ -392,15 +380,15 @@
      * Performs automatic time zone detection.
      */
     @GuardedBy("this")
-    private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
-        if (!mCallback.isAutoDetectionEnabled()) {
-            // Avoid doing unnecessary work with this (race-prone) check.
+    private void doAutoTimeZoneDetection(
+            @NonNull ConfigurationInternal currentUserConfig, @NonNull String detectionReason) {
+        if (!currentUserConfig.getAutoDetectionEnabledBehavior()) {
+            // Avoid doing unnecessary work.
             return;
         }
 
-        // Use the right suggestions based on the current configuration. This check is potentially
-        // race-prone until this value is set via a call to TimeZoneDetectorStrategy.
-        if (mCallback.isGeoDetectionEnabled()) {
+        // Use the right suggestions based on the current configuration.
+        if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
             doGeolocationTimeZoneDetection(detectionReason);
         } else  {
             doTelephonyTimeZoneDetection(detectionReason);
@@ -480,35 +468,18 @@
 
         // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
         // zone ID.
-        String newZoneId = bestTelephonySuggestion.suggestion.getZoneId();
-        if (newZoneId == null) {
+        String zoneId = bestTelephonySuggestion.suggestion.getZoneId();
+        if (zoneId == null) {
             Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
                     + " bestTelephonySuggestion=" + bestTelephonySuggestion
                     + " detectionReason=" + detectionReason);
             return;
         }
 
-        String zoneId = bestTelephonySuggestion.suggestion.getZoneId();
         String cause = "Found good suggestion."
                 + ", bestTelephonySuggestion=" + bestTelephonySuggestion
                 + ", detectionReason=" + detectionReason;
-        setAutoDeviceTimeZoneIfRequired(zoneId, cause);
-    }
-
-    @GuardedBy("this")
-    private void setAutoDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) {
-        Objects.requireNonNull(newZoneId);
-        Objects.requireNonNull(cause);
-
-        if (!mCallback.isAutoDetectionEnabled()) {
-            if (DBG) {
-                Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
-                        + ", newZoneId=" + newZoneId
-                        + ", cause=" + cause);
-            }
-            return;
-        }
-        setDeviceTimeZoneIfRequired(newZoneId, cause);
+        setDeviceTimeZoneIfRequired(zoneId, cause);
     }
 
     @GuardedBy("this")
@@ -582,13 +553,39 @@
         return findBestTelephonySuggestion();
     }
 
-    @Override
-    public synchronized void handleAutoTimeZoneConfigChanged() {
+    private synchronized void handleConfigChanged() {
         if (DBG) {
-            Slog.d(LOG_TAG, "handleAutoTimeZoneConfigChanged()");
+            Slog.d(LOG_TAG, "handleConfigChanged()");
         }
 
-        doAutoTimeZoneDetection("handleAutoTimeZoneConfigChanged()");
+        clearGeolocationSuggestionIfNeeded();
+
+        for (ConfigurationChangeListener listener : mConfigChangeListeners) {
+            listener.onChange();
+        }
+    }
+
+    @GuardedBy("this")
+    private void clearGeolocationSuggestionIfNeeded() {
+        // This method is called whenever the user changes or the config for any user changes. We
+        // don't know what happened, so we capture the current user's config, check to see if we
+        // need to clear state associated with a previous user, and rerun detection.
+        int currentUserId = mCallback.getCurrentUserId();
+        ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+
+        GeolocationTimeZoneSuggestion latestGeoLocationSuggestion =
+                mLatestGeoLocationSuggestion.get();
+        if (latestGeoLocationSuggestion != null
+                && !currentUserConfig.getGeoDetectionEnabledBehavior()) {
+            // The current user's config has geodetection disabled, so clear the latest suggestion.
+            // This is done to ensure we only ever keep a geolocation suggestion if the user has
+            // said it is ok to do so.
+            mLatestGeoLocationSuggestion.set(null);
+            mTimeZoneChangesLog.log(
+                    "clearGeolocationSuggestionIfNeeded: Cleared latest Geolocation suggestion.");
+        }
+
+        doAutoTimeZoneDetection(currentUserConfig, "clearGeolocationSuggestionIfNeeded()");
     }
 
     @Override
@@ -604,11 +601,14 @@
         ipw.println("TimeZoneDetectorStrategy:");
 
         ipw.increaseIndent(); // level 1
-        ipw.println("mCallback.isAutoDetectionEnabled()=" + mCallback.isAutoDetectionEnabled());
+        int currentUserId = mCallback.getCurrentUserId();
+        ipw.println("mCallback.getCurrentUserId()=" + currentUserId);
+        ConfigurationInternal configuration = mCallback.getConfigurationInternal(currentUserId);
+        ipw.println("mCallback.getConfiguration(currentUserId)=" + configuration);
+        ipw.println("[Capabilities=" + configuration.createCapabilities() + "]");
         ipw.println("mCallback.isDeviceTimeZoneInitialized()="
                 + mCallback.isDeviceTimeZoneInitialized());
         ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone());
-        ipw.println("mCallback.isGeoDetectionEnabled()=" + mCallback.isGeoDetectionEnabled());
 
         ipw.println("Time zone change log:");
         ipw.increaseIndent(); // level 2
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 964de13..56261c4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -110,9 +110,13 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTAINERS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
@@ -145,9 +149,6 @@
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
@@ -1096,15 +1097,15 @@
 
     private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
         if (!attachedToProcess()) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
-                    "Can't report activity moved to display - client not running, activityRecord="
-                            + this + ", displayId=" + displayId);
+            ProtoLog.w(WM_DEBUG_SWITCH, "Can't report activity moved "
+                    + "to display - client not running, activityRecord=%s, displayId=%d",
+                    this, displayId);
             return;
         }
         try {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Reporting activity moved to display" + ", activityRecord=" + this
-                            + ", displayId=" + displayId + ", config=" + config);
+            ProtoLog.v(WM_DEBUG_SWITCH, "Reporting activity moved to "
+                    + "display, activityRecord=%s, displayId=%d, config=%s", this, displayId,
+                    config);
 
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     MoveToDisplayItem.obtain(displayId, config));
@@ -1115,14 +1116,13 @@
 
     private void scheduleConfigurationChanged(Configuration config) {
         if (!attachedToProcess()) {
-            if (DEBUG_CONFIGURATION) Slog.w(TAG,
-                    "Can't report activity configuration update - client not running"
-                            + ", activityRecord=" + this);
+            ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration "
+                    + "update - client not running, activityRecord=%s", this);
             return;
         }
         try {
-            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
-                    + config);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
+                    + "config: %s", this, config);
 
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     ActivityConfigurationChangeItem.obtain(config));
@@ -1949,10 +1949,8 @@
             startingWindow = null;
             startingDisplayed = false;
             if (surface == null) {
-                ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
-                        "startingWindow was set but startingSurface==null, couldn't "
-                                + "remove");
-
+                ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "startingWindow was set but "
+                        + "startingSurface==null, couldn't remove");
                 return;
             }
         } else {
@@ -1962,9 +1960,10 @@
             return;
         }
 
+
         ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
-                        + " startingView=%s Callers=%s",
-                this, startingWindow, startingSurface, Debug.getCallers(5));
+                + " startingView=%s Callers=%s", this, startingWindow, startingSurface,
+                Debug.getCallers(5));
 
 
         // Use the same thread to remove the window as we used to add it, as otherwise we end up
@@ -2399,9 +2398,8 @@
      */
     boolean moveFocusableActivityToTop(String reason) {
         if (!isFocusable()) {
-            if (DEBUG_FOCUS) {
-                Slog.d(TAG_FOCUS, "moveActivityStackToFront: unfocusable activity=" + this);
-            }
+            ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: unfocusable "
+                    + "activity=%s", this);
             return false;
         }
 
@@ -2414,15 +2412,11 @@
 
         if (mRootWindowContainer.getTopResumedActivity() == this
                 && getDisplayContent().mFocusedApp == this) {
-            if (DEBUG_FOCUS) {
-                Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
-            }
+            ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: already on top, "
+                    + "activity=%s", this);
             return !isState(RESUMED);
         }
-
-        if (DEBUG_FOCUS) {
-            Slog.d(TAG_FOCUS, "moveActivityStackToFront: activity=" + this);
-        }
+        ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: activity=%s", this);
 
         stack.moveToFront(reason, task);
         // Report top activity change to tracking services and WM
@@ -2798,10 +2792,8 @@
             mRootWindowContainer.resumeFocusedStacksTopActivities();
         }
 
-        if (DEBUG_CONTAINERS) {
-            Slog.d(TAG_CONTAINERS, "destroyIfPossible: r=" + this + " destroy returned removed="
-                    + activityRemoved);
-        }
+        ProtoLog.d(WM_DEBUG_CONTAINERS, "destroyIfPossible: r=%s destroy returned "
+                + "removed=%s", this, activityRemoved);
 
         return activityRemoved;
     }
@@ -2935,10 +2927,9 @@
         finishActivityResults(Activity.RESULT_CANCELED,
                 null /* resultData */, null /* resultGrants */);
         makeFinishingLocked();
-        if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
-            Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack, reason="
-                    + reason + ", callers=" + Debug.getCallers(5));
-        }
+
+        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s from stack, reason= %s "
+                        + "callers=%s", this, reason, Debug.getCallers(5));
 
         takeFromHistory();
         removeTimeouts();
@@ -2978,7 +2969,7 @@
     void destroyed(String reason) {
         removeDestroyTimeout();
 
-        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + this);
+        ProtoLog.d(WM_DEBUG_CONTAINERS, "activityDestroyedLocked: r=%s", this);
 
         if (!isState(DESTROYING, DESTROYED)) {
             throw new IllegalStateException(
@@ -3179,12 +3170,9 @@
             remove = false;
         }
         if (remove) {
-            if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) {
-                Slog.i(TAG_ADD_REMOVE, "Removing activity " + this
-                        + " hasSavedState=" + mHaveState + " stateNotNeeded=" + stateNotNeeded
-                        + " finishing=" + finishing + " state=" + mState
-                        + " callers=" + Debug.getCallers(5));
-            }
+            ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s hasSavedState=%b "
+                    + "stateNotNeeded=%s finishing=%b state=%s callers=%s", this,
+                    mHaveState, stateNotNeeded, finishing, mState, Debug.getCallers(5));
             if (!finishing || (app != null && app.isRemoved())) {
                 Slog.w(TAG, "Force removing " + this + ": app died, no saved state");
                 EventLogTags.writeWmFinishActivity(mUserId, System.identityHashCode(this),
@@ -7007,27 +6995,27 @@
             boolean ignoreVisibility) {
         final Task stack = getRootTask();
         if (stack.mConfigWillChange) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check (will change): " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
+                    + "(will change): %s", this);
             return true;
         }
 
         // We don't worry about activities that are finishing.
         if (finishing) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration doesn't matter in finishing " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter "
+                    + "in finishing %s", this);
             stopFreezingScreenLocked(false);
             return true;
         }
 
         if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check invisible: " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
+                    + "invisible: %s", this);
             return true;
         }
 
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Ensuring correct configuration: " + this);
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Ensuring correct "
+                + "configuration: %s", this);
 
         final int newDisplayId = getDisplayId();
         final boolean displayChanged = mLastReportedDisplayId != newDisplayId;
@@ -7043,8 +7031,8 @@
         // the combine configurations are equal, but would otherwise differ in the override config
         mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
         if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration & display unchanged in " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+                    + "unchanged in %s", this);
             return true;
         }
 
@@ -7064,14 +7052,14 @@
             // No need to relaunch or schedule new config for activity that hasn't been launched
             // yet. We do, however, return after applying the config to activity record, so that
             // it will use it for launch transaction.
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check for initializing activity: " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check for "
+                    + "initializing activity: %s", this);
             return true;
         }
 
         if (changes == 0 && !forceNewConfig) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration no differences in " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s",
+                    this);
             // There are no significant differences, so we won't relaunch but should still deliver
             // the new configuration to the client process.
             if (displayChanged) {
@@ -7082,26 +7070,23 @@
             return true;
         }
 
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Configuration changes for " + this + ", allChanges="
-                        + Configuration.configurationDiffToString(changes));
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration changes for %s, "
+                + "allChanges=%s", this, Configuration.configurationDiffToString(changes));
 
         // If the activity isn't currently running, just leave the new configuration and it will
         // pick that up next time it starts.
         if (!attachedToProcess()) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration doesn't matter not running " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter not running %s", this);
             stopFreezingScreenLocked(false);
             forceNewConfig = false;
             return true;
         }
 
         // Figure out how to handle the changes between the configurations.
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Checking to restart " + info.name + ": changed=0x"
-                        + Integer.toHexString(changes) + ", handles=0x"
-                        + Integer.toHexString(info.getRealConfigChanged())
-                        + ", mLastReportedConfiguration=" + mLastReportedConfiguration);
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Checking to restart %s: changed=0x%s, "
+                + "handles=0x%s, mLastReportedConfiguration=%s", info.name,
+                Integer.toHexString(changes), Integer.toHexString(info.getRealConfigChanged()),
+                mLastReportedConfiguration);
 
         if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
             // Aha, the activity isn't handling the change, so DIE DIE DIE.
@@ -7118,20 +7103,20 @@
                 mRelaunchReason = RELAUNCH_REASON_NONE;
             }
             if (!attachedToProcess()) {
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is destroying non-running " + this);
+                ProtoLog.v(WM_DEBUG_CONFIGURATION,
+                        "Config is destroying non-running %s", this);
                 destroyImmediately("config");
             } else if (mState == PAUSING) {
                 // A little annoying: we are waiting for this activity to finish pausing. Let's not
                 // do anything now, but just flag that it needs to be restarted when done pausing.
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is skipping already pausing " + this);
+                ProtoLog.v(WM_DEBUG_CONFIGURATION,
+                        "Config is skipping already pausing %s", this);
                 deferRelaunchUntilPaused = true;
                 preserveWindowOnDeferredRelaunch = preserveWindow;
                 return true;
             } else {
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is relaunching " + this);
+                ProtoLog.v(WM_DEBUG_CONFIGURATION, "Config is relaunching %s",
+                        this);
                 if (DEBUG_STATES && !mVisibleRequested) {
                     Slog.v(TAG_STATES, "Config is relaunching invisible activity " + this
                             + " called by " + Debug.getCallers(4));
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4c93b9e..be7a6ae 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -56,12 +56,12 @@
 import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
@@ -116,6 +116,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
 import com.android.server.power.ShutdownCheckPoints;
@@ -669,10 +670,8 @@
                 if (stack != null) {
                     stack.mConfigWillChange = globalConfigWillChange;
                 }
-                if (DEBUG_CONFIGURATION) {
-                    Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = "
-                            + globalConfigWillChange);
-                }
+                ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "
+                        + "will change = %b", globalConfigWillChange);
 
                 final long origId = Binder.clearCallingIdentity();
 
@@ -695,10 +694,9 @@
                     if (stack != null) {
                         stack.mConfigWillChange = false;
                     }
-                    if (DEBUG_CONFIGURATION) {
-                        Slog.v(TAG_CONFIGURATION,
+                    ProtoLog.v(WM_DEBUG_CONFIGURATION,
                                 "Updating to new configuration after starting activity.");
-                    }
+
                     mService.updateConfigurationLocked(mRequest.globalConfig, null, false);
                 }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
index da0bfd6..3c562a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
@@ -43,12 +43,6 @@
     // Enable all debug log categories for activities.
     private static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
 
-    static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
-    public static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
-    static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_FOCUS = false;
-    static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
-    static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
     static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
     static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
     static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2adaa52..6a8cbfb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -66,6 +66,10 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_NONE;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
@@ -92,10 +96,6 @@
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
@@ -242,6 +242,7 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
@@ -813,7 +814,7 @@
             // in-place.
             updateConfigurationLocked(configuration, null, true);
             final Configuration globalConfig = getGlobalConfiguration();
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Initial config: %s", globalConfig);
 
             // Load resources only after the current configuration has been set.
             final Resources res = mContext.getResources();
@@ -1960,7 +1961,7 @@
 
             // update associated state if we're frontmost
             if (r.isFocusedActivityOnDisplay()) {
-                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
+                ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r);
                 applyUpdateLockStateLocked(r);
             }
         }
@@ -1974,8 +1975,8 @@
         final boolean nextState = r != null && r.immersive;
         mH.post(() -> {
             if (mUpdateLock.isHeld() != nextState) {
-                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
-                        "Applying new update lock state '" + nextState + "' for " + r);
+                ProtoLog.d(WM_DEBUG_IMMERSIVE, "Applying new update lock state '%s' for %s",
+                        nextState, r);
                 if (nextState) {
                     mUpdateLock.acquire();
                 } else {
@@ -2176,7 +2177,7 @@
     @Override
     public void setFocusedStack(int stackId) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
+        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedStack: stackId=%d", stackId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -2198,7 +2199,7 @@
     @Override
     public void setFocusedTask(int taskId) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
+        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d", taskId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -3013,7 +3014,7 @@
     }
 
     private void startLockTaskModeLocked(@Nullable Task task, boolean isSystemCaller) {
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
+        ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskModeLocked: %s", task);
         if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
             return;
         }
@@ -3075,8 +3076,7 @@
                     "updateLockTaskPackages()");
         }
         synchronized (mGlobalLock) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":"
-                    + Arrays.toString(packages));
+            ProtoLog.w(WM_DEBUG_LOCKTASK, "Allowlisting %d:%s", userId, Arrays.toString(packages));
             getLockTaskController().updateLockTaskPackages(userId, packages);
         }
     }
@@ -4001,9 +4001,9 @@
     @Override
     public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
             int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
-        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
-                + Arrays.toString(horizontalSizeConfiguration) + " "
-                + Arrays.toString(verticalSizeConfigurations));
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s",
+                token, Arrays.toString(horizontalSizeConfiguration),
+                Arrays.toString(verticalSizeConfigurations));
         synchronized (mGlobalLock) {
             ActivityRecord record = ActivityRecord.isInStackLocked(token);
             if (record == null) {
@@ -4497,8 +4497,8 @@
                     "updateLockTaskFeatures()");
         }
         synchronized (mGlobalLock) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
-                    Integer.toHexString(flags));
+            ProtoLog.w(WM_DEBUG_LOCKTASK, "Allowing features %d:0x%s",
+                    userId, Integer.toHexString(flags));
             getLockTaskController().updateLockTaskFeatures(userId, flags);
         }
     }
@@ -5183,8 +5183,8 @@
             return 0;
         }
 
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
-                "Updating global configuration to: " + values);
+        ProtoLog.i(WM_DEBUG_CONFIGURATION, "Updating global configuration "
+                + "to: %s", values);
         writeConfigurationChanged(changes);
         FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_CONFIGURATION_CHANGED,
                 values.colorMode,
@@ -5262,10 +5262,8 @@
         for (int i = pidMap.size() - 1; i >= 0; i--) {
             final int pid = pidMap.keyAt(i);
             final WindowProcessController app = pidMap.get(pid);
-            if (DEBUG_CONFIGURATION) {
-                Slog.v(TAG_CONFIGURATION, "Update process config of "
-                        + app.mName + " to new config " + configCopy);
-            }
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
+                    + "config %s", app.mName, configCopy);
             app.onConfigurationChanged(configCopy);
         }
 
@@ -6563,10 +6561,8 @@
             if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return;
 
             if (pid == MY_PID || pid < 0) {
-                if (DEBUG_CONFIGURATION) {
-                    Slog.w(TAG,
+                ProtoLog.w(WM_DEBUG_CONFIGURATION,
                             "Trying to update display configuration for system/invalid process.");
-                }
                 return;
             }
             synchronized (mGlobalLock) {
@@ -6574,18 +6570,14 @@
                         mRootWindowContainer.getDisplayContent(displayId);
                 if (displayContent == null) {
                     // Call might come when display is not yet added or has been removed.
-                    if (DEBUG_CONFIGURATION) {
-                        Slog.w(TAG, "Trying to update display configuration for non-existing "
-                                + "displayId=" + displayId);
-                    }
+                    ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display "
+                            + "configuration for non-existing displayId=%d", displayId);
                     return;
                 }
                 final WindowProcessController process = mProcessMap.getProcess(pid);
                 if (process == null) {
-                    if (DEBUG_CONFIGURATION) {
-                        Slog.w(TAG, "Trying to update display configuration for invalid "
-                                + "process, pid=" + pid);
-                    }
+                    ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display "
+                            + "configuration for invalid process, pid=%d", pid);
                     return;
                 }
                 process.registerDisplayConfigurationListener(displayContent);
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 167afab..7e55f0a 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,6 +37,7 @@
 import android.util.SparseArray;
 import android.util.Xml;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -333,8 +334,8 @@
                 }
                 try {
                     if (app.hasThread()) {
-                        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
-                                + app.mName + " new compat " + ci);
+                        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s "
+                                + "new compat %s", app.mName, ci);
                         app.getThread().updatePackageCompatibilityInfo(packageName, ci);
                     }
                 } catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 8ef57f7..c8d7693 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -29,7 +29,7 @@
 import static android.os.UserHandle.USER_CURRENT;
 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -65,6 +65,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
@@ -448,7 +449,7 @@
      * unlike {@link #stopLockTaskMode(Task, boolean, int)}, it doesn't perform the checks.
      */
     void clearLockedTasks(String reason) {
-        if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason);
+        ProtoLog.i(WM_DEBUG_LOCKTASK, "clearLockedTasks: %s", reason);
         if (!mLockTaskModeTasks.isEmpty()) {
             clearLockedTask(mLockTaskModeTasks.get(0));
         }
@@ -490,10 +491,10 @@
         if (!mLockTaskModeTasks.remove(task)) {
             return;
         }
-        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task);
+        ProtoLog.d(WM_DEBUG_LOCKTASK, "removeLockedTask: removed %s", task);
         if (mLockTaskModeTasks.isEmpty()) {
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
-                    " last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
+            ProtoLog.d(WM_DEBUG_LOCKTASK, "removeLockedTask: task=%s last task, "
+                    + "reverting locktask mode. Callers=%s", task, Debug.getCallers(3));
             mHandler.post(() -> performStopLockTask(task.mUserId));
         }
     }
@@ -558,7 +559,7 @@
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
                 // startLockTask() called by app, but app is not part of lock task allowlist. Show
                 // app pinning request. We will come back here with isSystemCaller true.
-                if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
+                ProtoLog.w(WM_DEBUG_LOCKTASK, "Mode default, asking user");
                 StatusBarManagerInternal statusBarManager = LocalServices.getService(
                         StatusBarManagerInternal.class);
                 if (statusBarManager != null) {
@@ -569,8 +570,7 @@
         }
 
         // System can only initiate screen pinning, not full lock task mode
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
-                isSystemCaller ? "Locking pinned" : "Locking fully");
+        ProtoLog.w(WM_DEBUG_LOCKTASK, "%s", isSystemCaller ? "Locking pinned" : "Locking fully");
         setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
                 "startLockTask", true);
     }
@@ -584,7 +584,7 @@
                                  String reason, boolean andResume) {
         // Should have already been checked, but do it again.
         if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+            ProtoLog.w(WM_DEBUG_LOCKTASK,
                     "setLockTaskMode: Can't lock due to auth");
             return;
         }
@@ -602,8 +602,8 @@
                     task.mUserId,
                     lockTaskModeState));
         }
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
-                " Callers=" + Debug.getCallers(4));
+        ProtoLog.w(WM_DEBUG_LOCKTASK, "setLockTaskMode: Locking to %s Callers=%s",
+                task, Debug.getCallers(4));
 
         if (!mLockTaskModeTasks.contains(task)) {
             mLockTaskModeTasks.add(task);
@@ -672,8 +672,8 @@
             }
 
             // Terminate locked tasks that have recently lost allowlist authorization.
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
-                    lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
+            ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: removing %s"
+                    + " mLockTaskAuth()=%s", lockedTask, lockedTask.lockTaskAuthToString());
             removeLockedTask(lockedTask);
             lockedTask.performClearTaskLocked();
             taskChanged = true;
@@ -686,8 +686,8 @@
         if (mLockTaskModeTasks.isEmpty() && task!= null
                 && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
             // This task must have just been authorized.
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
-                    "onLockTaskPackagesUpdated: starting new locktask task=" + task);
+            ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: starting new "
+                    + "locktask task=%s", task);
             setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false);
             taskChanged = true;
         }
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index cc5ed36..c3953b4 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -16,9 +16,8 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.wm.Task.TAG_ADD_REMOVE;
 import static com.android.server.wm.Task.TAG_TASKS;
 
 import android.app.ActivityOptions;
@@ -27,6 +26,7 @@
 import android.os.Debug;
 import android.util.Slog;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -218,8 +218,8 @@
             if (takeOptions) {
                 noOptions = takeOption(p, noOptions);
             }
-            if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing activity " + p + " from task="
-                    + mTask + " adding to task=" + targetTask + " Callers=" + Debug.getCallers(4));
+            ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s from task=%s "
+                    + "adding to task=%s Callers=%s", p, mTask,  targetTask, Debug.getCallers(4));
             if (DEBUG_TASKS) Slog.v(TAG_TASKS,
                     "Pushing next activity " + p + " out to target's task " + target);
             p.reparent(targetTask, position, "resetTargetTaskIfNeeded");
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 6cf9432..7b5b0ad 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -99,9 +99,8 @@
                 // the task's profile
                 return;
             }
-            if (!mAllowed && !task.isActivityTypeHome()) {
-                // Skip if the caller isn't allowed to fetch this task, except for the home
-                // task which we always return.
+            if (!mAllowed) {
+                // Skip if the caller isn't allowed to fetch this task
                 return;
             }
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e77a535..19bf451 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -79,6 +79,7 @@
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
@@ -87,9 +88,7 @@
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -1651,8 +1650,8 @@
      * Reorder the history stack so that the passed activity is brought to the front.
      */
     final void moveActivityToFrontLocked(ActivityRecord newTop) {
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing and adding activity "
-                + newTop + " to stack at top callers=" + Debug.getCallers(4));
+        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing and adding activity %s to stack at top "
+                + "callers=%s", newTop, Debug.getCallers(4));
 
         positionChildAtTop(newTop);
         updateEffectiveIntent();
@@ -1951,8 +1950,8 @@
                         ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
                 break;
         }
-        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this
-                + " mLockTaskAuth=" + lockTaskAuthToString());
+        ProtoLog.d(WM_DEBUG_LOCKTASK, "setLockTaskAuth: task=%s mLockTaskAuth=%s", this,
+                lockTaskAuthToString());
     }
 
     @Override
@@ -6370,7 +6369,8 @@
                 // Here it is!  Now, if this is not yet visible (occluded by another task) to the
                 // user, then just add it without starting; it will get started when the user
                 // navigates back to it.
-                if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task,
+                ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
+                                + "callers: %s", r, task,
                         new RuntimeException("here").fillInStackTrace());
                 rTask.positionChildAtTop(r);
                 ActivityOptions.abort(options);
@@ -6392,8 +6392,8 @@
         task = activityTask;
 
         // Slot the activity into the history stack and proceed
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
-                new RuntimeException("here").fillInStackTrace());
+        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to stack to task %s "
+                        + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
         task.positionChildAtTop(r);
 
         // The transition animation and starting window are not needed if {@code allowMoveToFront}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index bb9cf2e..c5ebace 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -22,9 +22,9 @@
 import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -72,6 +72,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.Watchdog;
 import com.android.server.wm.ActivityTaskManagerService.HotPath;
@@ -1348,9 +1349,8 @@
             }
             return;
         }
-        if (DEBUG_CONFIGURATION) {
-            Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName + " new config " + config);
-        }
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName,
+                config);
         if (Build.IS_DEBUGGABLE && mHasImeService) {
             // TODO (b/135719017): Temporary log for debugging IME service.
             Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 763654d..dda81ff 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -187,7 +187,7 @@
         moveEachPointers(mLastEvent, p(10, 10), p(10, 10));
         send(mLastEvent);
         goToStateClearFrom(STATE_DRAGGING_2FINGERS);
-        assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_UP);
+        assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_MOVE, ACTION_UP);
     }
 
     @Test
@@ -288,7 +288,7 @@
         assertState(STATE_DRAGGING);
         goToStateClearFrom(STATE_DRAGGING_2FINGERS);
         assertState(STATE_CLEAR);
-        assertCapturedEvents(ACTION_DOWN, ACTION_UP);
+        assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
         assertCapturedEventsNoHistory();
     }
 
@@ -301,6 +301,7 @@
         assertState(STATE_CLEAR);
         assertCapturedEvents(
                 /* goto dragging state */ ACTION_DOWN,
+                ACTION_MOVE,
                 /* leave dragging state */ ACTION_UP,
                 ACTION_DOWN,
                 ACTION_POINTER_DOWN,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 53c4d6f..f17173f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -62,6 +62,32 @@
         assertThat(message).isEqualTo(buildMessage("5F:81:21:00"));
     }
 
+    @Test
+    public void buildSetOsdName_short() {
+        String deviceName = "abc";
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1,
+                ADDR_TV, deviceName);
+        assertThat(message).isEqualTo(buildMessage("40:47:61:62:63"));
+    }
+
+    @Test
+    public void buildSetOsdName_maximumLength() {
+        String deviceName = "abcdefghijklmn";
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1,
+                ADDR_TV, deviceName);
+        assertThat(message).isEqualTo(
+                buildMessage("40:47:61:62:63:64:65:66:67:68:69:6A:6B:6C:6D:6E"));
+    }
+
+    @Test
+    public void buildSetOsdName_tooLong() {
+        String deviceName = "abcdefghijklmnop";
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1,
+                ADDR_TV, deviceName);
+        assertThat(message).isEqualTo(
+                buildMessage("40:47:61:62:63:64:65:66:67:68:69:6A:6B:6C:6D:6E"));
+    }
+
     /**
      * Build a CEC message from a hex byte string with bytes separated by {@code :}.
      *
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
new file mode 100644
index 0000000..d7ed96f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2020 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 android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.timezonedetector.TimeZoneCapabilities;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link ConfigurationInternal} and the {@link TimeZoneCapabilities} and
+ * {@link android.app.timezonedetector.TimeZoneConfiguration} that can be generated from it.
+ */
+public class ConfigurationInternalTest {
+
+    private static final int ARBITRARY_USER_ID = 99999;
+
+    /**
+     * Tests when {@link ConfigurationInternal#isUserConfigAllowed()} and
+     * {@link ConfigurationInternal#isAutoDetectionSupported()} are both true.
+     */
+    @Test
+    public void test_unrestricted() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionSupported(true)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities();
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration());
+            assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities();
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration());
+            assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+    }
+
+    /** Tests when {@link ConfigurationInternal#isUserConfigAllowed()} is false */
+    @Test
+    public void test_restricted() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(false)
+                .setAutoDetectionSupported(true)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration());
+            assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration());
+            assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+    }
+
+    /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */
+    @Test
+    public void test_autoDetectNotSupported() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionSupported(false)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration());
+            assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration());
+            assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index e5e9311..4ef2082 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
@@ -32,56 +33,64 @@
 
 class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
 
-    private StrategyListener mListener;
+    private ConfigurationChangeListener mConfigurationChangeListener;
 
     // Fake state
-    private TimeZoneCapabilities mCapabilities;
-    private TimeZoneConfiguration mConfiguration;
+    private ConfigurationInternal mConfigurationInternal;
 
     // Call tracking.
     private GeolocationTimeZoneSuggestion mLastGeolocationSuggestion;
     private ManualTimeZoneSuggestion mLastManualSuggestion;
     private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
-    private boolean mHandleAutoTimeZoneConfigChangedCalled;
     private boolean mDumpCalled;
     private final List<Dumpable> mDumpables = new ArrayList<>();
 
     @Override
-    public void setStrategyListener(@NonNull StrategyListener listener) {
-        mListener = listener;
+    public void addConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
+        if (mConfigurationChangeListener != null) {
+            fail("Fake only supports one listener");
+        }
+        mConfigurationChangeListener = listener;
     }
 
     @Override
-    public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-        return mCapabilities;
+    public ConfigurationInternal getConfigurationInternal(int userId) {
+        if (mConfigurationInternal.getUserId() != userId) {
+            fail("Fake only supports one user");
+        }
+        return mConfigurationInternal;
     }
 
     @Override
-    public boolean updateConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
-        assertNotNull(mConfiguration);
-        assertNotNull(configuration);
+    public ConfigurationInternal getCurrentUserConfigurationInternal() {
+        return mConfigurationInternal;
+    }
 
-        // Simulate the strategy's behavior: the new configuration will be the old configuration
-        // merged with the new.
-        TimeZoneConfiguration oldConfiguration = mConfiguration;
-        TimeZoneConfiguration newConfiguration =
-                new TimeZoneConfiguration.Builder(mConfiguration)
-                        .mergeProperties(configuration)
-                        .build();
+    @Override
+    public boolean updateConfiguration(@NonNull TimeZoneConfiguration requestedChanges) {
+        assertNotNull(mConfigurationInternal);
+        assertNotNull(requestedChanges);
 
-        if (newConfiguration.equals(oldConfiguration)) {
+        // Simulate the real strategy's behavior: the new configuration will be updated to be the
+        // old configuration merged with the new if the user has the capability to up the settings.
+        // Then, if the configuration changed, the change listener is invoked.
+        TimeZoneCapabilities capabilities = mConfigurationInternal.createCapabilities();
+        TimeZoneConfiguration newConfiguration = capabilities.applyUpdate(requestedChanges);
+        if (newConfiguration == null) {
             return false;
         }
-        mConfiguration = newConfiguration;
-        mListener.onConfigurationChanged();
+
+        if (!newConfiguration.equals(capabilities.getConfiguration())) {
+            mConfigurationInternal = mConfigurationInternal.merge(newConfiguration);
+
+            // Note: Unlike the real strategy, the listeners is invoked synchronously.
+            mConfigurationChangeListener.onChange();
+        }
         return true;
     }
 
-    @Override
-    @NonNull
-    public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-        return mConfiguration;
+    public void simulateConfigurationChangeForTests() {
+        mConfigurationChangeListener.onChange();
     }
 
     @Override
@@ -103,11 +112,6 @@
     }
 
     @Override
-    public void handleAutoTimeZoneConfigChanged() {
-        mHandleAutoTimeZoneConfigChangedCalled = true;
-    }
-
-    @Override
     public void addDumpable(Dumpable dumpable) {
         mDumpables.add(dumpable);
     }
@@ -117,19 +121,14 @@
         mDumpCalled = true;
     }
 
-    void initializeConfiguration(TimeZoneConfiguration configuration) {
-        mConfiguration = configuration;
-    }
-
-    void initializeCapabilities(TimeZoneCapabilities capabilities) {
-        mCapabilities = capabilities;
+    void initializeConfiguration(ConfigurationInternal configurationInternal) {
+        mConfigurationInternal = configurationInternal;
     }
 
     void resetCallTracking() {
         mLastGeolocationSuggestion = null;
         mLastManualSuggestion = null;
         mLastTelephonySuggestion = null;
-        mHandleAutoTimeZoneConfigChangedCalled = false;
         mDumpCalled = false;
     }
 
@@ -146,10 +145,6 @@
         assertEquals(expectedSuggestion, mLastTelephonySuggestion);
     }
 
-    void verifyHandleAutoTimeZoneConfigChangedCalled() {
-        assertTrue(mHandleAutoTimeZoneConfigChangedCalled);
-    }
-
     void verifyDumpCalled() {
         assertTrue(mDumpCalled);
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
new file mode 100644
index 0000000..f45b3a8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.UserIdInt;
+
+/** A fake {@link CallerIdentityInjector} used in tests. */
+public class TestCallerIdentityInjector implements CallerIdentityInjector {
+
+    private long mToken = 9999L;
+    private int mCallingUserId;
+    private Integer mCurrentCallingUserId;
+
+    public void initializeCallingUserId(@UserIdInt int userId) {
+        mCallingUserId = userId;
+        mCurrentCallingUserId = userId;
+    }
+
+    @Override
+    public int getCallingUserId() {
+        assertNotNull("callingUserId has been cleared", mCurrentCallingUserId);
+        return mCurrentCallingUserId;
+    }
+
+    @Override
+    public long clearCallingIdentity() {
+        mCurrentCallingUserId = null;
+        return mToken;
+    }
+
+    @Override
+    public void restoreCallingIdentity(long token) {
+        assertEquals(token, mToken);
+        mCurrentCallingUserId = mCallingUserId;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index e9d57e5..918babc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.timezonedetector;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
 import android.content.Context;
@@ -85,6 +86,18 @@
         mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable);
     }
 
+    @Test
+    public void testAddConfigurationListener() throws Exception {
+        boolean[] changeCalled = new boolean[2];
+        mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[0] = true);
+        mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[1] = true);
+
+        mFakeTimeZoneDetectorStrategy.simulateConfigurationChangeForTests();
+
+        assertTrue(changeCalled[0]);
+        assertTrue(changeCalled[1]);
+    }
+
     private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
         return new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS);
     }
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 3a1ec4f..27b04b6 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.timezonedetector;
 
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -36,7 +34,6 @@
 import android.app.timezonedetector.ITimeZoneConfigurationListener;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -65,6 +62,7 @@
     private TimeZoneDetectorService mTimeZoneDetectorService;
     private HandlerThread mHandlerThread;
     private TestHandler mTestHandler;
+    private TestCallerIdentityInjector mTestCallerIdentityInjector;
 
 
     @Before
@@ -76,10 +74,14 @@
         mHandlerThread.start();
         mTestHandler = new TestHandler(mHandlerThread.getLooper());
 
+        mTestCallerIdentityInjector = new TestCallerIdentityInjector();
+        mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID);
+
         mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy();
 
         mTimeZoneDetectorService = new TimeZoneDetectorService(
-                mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy);
+                mMockContext, mTestHandler, mTestCallerIdentityInjector,
+                mFakeTimeZoneDetectorStrategy);
     }
 
     @After
@@ -107,40 +109,12 @@
     public void testGetCapabilities() {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
-        TimeZoneCapabilities capabilities = createTimeZoneCapabilities();
-        mFakeTimeZoneDetectorStrategy.initializeCapabilities(capabilities);
-
-        assertEquals(capabilities, mTimeZoneDetectorService.getCapabilities());
-
-        verify(mMockContext).enforceCallingPermission(
-                eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
-                anyString());
-    }
-
-    @Test(expected = SecurityException.class)
-    public void testGetConfiguration_withoutPermission() {
-        doThrow(new SecurityException("Mock"))
-                .when(mMockContext).enforceCallingPermission(anyString(), any());
-
-        try {
-            mTimeZoneDetectorService.getConfiguration();
-            fail();
-        } finally {
-            verify(mMockContext).enforceCallingPermission(
-                    eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
-                    anyString());
-        }
-    }
-
-    @Test
-    public void testGetConfiguration() {
-        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
-
-        TimeZoneConfiguration configuration =
-                createTimeZoneConfiguration(false /* autoDetectionEnabled */);
+        ConfigurationInternal configuration =
+                createConfigurationInternal(true /* autoDetectionEnabled*/);
         mFakeTimeZoneDetectorStrategy.initializeConfiguration(configuration);
 
-        assertEquals(configuration, mTimeZoneDetectorService.getConfiguration());
+        assertEquals(configuration.createCapabilities(),
+                mTimeZoneDetectorService.getCapabilities());
 
         verify(mMockContext).enforceCallingPermission(
                 eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
@@ -181,10 +155,9 @@
 
     @Test
     public void testConfigurationChangeListenerRegistrationAndCallbacks() throws Exception {
-        TimeZoneConfiguration autoDetectDisabledConfiguration =
-                createTimeZoneConfiguration(false /* autoDetectionEnabled */);
-
-        mFakeTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration);
+        ConfigurationInternal initialConfiguration =
+                createConfigurationInternal(false /* autoDetectionEnabled */);
+        mFakeTimeZoneDetectorStrategy.initializeConfiguration(initialConfiguration);
 
         IBinder mockListenerBinder = mock(IBinder.class);
         ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
@@ -210,13 +183,12 @@
             // Simulate the configuration being changed and verify the mockListener was notified.
             TimeZoneConfiguration autoDetectEnabledConfiguration =
                     createTimeZoneConfiguration(true /* autoDetectionEnabled */);
-
             mTimeZoneDetectorService.updateConfiguration(autoDetectEnabledConfiguration);
 
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
                     anyString());
-            verify(mockListener).onChange(autoDetectEnabledConfiguration);
+            verify(mockListener).onChange();
             verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
             reset(mockListenerBinder, mockListener, mMockContext);
         }
@@ -242,12 +214,14 @@
         {
             doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
+            TimeZoneConfiguration autoDetectDisabledConfiguration =
+                    createTimeZoneConfiguration(false /* autoDetectionEnabled */);
             mTimeZoneDetectorService.updateConfiguration(autoDetectDisabledConfiguration);
 
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
                     anyString());
-            verify(mockListener, never()).onChange(any());
+            verify(mockListener, never()).onChange();
             verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
             reset(mockListenerBinder, mockListener, mMockContext);
         }
@@ -379,33 +353,22 @@
         mFakeTimeZoneDetectorStrategy.verifyDumpCalled();
     }
 
-    @Test
-    public void testHandleAutoTimeZoneConfigChanged() throws Exception {
-        mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
-        mTestHandler.assertTotalMessagesEnqueued(1);
-        mTestHandler.waitForMessagesToBeProcessed();
-        mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
-
-        mFakeTimeZoneDetectorStrategy.resetCallTracking();
-
-        mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
-        mTestHandler.assertTotalMessagesEnqueued(2);
-        mTestHandler.waitForMessagesToBeProcessed();
-        mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
-    }
-
-    private static TimeZoneConfiguration createTimeZoneConfiguration(
-            boolean autoDetectionEnabled) {
-        return new TimeZoneConfiguration.Builder()
+    private static TimeZoneConfiguration createTimeZoneConfiguration(boolean autoDetectionEnabled) {
+        return new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
                 .setAutoDetectionEnabled(autoDetectionEnabled)
                 .build();
     }
 
-    private static TimeZoneCapabilities createTimeZoneCapabilities() {
-        return new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
-                .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
-                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
-                .setSuggestManualTimeZone(CAPABILITY_POSSESSED)
+    private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) {
+        // Default geo detection settings from auto detection settings - they are not important to
+        // the tests.
+        final boolean geoDetectionEnabled = autoDetectionEnabled;
+        return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionSupported(true)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionEnabled(autoDetectionEnabled)
+                .setLocationEnabled(geoDetectionEnabled)
+                .setGeoDetectionEnabled(geoDetectionEnabled)
                 .build();
     }
 
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 a6caa42..2bee5e5 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -23,10 +23,6 @@
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
 
 import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGH;
 import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGHEST;
@@ -37,9 +33,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -47,7 +43,6 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.util.IndentingPrintWriter;
 
@@ -61,7 +56,6 @@
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -94,192 +88,149 @@
                     TELEPHONY_SCORE_HIGHEST),
     };
 
-    private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED =
-            new TimeZoneConfiguration.Builder()
-                    .setAutoDetectionEnabled(true)
-                    .build();
-
-    private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED =
-            new TimeZoneConfiguration.Builder()
+    private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(false)
+                    .setAutoDetectionSupported(true)
                     .setAutoDetectionEnabled(false)
-                    .build();
-
-    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED =
-            new TimeZoneConfiguration.Builder()
+                    .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
                     .build();
 
-    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED =
-            new TimeZoneConfiguration.Builder()
+    private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(false)
+                    .setAutoDetectionSupported(true)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
                     .setGeoDetectionEnabled(true)
                     .build();
 
+    private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(false)
+                    .setAutoDetectionEnabled(false)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(false)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(true)
+                    .setAutoDetectionEnabled(false)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(false)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_ENABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(true)
+                    .setAutoDetectionEnabled(false)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(true)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setAutoDetectionSupported(true)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(false)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setAutoDetectionSupported(true)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(true)
+                    .build();
+
+    private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED =
+            createConfig(false /* autoDetection */, null);
+    private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED =
+            createConfig(true /* autoDetection */, null);
+    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED =
+            createConfig(null, true /* geoDetection */);
+    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED =
+            createConfig(null, false /* geoDetection */);
+
     private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
     private FakeCallback mFakeCallback;
-    private MockStrategyListener mMockStrategyListener;
+    private MockConfigChangeListener mMockConfigChangeListener;
+
 
     @Before
     public void setUp() {
         mFakeCallback = new FakeCallback();
-        mMockStrategyListener = new MockStrategyListener();
+        mMockConfigChangeListener = new MockConfigChangeListener();
         mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeCallback);
-        mFakeCallback.setStrategyForSettingsCallbacks(mTimeZoneDetectorStrategy);
-        mTimeZoneDetectorStrategy.setStrategyListener(mMockStrategyListener);
+        mTimeZoneDetectorStrategy.addConfigChangeListener(mMockConfigChangeListener);
     }
 
     @Test
-    public void testGetCapabilities() {
-        new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID);
-        assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID));
-    }
-
-    @Test
-    public void testGetConfiguration() {
-        new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID);
-        assertTrue(expectedConfiguration.isComplete());
-        assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID));
-    }
-
-    @Test
-    public void testCapabilitiesTestInfra_unrestricted() {
-        Script script = new Script();
-
-        script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone());
-        }
-
-        script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
-        }
-    }
-
-    @Test
-    public void testCapabilitiesTestInfra_restricted() {
-        Script script = new Script();
-
-        script.initializeUser(USER_ID, UserCase.RESTRICTED,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
-        }
-
-        script.initializeUser(USER_ID, UserCase.RESTRICTED,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
-        }
-    }
-
-    @Test
-    public void testCapabilitiesTestInfra_autoDetectNotSupported() {
-        Script script = new Script();
-
-        script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
-        }
-
-        script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
-        }
+    public void testGetCurrentUserConfiguration() {
+        new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
+        ConfigurationInternal expectedConfiguration =
+                mFakeCallback.getConfigurationInternal(USER_ID);
+        assertEquals(expectedConfiguration,
+                mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal());
     }
 
     @Test
     public void testUpdateConfiguration_unrestricted() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // Set the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */);
 
         // Nothing should have happened: it was initialized in this state.
         script.verifyConfigurationNotChanged();
 
         // Update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(USER_ID,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED);
 
         // Update the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(USER_ID,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // Update the configuration to enable geolocation time zone detection.
         script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_ENABLED,  true /* expectedResult */);
+                CONFIG_GEO_DETECTION_ENABLED,  true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(USER_ID,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED));
+        script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED);
     }
 
     @Test
     public void testUpdateConfiguration_restricted() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED);
 
         // Try to update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
         // Update the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_ENABLED,  false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED,  false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Update the configuration to enable geolocation time zone detection.
+        // Try to  update the configuration to enable geolocation time zone detection.
         script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_ENABLED,  false /* expectedResult */);
+                CONFIG_GEO_DETECTION_ENABLED,  false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
@@ -287,20 +238,16 @@
 
     @Test
     public void testUpdateConfiguration_autoDetectNotSupported() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED);
 
         // Try to update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
         // Update the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
@@ -313,8 +260,7 @@
         TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion =
                 createEmptySlotIndex2Suggestion();
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion)
@@ -359,9 +305,7 @@
         TelephonyTestCase testCase2 = newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
                 QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
 
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // A low quality suggestions will not be taken: The device time zone setting is left
         // uninitialized.
@@ -426,8 +370,7 @@
 
         for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
             // Start with the device in a known state.
-            script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                    CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+            script.initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                     .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
             TelephonyTimeZoneSuggestion suggestion =
@@ -447,8 +390,7 @@
                     mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting on should cause the device setting to be set.
-            script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED,
-                    true /* expectedResult */);
+            script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */);
 
             // When time zone detection is already enabled the suggestion (if it scores highly
             // enough) should be set immediately.
@@ -465,8 +407,7 @@
                     mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting should off should do nothing.
-            script.simulateUpdateConfiguration(
-                    USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
+            script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */)
                     .verifyTimeZoneNotChanged();
 
             // Assert internal service state.
@@ -480,8 +421,7 @@
     @Test
     public void testTelephonySuggestionsSingleSlotId() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
@@ -546,8 +486,7 @@
                         TELEPHONY_SCORE_NONE);
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
                 // Initialize the latest suggestions as empty so we don't need to worry about nulls
                 // below for the first loop.
@@ -632,9 +571,7 @@
      */
     @Test
     public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         TelephonyTestCase testCase = newTelephonyTestCase(
                 MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
@@ -652,40 +589,39 @@
 
         // Toggling time zone detection should set the device time zone only if the current setting
         // value is different from the most recent telephony suggestion.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */)
                 .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+                .simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneNotChanged();
 
         // Simulate a user turning auto detection off, a new suggestion being made while auto
         // detection is off, and the user turning it on again.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */)
                 .simulateTelephonyTimeZoneSuggestion(newYorkSuggestion)
                 .verifyTimeZoneNotChanged();
         // Latest suggestion should be used.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(newYorkSuggestion);
     }
 
     @Test
-    public void testManualSuggestion_autoDetectionEnabled_autoTelephony() {
-        checkManualSuggestion_autoDetectionEnabled(false /* geoDetectionEnabled */);
+    public void testManualSuggestion_unrestricted_autoDetectionEnabled_autoTelephony() {
+        checkManualSuggestion_unrestricted_autoDetectionEnabled(false /* geoDetectionEnabled */);
     }
 
     @Test
-    public void testManualSuggestion_autoDetectionEnabled_autoGeo() {
-        checkManualSuggestion_autoDetectionEnabled(true /* geoDetectionEnabled */);
+    public void testManualSuggestion_unrestricted_autoDetectionEnabled_autoGeo() {
+        checkManualSuggestion_unrestricted_autoDetectionEnabled(true /* geoDetectionEnabled */);
     }
 
-    private void checkManualSuggestion_autoDetectionEnabled(boolean geoDetectionEnabled) {
-        TimeZoneConfiguration geoTzEnabledConfig =
-                new TimeZoneConfiguration.Builder()
+    private void checkManualSuggestion_unrestricted_autoDetectionEnabled(
+            boolean geoDetectionEnabled) {
+        ConfigurationInternal geoTzEnabledConfig =
+                new ConfigurationInternal.Builder(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                         .setGeoDetectionEnabled(geoDetectionEnabled)
                         .build();
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(geoTzEnabledConfig))
+                .initializeConfig(geoTzEnabledConfig)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Auto time zone detection is enabled so the manual suggestion should be ignored.
@@ -697,35 +633,19 @@
     @Test
     public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-        // Auto time zone detection is enabled so the manual suggestion should be ignored.
+        // User is restricted so the manual suggestion should be ignored.
         script.simulateManualTimeZoneSuggestion(
                 USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
-            .verifyTimeZoneNotChanged();
-    }
-
-    @Test
-    public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
-
-        // Auto time zone detection is enabled so the manual suggestion should be ignored.
-        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
-        script.simulateManualTimeZoneSuggestion(
-                USER_ID, manualSuggestion, true /* expectedResult */)
-            .verifyTimeZoneChangedAndReset(manualSuggestion);
+                .verifyTimeZoneNotChanged();
     }
 
     @Test
     public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Auto time zone detection is disabled so the manual suggestion should be used.
@@ -738,8 +658,7 @@
     @Test
     public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Restricted users do not have the capability.
@@ -750,10 +669,9 @@
     }
 
     @Test
-    public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() {
+    public void testManualSuggestion_autoDetectNotSupported() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Unrestricted users have the capability.
@@ -765,9 +683,7 @@
 
     @Test
     public void testGeoSuggestion_uncertain() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeoLocationSuggestion();
@@ -783,8 +699,7 @@
     @Test
     public void testGeoSuggestion_noZones() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         GeolocationTimeZoneSuggestion noZonesSuggestion = createGeoLocationSuggestion(list());
@@ -802,8 +717,7 @@
                 createGeoLocationSuggestion(list("Europe/London"));
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateGeolocationTimeZoneSuggestion(suggestion)
@@ -828,8 +742,7 @@
                 createGeoLocationSuggestion(list("Europe/Paris"));
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion)
@@ -856,72 +769,27 @@
                 mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
-    /**
-     * Confirms that toggling the auto time zone detection enabled setting has the expected behavior
-     * when the strategy is "opinionated" and "un-opinionated" when in geolocation detection is
-     * enabled.
-     */
     @Test
-    public void testTogglingAutoDetectionEnabled_autoGeo() {
-        GeolocationTimeZoneSuggestion geolocationSuggestion =
+    public void testGeoSuggestion_togglingGeoDetectionClearsLastSuggestion() {
+        GeolocationTimeZoneSuggestion suggestion =
                 createGeoLocationSuggestion(list("Europe/London"));
-        GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
-                createUncertainGeoLocationSuggestion();
-        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-        script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion);
-
-        // When time zone detection is not enabled, the time zone suggestion will not be set.
-        script.verifyTimeZoneNotChanged();
-
-        // Assert internal service state.
-        assertEquals(geolocationSuggestion,
-                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
-
-        // Toggling the time zone setting on should cause the device setting to be set.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+        script.simulateGeolocationTimeZoneSuggestion(suggestion)
                 .verifyTimeZoneChangedAndReset("Europe/London");
 
-        // Toggling the time zone setting should off should do nothing because the device is now
-        // set to that time zone.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged();
+        // Assert internal service state.
+        assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
 
-        // Now toggle auto time zone setting, and confirm it is opinionated.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .simulateManualTimeZoneSuggestion(
-                        USER_ID, manualSuggestion, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset(manualSuggestion)
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset("Europe/London");
-
-        // Now withdraw the geolocation suggestion, and assert the strategy is no longer
-        // opinionated.
-        /* expectedResult */
-        script.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
-                .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged()
-                .simulateManualTimeZoneSuggestion(
-                        USER_ID, manualSuggestion, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset(manualSuggestion)
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged();
+        // Turn off geo detection and verify the latest suggestion is cleared.
+        script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true)
+                .verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // Assert internal service state.
-        assertEquals(uncertainGeolocationSuggestion,
-                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
     /**
@@ -937,88 +805,48 @@
                 "Europe/Paris");
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Add suggestions. Nothing should happen as time zone detection is disabled.
         script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
                 .verifyTimeZoneNotChanged();
+
+        // Geolocation suggestions are only stored when geolocation detection is enabled.
+        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+
         script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
                 .verifyTimeZoneNotChanged();
 
-        // Assert internal service state.
-        assertEquals(geolocationSuggestion,
-                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+        // Telephony suggestions are always stored.
         assertEquals(telephonySuggestion,
                 mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion);
 
         // Toggling the time zone detection enabled setting on should cause the device setting to be
         // set from the telephony signal, as we've started with geolocation time zone detection
         // disabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(telephonySuggestion);
 
-        // Changing the detection to enable geo detection should cause the device tz setting to
-        // change to the geo suggestion.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */)
+        // Changing the detection to enable geo detection won't cause the device tz setting to
+        // change because the geo suggestion is empty.
+        script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */)
+                .verifyTimeZoneNotChanged()
+                .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
                 .verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0));
 
         // Changing the detection to disable geo detection should cause the device tz setting to
         // change to the telephony suggestion.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(telephonySuggestion);
-    }
 
-    /**
-     * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
-     * zone is actually necessary. This test proves that the strategy doesn't assume it knows the
-     * current setting.
-     */
-    @Test
-    public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting_autoGeo() {
-        GeolocationTimeZoneSuggestion losAngelesSuggestion =
-                createGeoLocationSuggestion(list("America/Los_Angeles"));
-        GeolocationTimeZoneSuggestion newYorkSuggestion =
-                createGeoLocationSuggestion(list("America/New_York"));
-
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED));
-
-        // Initialization.
-        script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion)
-                .verifyTimeZoneChangedAndReset("America/Los_Angeles");
-        // Suggest it again - it should not be set because it is already set.
-        script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion)
-                .verifyTimeZoneNotChanged();
-
-        // Toggling time zone detection should set the device time zone only if the current setting
-        // value is different from the most recent telephony suggestion.
-        /* expectedResult */
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged();
-
-        // Simulate a user turning auto detection off, a new suggestion being made while auto
-        // detection is off, and the user turning it on again.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .simulateGeolocationTimeZoneSuggestion(newYorkSuggestion)
-                .verifyTimeZoneNotChanged();
-        // Latest suggestion should be used.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset("America/New_York");
+        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
     @Test
     public void testAddDumpable() {
         new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         AtomicBoolean dumpCalled = new AtomicBoolean(false);
@@ -1069,25 +897,26 @@
         return suggestion;
     }
 
+    private static TimeZoneConfiguration createConfig(
+            @Nullable Boolean autoDetection, @Nullable Boolean geoDetection) {
+        TimeZoneConfiguration.Builder builder = new TimeZoneConfiguration.Builder(USER_ID);
+        if (autoDetection != null) {
+            builder.setAutoDetectionEnabled(autoDetection);
+        }
+        if (geoDetection != null) {
+            builder.setGeoDetectionEnabled(geoDetection);
+        }
+        return builder.build();
+    }
+
     static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback {
 
-        private TimeZoneCapabilities mCapabilities;
-        private final TestState<UserConfiguration> mConfiguration = new TestState<>();
+        private final TestState<ConfigurationInternal> mConfigurationInternal = new TestState<>();
         private final TestState<String> mTimeZoneId = new TestState<>();
-        private TimeZoneDetectorStrategyImpl mStrategy;
+        private ConfigurationChangeListener mConfigChangeListener;
 
-        void setStrategyForSettingsCallbacks(TimeZoneDetectorStrategyImpl strategy) {
-            assertNotNull(strategy);
-            mStrategy = strategy;
-        }
-
-        void initializeUser(@UserIdInt int userId, TimeZoneCapabilities capabilities,
-                TimeZoneConfiguration configuration) {
-            assertEquals(userId, capabilities.getUserId());
-            mCapabilities = capabilities;
-            assertTrue("Configuration must be complete when initializing, config=" + configuration,
-                    configuration.isComplete());
-            mConfiguration.init(new UserConfiguration(userId, configuration));
+        void initializeConfig(ConfigurationInternal configurationInternal) {
+            mConfigurationInternal.init(configurationInternal);
         }
 
         void initializeTimeZoneSetting(String zoneId) {
@@ -1095,43 +924,22 @@
         }
 
         @Override
-        public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-            assertEquals(userId, mCapabilities.getUserId());
-            return mCapabilities;
+        public void setConfigChangeListener(ConfigurationChangeListener listener) {
+            mConfigChangeListener = listener;
         }
 
         @Override
-        public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-            UserConfiguration latest = mConfiguration.getLatest();
-            assertEquals(userId, latest.userId);
-            return latest.configuration;
-        }
-
-        @Override
-        public void setConfiguration(@UserIdInt int userId, TimeZoneConfiguration newConfig) {
-            assertNotNull(newConfig);
-            assertTrue(newConfig.isComplete());
-
-            UserConfiguration latestUserConfig = mConfiguration.getLatest();
-            assertEquals(userId, latestUserConfig.userId);
-            TimeZoneConfiguration oldConfig = latestUserConfig.configuration;
-
-            mConfiguration.set(new UserConfiguration(userId, newConfig));
-
-            if (!newConfig.equals(oldConfig)) {
-                // Simulate what happens when the auto detection configuration is changed.
-                mStrategy.handleAutoTimeZoneConfigChanged();
+        public ConfigurationInternal getConfigurationInternal(int userId) {
+            ConfigurationInternal configuration = mConfigurationInternal.getLatest();
+            if (userId != configuration.getUserId()) {
+                fail("FakeCallback does not support multiple users.");
             }
+            return configuration;
         }
 
         @Override
-        public boolean isAutoDetectionEnabled() {
-            return mConfiguration.getLatest().configuration.isAutoDetectionEnabled();
-        }
-
-        @Override
-        public boolean isGeoDetectionEnabled() {
-            return mConfiguration.getLatest().configuration.isGeoDetectionEnabled();
+        public int getCurrentUserId() {
+            return mConfigurationInternal.getLatest().getUserId();
         }
 
         @Override
@@ -1149,9 +957,25 @@
             mTimeZoneId.set(zoneId);
         }
 
+        @Override
+        public void storeConfiguration(TimeZoneConfiguration newConfiguration) {
+            ConfigurationInternal oldConfiguration = mConfigurationInternal.getLatest();
+            if (newConfiguration.getUserId() != oldConfiguration.getUserId()) {
+                fail("FakeCallback does not support multiple users");
+            }
+
+            ConfigurationInternal mergedConfiguration = oldConfiguration.merge(newConfiguration);
+            if (!mergedConfiguration.equals(oldConfiguration)) {
+                mConfigurationInternal.set(mergedConfiguration);
+
+                // Note: Unlike the real callback impl, the listener is invoked synchronously.
+                mConfigChangeListener.onChange();
+            }
+        }
+
         void assertKnownUser(int userId) {
-            assertEquals(userId, mCapabilities.getUserId());
-            assertEquals(userId, mConfiguration.getLatest().userId);
+            assertEquals("FakeCallback does not support multiple users",
+                    mConfigurationInternal.getLatest().getUserId(), userId);
         }
 
         void assertTimeZoneNotChanged() {
@@ -1166,43 +990,7 @@
 
         void commitAllChanges() {
             mTimeZoneId.commitLatest();
-            mConfiguration.commitLatest();
-        }
-    }
-
-    private static final class UserConfiguration {
-        public final @UserIdInt int userId;
-        public final TimeZoneConfiguration configuration;
-
-        UserConfiguration(int userId, TimeZoneConfiguration configuration) {
-            this.userId = userId;
-            this.configuration = configuration;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            UserConfiguration that = (UserConfiguration) o;
-            return userId == that.userId
-                    && Objects.equals(configuration, that.configuration);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(userId, configuration);
-        }
-
-        @Override
-        public String toString() {
-            return "UserConfiguration{"
-                    + "userId=" + userId
-                    + ", configuration=" + configuration
-                    + '}';
+            mConfigurationInternal.commitLatest();
         }
     }
 
@@ -1255,64 +1043,14 @@
         }
     }
 
-    /** Simulated user test cases. */
-    enum UserCase {
-        /** A catch-all for users that can set auto time zone config. */
-        UNRESTRICTED,
-        /** A catch-all for users that can't set auto time zone config. */
-        RESTRICTED,
-        /**
-         * Like {@link #UNRESTRICTED}, but auto tz detection is not
-         * supported on the device.
-         */
-        AUTO_DETECT_NOT_SUPPORTED,
-    }
-
-    /**
-     * Creates a {@link TimeZoneCapabilities} object for a user in the specific role with the
-     * supplied configuration.
-     */
-    private static TimeZoneCapabilities createCapabilities(
-            int userId, UserCase userCase, TimeZoneConfiguration configuration) {
-        switch (userCase) {
-            case UNRESTRICTED: {
-                int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled()
-                        ? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED;
-                return new TimeZoneCapabilities.Builder(userId)
-                        .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
-                        .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
-                        .setSuggestManualTimeZone(suggestManualTimeZoneCapability)
-                        .build();
-            }
-            case RESTRICTED: {
-                return new TimeZoneCapabilities.Builder(userId)
-                        .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
-                        .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
-                        .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED)
-                        .build();
-            }
-            case AUTO_DETECT_NOT_SUPPORTED: {
-                return new TimeZoneCapabilities.Builder(userId)
-                        .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
-                        .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
-                        .setSuggestManualTimeZone(CAPABILITY_POSSESSED)
-                        .build();
-            }
-            default:
-                throw new AssertionError(userCase + " not recognized");
-        }
-    }
-
     /**
      * A "fluent" class allows reuse of code in tests: initialization, simulation and verification
      * logic.
      */
     private class Script {
 
-        Script initializeUser(
-                @UserIdInt int userId, UserCase userCase, TimeZoneConfiguration configuration) {
-            TimeZoneCapabilities capabilities = createCapabilities(userId, userCase, configuration);
-            mFakeCallback.initializeUser(userId, capabilities, configuration);
+        Script initializeConfig(ConfigurationInternal configuration) {
+            mFakeCallback.initializeConfig(configuration);
             return this;
         }
 
@@ -1326,10 +1064,9 @@
          * the return value.
          */
         Script simulateUpdateConfiguration(
-                @UserIdInt int userId, TimeZoneConfiguration configuration,
-                boolean expectedResult) {
+                TimeZoneConfiguration configuration, boolean expectedResult) {
             assertEquals(expectedResult,
-                    mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration));
+                    mTimeZoneDetectorStrategy.updateConfiguration(configuration));
             return this;
         }
 
@@ -1392,16 +1129,14 @@
         /**
          * Verifies that the configuration has been changed to the expected value.
          */
-        Script verifyConfigurationChangedAndReset(
-                @UserIdInt int userId, TimeZoneConfiguration expected) {
-            mFakeCallback.mConfiguration.assertHasBeenSet();
-            UserConfiguration expectedUserConfig = new UserConfiguration(userId, expected);
-            assertEquals(expectedUserConfig, mFakeCallback.mConfiguration.getLatest());
+        Script verifyConfigurationChangedAndReset(ConfigurationInternal expected) {
+            mFakeCallback.mConfigurationInternal.assertHasBeenSet();
+            assertEquals(expected, mFakeCallback.mConfigurationInternal.getLatest());
             mFakeCallback.commitAllChanges();
 
             // Also confirm the listener triggered.
-            mMockStrategyListener.verifyOnConfigurationChangedCalled();
-            mMockStrategyListener.reset();
+            mMockConfigChangeListener.verifyOnChangeCalled();
+            mMockConfigChangeListener.reset();
             return this;
         }
 
@@ -1410,10 +1145,10 @@
          * {@link TimeZoneConfiguration} have been changed.
          */
         Script verifyConfigurationNotChanged() {
-            mFakeCallback.mConfiguration.assertHasNotBeenSet();
+            mFakeCallback.mConfigurationInternal.assertHasNotBeenSet();
 
             // Also confirm the listener did not trigger.
-            mMockStrategyListener.verifyOnConfigurationChangedNotCalled();
+            mMockConfigChangeListener.verifyOnChangeNotCalled();
             return this;
         }
 
@@ -1448,24 +1183,24 @@
         return new TelephonyTestCase(matchType, quality, expectedScore);
     }
 
-    private static class MockStrategyListener implements TimeZoneDetectorStrategy.StrategyListener {
-        private boolean mOnConfigurationChangedCalled;
+    private static class MockConfigChangeListener implements ConfigurationChangeListener {
+        private boolean mOnChangeCalled;
 
         @Override
-        public void onConfigurationChanged() {
-            mOnConfigurationChangedCalled = true;
+        public void onChange() {
+            mOnChangeCalled = true;
         }
 
-        void verifyOnConfigurationChangedCalled() {
-            assertTrue(mOnConfigurationChangedCalled);
+        void verifyOnChangeCalled() {
+            assertTrue(mOnChangeCalled);
         }
 
-        void verifyOnConfigurationChangedNotCalled() {
-            assertFalse(mOnConfigurationChangedCalled);
+        void verifyOnChangeNotCalled() {
+            assertFalse(mOnChangeCalled);
         }
 
         void reset() {
-            mOnConfigurationChangedCalled = false;
+            mOnChangeCalled = false;
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index bce1142..ca3f815 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -53,7 +53,6 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.compatibility.common.util.SystemUtil;
-import com.android.internal.annotations.GuardedBy;
 
 import org.junit.After;
 import org.junit.Before;
@@ -76,14 +75,10 @@
 
     private static final int WAIT_TIMEOUT_MS = 5000;
     private static final Object sLock = new Object();
-    @GuardedBy("sLock")
-    private static boolean sTaskStackChangedCalled;
-    private static boolean sActivityBResumed;
 
     @Before
     public void setUp() throws Exception {
         mService = ActivityManager.getService();
-        sTaskStackChangedCalled = false;
     }
 
     @After
@@ -94,47 +89,33 @@
 
     @Test
     @Presubmit
-    @FlakyTest(bugId = 130388819)
     public void testTaskStackChanged_afterFinish() throws Exception {
+        final TestActivity activity = startTestActivity(ActivityA.class);
+        final CountDownLatch latch = new CountDownLatch(1);
         registerTaskStackChangedListener(new TaskStackListener() {
             @Override
             public void onTaskStackChanged() throws RemoteException {
-                synchronized (sLock) {
-                    sTaskStackChangedCalled = true;
-                }
+                latch.countDown();
             }
         });
 
-        Context context = getInstrumentation().getContext();
-        context.startActivity(
-                new Intent(context, ActivityA.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-        UiDevice.getInstance(getInstrumentation()).waitForIdle();
-        synchronized (sLock) {
-            assertTrue(sTaskStackChangedCalled);
-        }
-        assertTrue(sActivityBResumed);
+        activity.finish();
+        waitForCallback(latch);
     }
 
     @Test
     @Presubmit
     public void testTaskStackChanged_resumeWhilePausing() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
         registerTaskStackChangedListener(new TaskStackListener() {
             @Override
             public void onTaskStackChanged() throws RemoteException {
-                synchronized (sLock) {
-                    sTaskStackChangedCalled = true;
-                }
+                latch.countDown();
             }
         });
 
-        final Context context = getInstrumentation().getContext();
-        context.startActivity(new Intent(context, ResumeWhilePausingActivity.class).addFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK));
-        UiDevice.getInstance(getInstrumentation()).waitForIdle();
-
-        synchronized (sLock) {
-            assertTrue(sTaskStackChangedCalled);
-        }
+        startTestActivity(ResumeWhilePausingActivity.class);
+        waitForCallback(latch);
     }
 
     @Test
@@ -512,7 +493,7 @@
         try {
             final boolean result = latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
             if (!result) {
-                throw new RuntimeException("Timed out waiting for task stack change notification");
+                throw new AssertionError("Timed out waiting for task stack change notification");
             }
         } catch (InterruptedException e) {
         }
@@ -569,19 +550,6 @@
     }
 
     public static class ActivityA extends TestActivity {
-
-        private boolean mActivityBLaunched = false;
-
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            if (mActivityBLaunched) {
-                return;
-            }
-            mActivityBLaunched = true;
-            finish();
-            startActivity(new Intent(this, ActivityB.class));
-        }
     }
 
     public static class ActivityB extends TestActivity {
@@ -589,10 +557,6 @@
         @Override
         protected void onPostResume() {
             super.onPostResume();
-            synchronized (sLock) {
-                sTaskStackChangedCalled = false;
-            }
-            sActivityBResumed = true;
             finish();
         }
     }