Merge "Add role for Cluster Service in Automotive"
diff --git a/Android.bp b/Android.bp
index 0780f88..e756b34 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1415,3 +1415,30 @@
     name: "framework-telephony-jarjar-rules",
     srcs: ["telephony/framework-telephony-jarjar-rules.txt"],
 }
+
+// protolog start
+filegroup {
+    name: "protolog-common-src",
+    srcs: [
+        "core/java/com/android/internal/protolog/common/**/*.java",
+    ],
+}
+
+java_library {
+    name: "protolog-lib",
+    platform_apis: true,
+    srcs: [
+        "core/java/com/android/internal/protolog/ProtoLogImpl.java",
+        "core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java",
+        ":protolog-common-src",
+    ],
+}
+
+java_library {
+    name: "protolog-groups",
+    srcs: [
+        "core/java/com/android/internal/protolog/ProtoLogGroup.java",
+        ":protolog-common-src",
+    ],
+}
+// protolog end
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 0fe34fb..2bd5aee 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -311,15 +311,6 @@
     compile_dex: true,
 }
 
-java_defaults {
-    name: "android_stubs_dists_default",
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        tag: ".jar",
-        dest: "android.jar",
-    },
-}
-
 java_library_static {
     name: "android_monolith_stubs_current",
     srcs: [ ":api-stubs-docs" ],
@@ -355,21 +346,7 @@
     name: "android_system_monolith_stubs_current",
     srcs: [ ":system-api-stubs-docs" ],
     static_libs: [ "private-stub-annotations-jar" ],
-    defaults: [
-        "android_defaults_stubs_current",
-        "android_stubs_dists_default",
-    ],
-    dist: {
-        dir: "apistubs/android/system",
-    },
-    dists: [
-        {
-            // Legacy dist path
-            targets: ["sdk", "win_sdk"],
-            tag: ".jar",
-            dest: "android_system.jar",
-        },
-    ],
+    defaults: ["android_defaults_stubs_current"],
 }
 
 java_library_static {
@@ -401,34 +378,14 @@
     name: "android_test_stubs_current",
     srcs: [ ":test-api-stubs-docs" ],
     static_libs: [ "private-stub-annotations-jar" ],
-    defaults: [
-        "android_defaults_stubs_current",
-        "android_stubs_dists_default",
-    ],
-    dist: {
-        dir: "apistubs/android/test",
-    },
-    dists: [
-        {
-            // Legacy dist path
-            targets: ["sdk", "win_sdk"],
-            tag: ".jar",
-            dest: "android_test.jar",
-        },
-    ],
+    defaults: ["android_defaults_stubs_current"],
 }
 
 java_library_static {
     name: "android_module_lib_stubs_current",
     srcs: [ ":module-lib-api-stubs-docs-non-updatable" ],
-    defaults: [
-        "android_defaults_stubs_current",
-        "android_stubs_dists_default",
-    ],
+    defaults: ["android_defaults_stubs_current"],
     libs: ["sdk_system_29_android"],
-    dist: {
-        dir: "apistubs/android/module-lib",
-    },
 }
 
 java_library_static {
diff --git a/api/current.txt b/api/current.txt
index b70103b..69700f3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5596,6 +5596,7 @@
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
     method public int getSemanticAction();
+    method public boolean isAuthenticationRequired();
     method public boolean isContextual();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
@@ -5625,6 +5626,7 @@
     method @NonNull public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
     method @NonNull public android.os.Bundle getExtras();
     method @NonNull public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean);
+    method @NonNull public android.app.Notification.Action.Builder setAuthenticationRequired(boolean);
     method @NonNull public android.app.Notification.Action.Builder setContextual(boolean);
     method @NonNull public android.app.Notification.Action.Builder setSemanticAction(int);
   }
@@ -24065,6 +24067,8 @@
     method public boolean isSink();
     method public boolean isSource();
     field public static final int TYPE_AUX_LINE = 19; // 0x13
+    field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
+    field public static final int TYPE_BLE_SPEAKER = 27; // 0x1b
     field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
     field public static final int TYPE_BLUETOOTH_SCO = 7; // 0x7
     field public static final int TYPE_BUILTIN_EARPIECE = 1; // 0x1
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index f53ac8c..b587ea1 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -94,6 +94,7 @@
   }
 
   public interface Parcelable {
+    method public default int getStability();
     field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
     field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 1ac02c0..721523c9 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7142,6 +7142,8 @@
     field @Deprecated public static final int METERED_OVERRIDE_METERED = 1; // 0x1
     field @Deprecated public static final int METERED_OVERRIDE_NONE = 0; // 0x0
     field @Deprecated public static final int METERED_OVERRIDE_NOT_METERED = 2; // 0x2
+    field @Deprecated public static final int RANDOMIZATION_AUTO = 3; // 0x3
+    field @Deprecated public static final int RANDOMIZATION_ENHANCED = 2; // 0x2
     field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
     field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
     field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 1579715..7c41951 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -85,8 +85,9 @@
         "src/metrics/EventMetricProducer.cpp",
         "src/metrics/GaugeMetricProducer.cpp",
         "src/metrics/MetricProducer.cpp",
-        "src/metrics/metrics_manager_util.cpp",
         "src/metrics/MetricsManager.cpp",
+        "src/metrics/parsing_utils/config_update_utils.cpp",
+        "src/metrics/parsing_utils/metrics_manager_util.cpp",
         "src/metrics/ValueMetricProducer.cpp",
         "src/packages/UidMap.cpp",
         "src/shell/shell_config.proto",
@@ -322,6 +323,8 @@
         "tests/metrics/metrics_test_helper.cpp",
         "tests/metrics/OringDurationTracker_test.cpp",
         "tests/metrics/ValueMetricProducer_test.cpp",
+        "tests/metrics/parsing_utils/config_update_utils_test.cpp",
+        "tests/metrics/parsing_utils/metrics_manager_util_test.cpp",
         "tests/MetricsManager_test.cpp",
         "tests/shell/ShellSubscriber_test.cpp",
         "tests/state/StateTracker_test.cpp",
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 6327490..7bee4e2 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -546,7 +546,8 @@
         }
     } else {
         // Preserve the existing MetricsManager, update necessary components and metadata in place.
-        configValid = it->second->updateConfig(timestampNs, config);
+        configValid = it->second->updateConfig(config, mTimeBaseNs, timestampNs,
+                                               mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
         if (configValid) {
             // TODO(b/162323476): refresh TTL, ensure init() is handled properly.
             mUidMap->OnConfigUpdated(key);
diff --git a/cmds/statsd/src/hash.h b/cmds/statsd/src/hash.h
index cfe869f..bd6b0cd 100644
--- a/cmds/statsd/src/hash.h
+++ b/cmds/statsd/src/hash.h
@@ -22,6 +22,7 @@
 namespace os {
 namespace statsd {
 
+// Uses murmur2 hashing algorithm.
 extern uint32_t Hash32(const char *data, size_t n, uint32_t seed);
 extern uint64_t Hash64(const char* data, size_t n, uint64_t seed);
 
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
index b94a957..60bcc26 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -27,8 +27,9 @@
 using std::unordered_map;
 using std::vector;
 
-CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index)
-    : LogMatchingTracker(id, index) {
+CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index,
+                                                             const uint64_t protoHash)
+    : LogMatchingTracker(id, index, protoHash) {
 }
 
 CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
index 55bc4605..6b8a7fb 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -28,7 +28,7 @@
 // Represents a AtomMatcher_Combination in the StatsdConfig.
 class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
 public:
-    CombinationLogMatchingTracker(const int64_t& id, const int index);
+    CombinationLogMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash);
 
     bool init(const std::vector<AtomMatcher>& allLogMatchers,
               const std::vector<sp<LogMatchingTracker>>& allTrackers,
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index 88ab4e6..49a41ad 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -33,8 +33,8 @@
 
 class LogMatchingTracker : public virtual RefBase {
 public:
-    LogMatchingTracker(const int64_t& id, const int index)
-        : mId(id), mIndex(index), mInitialized(false){};
+    LogMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash)
+        : mId(id), mIndex(index), mInitialized(false), mProtoHash(protoHash){};
 
     virtual ~LogMatchingTracker(){};
 
@@ -69,10 +69,14 @@
         return mAtomIds;
     }
 
-    const int64_t& getId() const {
+    int64_t getId() const {
         return mId;
     }
 
+    uint64_t getProtoHash() const {
+        return mProtoHash;
+    }
+
 protected:
     // Name of this matching. We don't really need the name, but it makes log message easy to debug.
     const int64_t mId;
@@ -87,6 +91,14 @@
     // return kNotMatched when we receive an event with an id not in the list. This is especially
     // useful when we have a complex CombinationLogMatcherTracker.
     std::set<int> mAtomIds;
+
+    // Hash of the AtomMatcher's proto bytes from StatsdConfig.
+    // Used to determine if the definition of this matcher has changed across a config update.
+    const uint64_t mProtoHash;
+
+    FRIEND_TEST(MetricsManagerTest, TestCreateLogTrackerSimple);
+    FRIEND_TEST(MetricsManagerTest, TestCreateLogTrackerCombination);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
index 082daf5a..ff47d35 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -26,11 +26,11 @@
 using std::unordered_map;
 using std::vector;
 
-
 SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index,
+                                                   const uint64_t protoHash,
                                                    const SimpleAtomMatcher& matcher,
-                                                   const UidMap& uidMap)
-    : LogMatchingTracker(id, index), mMatcher(matcher), mUidMap(uidMap) {
+                                                   const sp<UidMap>& uidMap)
+    : LogMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) {
     if (!matcher.has_atom_id()) {
         mInitialized = false;
     } else {
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index a0f6a88..e58e012 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -29,9 +29,8 @@
 
 class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
 public:
-    SimpleLogMatchingTracker(const int64_t& id, const int index,
-                             const SimpleAtomMatcher& matcher,
-                             const UidMap& uidMap);
+    SimpleLogMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash,
+                             const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap);
 
     ~SimpleLogMatchingTracker();
 
@@ -46,7 +45,7 @@
 
 private:
     const SimpleAtomMatcher mMatcher;
-    const UidMap& mUidMap;
+    const sp<UidMap> mUidMap;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 2b4c6a3..e6d9122 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -81,14 +81,15 @@
     return matched;
 }
 
-bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
+bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
+                    const string& str_match) {
     if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
         int uid = fieldValue.mValue.int_value;
         auto aidIt = UidMap::sAidToUidMapping.find(str_match);
         if (aidIt != UidMap::sAidToUidMapping.end()) {
             return ((int)aidIt->second) == uid;
         }
-        std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
+        std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
         return packageNames.find(str_match) != packageNames.end();
     } else if (fieldValue.mValue.getType() == STRING) {
         return fieldValue.mValue.str_value == str_match;
@@ -96,7 +97,7 @@
     return false;
 }
 
-bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
+bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
                    const vector<FieldValue>& values, int start, int end, int depth) {
     if (depth > 2) {
         ALOGE("Depth > 3 not supported");
@@ -353,7 +354,7 @@
     }
 }
 
-bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
+bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
                    const LogEvent& event) {
     if (event.GetTagId() != simpleMatcher.atom_id()) {
         return false;
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
index 1ab3e87..130b606 100644
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -36,8 +36,8 @@
 bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
                       const std::vector<MatchingState>& matcherResults);
 
-bool matchesSimple(const UidMap& uidMap,
-    const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
+bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
+                   const LogEvent& wrapper);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 189d811..2c3deca 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -26,7 +26,8 @@
 #include "guardrail/StatsdStats.h"
 #include "matchers/CombinationLogMatchingTracker.h"
 #include "matchers/SimpleLogMatchingTracker.h"
-#include "metrics_manager_util.h"
+#include "parsing_utils/config_update_utils.h"
+#include "parsing_utils/metrics_manager_util.h"
 #include "state/StateManager.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
@@ -76,13 +77,14 @@
     // Init the ttl end timestamp.
     refreshTtl(timeBaseNs);
 
-    mConfigValid = initStatsdConfig(
-            key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
-            mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
-            mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
-            mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
-            mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
+    mConfigValid =
+            initStatsdConfig(key, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                             periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIds,
+                             mAllAtomMatchers, mLogTrackerMap, mAllConditionTrackers,
+                             mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
+                             mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
+                             mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
+                             mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
 
     mHashStringsInReport = config.hash_strings_in_metric_report();
     mVersionStringsInReport = config.version_strings_in_metric_report();
@@ -195,7 +197,19 @@
     VLOG("~MetricsManager()");
 }
 
-bool MetricsManager::updateConfig(const int64_t currentTimeNs, const StatsdConfig& config) {
+bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
+                                  const int64_t currentTimeNs,
+                                  const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                                  const sp<AlarmMonitor>& periodicAlarmMonitor) {
+    vector<sp<LogMatchingTracker>> newAtomMatchers;
+    unordered_map<int64_t, int> newLogTrackerMap;
+    mTagIds.clear();
+    mConfigValid =
+            updateStatsdConfig(mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor,
+                               periodicAlarmMonitor, timeBaseNs, currentTimeNs, mAllAtomMatchers,
+                               mLogTrackerMap, mTagIds, newAtomMatchers, newLogTrackerMap);
+    mAllAtomMatchers = newAtomMatchers;
+    mLogTrackerMap = newLogTrackerMap;
     return mConfigValid;
 }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 042de29..6f9a2336 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -46,7 +46,9 @@
 
     virtual ~MetricsManager();
 
-    bool updateConfig(const int64_t currentTimeNs, const StatsdConfig& config);
+    bool updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
+                      const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor);
 
     // Return whether the configuration is valid.
     bool isConfigValid() const;
@@ -237,6 +239,12 @@
     // Hold all periodic alarm trackers.
     std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers;
 
+    // To make updating configs faster, we map the id of a LogMatchingTracker, MetricProducer, and
+    // ConditionTracker to its index in the corresponding vector.
+
+    // Maps the id of an atom matcher to its index in mAllAtomMatchers.
+    std::unordered_map<int64_t, int> mLogTrackerMap;
+
     // To make the log processing more efficient, we want to do as much filtering as possible
     // before we go into individual trackers and conditions to match.
 
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
new file mode 100644
index 0000000..562e291
--- /dev/null
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+
+#include "config_update_utils.h"
+
+#include "external/StatsPullerManager.h"
+#include "hash.h"
+#include "metrics_manager_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate.
+// Returns whether the function was successful or not.
+bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx,
+                                  const unordered_map<int64_t, int>& oldLogTrackerMap,
+                                  const vector<sp<LogMatchingTracker>>& oldAtomMatchers,
+                                  const unordered_map<int64_t, int>& newLogTrackerMap,
+                                  vector<UpdateStatus>& matchersToUpdate,
+                                  vector<bool>& cycleTracker) {
+    // Have already examined this matcher.
+    if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) {
+        return true;
+    }
+
+    const AtomMatcher& matcher = config.atom_matcher(matcherIdx);
+    int64_t id = matcher.id();
+    // Check if new matcher.
+    const auto& oldLogTrackerIt = oldLogTrackerMap.find(id);
+    if (oldLogTrackerIt == oldLogTrackerMap.end()) {
+        matchersToUpdate[matcherIdx] = UPDATE_REPLACE;
+        return true;
+    }
+
+    // This is an existing matcher. Check if it has changed.
+    string serializedMatcher;
+    if (!matcher.SerializeToString(&serializedMatcher)) {
+        ALOGE("Unable to serialize matcher %lld", (long long)id);
+        return false;
+    }
+    uint64_t newProtoHash = Hash64(serializedMatcher);
+    if (newProtoHash != oldAtomMatchers[oldLogTrackerIt->second]->getProtoHash()) {
+        matchersToUpdate[matcherIdx] = UPDATE_REPLACE;
+        return true;
+    }
+
+    switch (matcher.contents_case()) {
+        case AtomMatcher::ContentsCase::kSimpleAtomMatcher: {
+            matchersToUpdate[matcherIdx] = UPDATE_PRESERVE;
+            return true;
+        }
+        case AtomMatcher::ContentsCase::kCombination: {
+            // Recurse to check if children have changed.
+            cycleTracker[matcherIdx] = true;
+            UpdateStatus status = UPDATE_PRESERVE;
+            for (const int64_t childMatcherId : matcher.combination().matcher()) {
+                const auto& childIt = newLogTrackerMap.find(childMatcherId);
+                if (childIt == newLogTrackerMap.end()) {
+                    ALOGW("Matcher %lld not found in the config", (long long)childMatcherId);
+                    return false;
+                }
+                const int childIdx = childIt->second;
+                if (cycleTracker[childIdx]) {
+                    ALOGE("Cycle detected in matcher config");
+                    return false;
+                }
+                if (!determineMatcherUpdateStatus(config, childIdx, oldLogTrackerMap,
+                                                  oldAtomMatchers, newLogTrackerMap,
+                                                  matchersToUpdate, cycleTracker)) {
+                    return false;
+                }
+
+                if (matchersToUpdate[childIdx] == UPDATE_REPLACE) {
+                    status = UPDATE_REPLACE;
+                    break;
+                }
+            }
+            matchersToUpdate[matcherIdx] = status;
+            cycleTracker[matcherIdx] = false;
+            return true;
+        }
+        default: {
+            ALOGE("Matcher \"%lld\" malformed", (long long)id);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool updateLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                       const unordered_map<int64_t, int>& oldLogTrackerMap,
+                       const vector<sp<LogMatchingTracker>>& oldAtomMatchers, set<int>& allTagIds,
+                       unordered_map<int64_t, int>& newLogTrackerMap,
+                       vector<sp<LogMatchingTracker>>& newAtomMatchers) {
+    const int atomMatcherCount = config.atom_matcher_size();
+
+    vector<AtomMatcher> matcherProtos;
+    matcherProtos.reserve(atomMatcherCount);
+    newAtomMatchers.reserve(atomMatcherCount);
+
+    // Maps matcher id to their position in the config. For fast lookup of dependencies.
+    for (int i = 0; i < atomMatcherCount; i++) {
+        const AtomMatcher& matcher = config.atom_matcher(i);
+        if (newLogTrackerMap.find(matcher.id()) != newLogTrackerMap.end()) {
+            ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id());
+            return false;
+        }
+        newLogTrackerMap[matcher.id()] = i;
+        matcherProtos.push_back(matcher);
+    }
+
+    // For combination matchers, we need to determine if any children need to be updated.
+    vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(atomMatcherCount, false);
+    for (int i = 0; i < atomMatcherCount; i++) {
+        if (!determineMatcherUpdateStatus(config, i, oldLogTrackerMap, oldAtomMatchers,
+                                          newLogTrackerMap, matchersToUpdate, cycleTracker)) {
+            return false;
+        }
+    }
+
+    for (int i = 0; i < atomMatcherCount; i++) {
+        const AtomMatcher& matcher = config.atom_matcher(i);
+        const int64_t id = matcher.id();
+        switch (matchersToUpdate[i]) {
+            case UPDATE_PRESERVE: {
+                const auto& oldLogTrackerIt = oldLogTrackerMap.find(id);
+                if (oldLogTrackerIt == oldLogTrackerMap.end()) {
+                    ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it "
+                          "to be there",
+                          (long long)id);
+                    return false;
+                }
+                const int oldIndex = oldLogTrackerIt->second;
+                newAtomMatchers.push_back(oldAtomMatchers[oldIndex]);
+                break;
+            }
+            case UPDATE_REPLACE: {
+                sp<LogMatchingTracker> tracker = createLogTracker(matcher, i, uidMap);
+                if (tracker == nullptr) {
+                    return false;
+                }
+                newAtomMatchers.push_back(tracker);
+                break;
+            }
+            default: {
+                ALOGE("Matcher \"%lld\" update state is unknown. This should never happen",
+                      (long long)id);
+                return false;
+            }
+        }
+    }
+
+    std::fill(cycleTracker.begin(), cycleTracker.end(), false);
+    for (auto& matcher : newAtomMatchers) {
+        if (!matcher->init(matcherProtos, newAtomMatchers, newLogTrackerMap, cycleTracker)) {
+            return false;
+        }
+        // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+        const set<int>& tagIds = matcher->getAtomIds();
+        allTagIds.insert(tagIds.begin(), tagIds.end());
+    }
+
+    return true;
+}
+
+bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+                        const sp<StatsPullerManager>& pullerManager,
+                        const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                        const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                        const int64_t currentTimeNs,
+                        const vector<sp<LogMatchingTracker>>& oldAtomMatchers,
+                        const unordered_map<int64_t, int>& oldLogTrackerMap, set<int>& allTagIds,
+                        vector<sp<LogMatchingTracker>>& newAtomMatchers,
+                        unordered_map<int64_t, int>& newLogTrackerMap) {
+    if (!updateLogTrackers(config, uidMap, oldLogTrackerMap, oldAtomMatchers, allTagIds,
+                           newLogTrackerMap, newAtomMatchers)) {
+        ALOGE("updateLogMatchingTrackers failed");
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
new file mode 100644
index 0000000..951ab03
--- /dev/null
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "anomaly/AlarmMonitor.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/LogMatchingTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for MetricsManager to update itself from a new StatsdConfig.
+// *Note*: only updateStatsdConfig() should be called from outside this file.
+// All other functions are intermediate steps, created to make unit testing easier.
+
+// Possible update states for a component. PRESERVE means we should keep the existing one.
+// REPLACE means we should create a new one, either because it didn't exist or it changed.
+enum UpdateStatus {
+    UPDATE_UNKNOWN = 0,
+    UPDATE_PRESERVE = 1,
+    UPDATE_REPLACE = 2,
+};
+
+// Recursive function to determine if a matcher needs to be updated.
+// input:
+// [config]: the input StatsdConfig
+// [matcherIdx]: the index of the current matcher to be updated
+// [newLogTrackerMap]: matcher id to index mapping in the input StatsdConfig
+// [oldLogTrackerMap]: matcher id to index mapping in the existing MetricsManager
+// [oldAtomMatchers]: stores the existing LogMatchingTrackers
+// output:
+// [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will
+//                     be updated from UPDATE_UNKNOWN after this call.
+// [cycleTracker]: intermediate param used during recursion.
+bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx,
+                                  const unordered_map<int64_t, int>& oldLogTrackerMap,
+                                  const vector<sp<LogMatchingTracker>>& oldAtomMatchers,
+                                  const unordered_map<int64_t, int>& newLogTrackerMap,
+                                  vector<UpdateStatus>& matchersToUpdate,
+                                  vector<bool>& cycleTracker);
+
+// Updates the LogMatchingTrackers.
+// input:
+// [config]: the input StatsdConfig
+// [oldLogTrackerMap]: existing matcher id to index mapping
+// [oldAtomMatchers]: stores the existing LogMatchingTrackers
+// output:
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+// [newLogTrackerMap]: new matcher id to index mapping
+// [newAtomMatchers]: stores the new LogMatchingTrackers
+bool updateLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                       const unordered_map<int64_t, int>& oldLogTrackerMap,
+                       const vector<sp<LogMatchingTracker>>& oldAtomMatchers, set<int>& allTagIds,
+                       unordered_map<int64_t, int>& newLogTrackerMap,
+                       vector<sp<LogMatchingTracker>>& newAtomMatchers);
+
+// Updates the existing MetricsManager from a new StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+                        const sp<StatsPullerManager>& pullerManager,
+                        const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                        const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                        const int64_t currentTimeNs,
+                        const std::vector<sp<LogMatchingTracker>>& oldAtomMatchers,
+                        const unordered_map<int64_t, int>& oldLogTrackerMap,
+                        std::set<int>& allTagIds,
+                        std::vector<sp<LogMatchingTracker>>& newAtomMatchers,
+                        unordered_map<int64_t, int>& newLogTrackerMap);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
similarity index 90%
rename from cmds/statsd/src/metrics/metrics_manager_util.cpp
rename to cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index 8917c36..52ef95d 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -22,10 +22,10 @@
 #include <inttypes.h>
 
 #include "FieldValue.h"
-#include "MetricProducer.h"
 #include "condition/CombinationConditionTracker.h"
 #include "condition/SimpleConditionTracker.h"
 #include "external/StatsPullerManager.h"
+#include "hash.h"
 #include "matchers/CombinationLogMatchingTracker.h"
 #include "matchers/EventMatcherWizard.h"
 #include "matchers/SimpleLogMatchingTracker.h"
@@ -33,6 +33,7 @@
 #include "metrics/DurationMetricProducer.h"
 #include "metrics/EventMetricProducer.h"
 #include "metrics/GaugeMetricProducer.h"
+#include "metrics/MetricProducer.h"
 #include "metrics/ValueMetricProducer.h"
 #include "state/StateManager.h"
 #include "stats_util.h"
@@ -61,6 +62,28 @@
 
 }  // namespace
 
+sp<LogMatchingTracker> createLogTracker(const AtomMatcher& logMatcher, const int index,
+                                        const sp<UidMap>& uidMap) {
+    string serializedMatcher;
+    if (!logMatcher.SerializeToString(&serializedMatcher)) {
+        ALOGE("Unable to serialize matcher %lld", (long long)logMatcher.id());
+        return nullptr;
+    }
+    uint64_t protoHash = Hash64(serializedMatcher);
+    switch (logMatcher.contents_case()) {
+        case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
+            return new SimpleLogMatchingTracker(logMatcher.id(), index, protoHash,
+                                                logMatcher.simple_atom_matcher(), uidMap);
+            break;
+        case AtomMatcher::ContentsCase::kCombination:
+            return new CombinationLogMatchingTracker(logMatcher.id(), index, protoHash);
+            break;
+        default:
+            ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
+            return nullptr;
+    }
+}
+
 bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex,
                                  const bool usedForDimension,
                                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
@@ -184,9 +207,7 @@
 //      to provide the producer with state about its activators and deactivators.
 // Returns false if there are errors.
 bool handleMetricActivation(
-        const StatsdConfig& config,
-        const int64_t metricId,
-        const int metricIndex,
+        const StatsdConfig& config, const int64_t metricId, const int metricIndex,
         const unordered_map<int64_t, int>& metricToActivationMap,
         const unordered_map<int64_t, int>& logTrackerMap,
         unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
@@ -210,10 +231,11 @@
             return false;
         }
 
-        ActivationType activationType = (activation.has_activation_type()) ?
-                activation.activation_type() : metricActivation.activation_type();
-        std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
-                activationType, activation.ttl_seconds() * NS_PER_SEC);
+        ActivationType activationType = (activation.has_activation_type())
+                                                ? activation.activation_type()
+                                                : metricActivation.activation_type();
+        std::shared_ptr<Activation> activationWrapper =
+                std::make_shared<Activation>(activationType, activation.ttl_seconds() * NS_PER_SEC);
 
         int atomMatcherIndex = itr->second;
         activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
@@ -235,7 +257,7 @@
     return true;
 }
 
-bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
+bool initLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
                      unordered_map<int64_t, int>& logTrackerMap,
                      vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
     vector<AtomMatcher> matcherConfigs;
@@ -245,22 +267,12 @@
 
     for (int i = 0; i < atomMatcherCount; i++) {
         const AtomMatcher& logMatcher = config.atom_matcher(i);
-
         int index = allAtomMatchers.size();
-        switch (logMatcher.contents_case()) {
-            case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
-                allAtomMatchers.push_back(new SimpleLogMatchingTracker(
-                        logMatcher.id(), index, logMatcher.simple_atom_matcher(), uidMap));
-                break;
-            case AtomMatcher::ContentsCase::kCombination:
-                allAtomMatchers.push_back(
-                        new CombinationLogMatchingTracker(logMatcher.id(), index));
-                break;
-            default:
-                ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
-                return false;
-                // continue;
+        sp<LogMatchingTracker> tracker = createLogTracker(logMatcher, index, uidMap);
+        if (tracker == nullptr) {
+            return false;
         }
+        allAtomMatchers.push_back(tracker);
         if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) {
             ALOGE("Duplicate AtomMatcher found!");
             return false;
@@ -383,7 +395,7 @@
         const MetricActivation& metricActivation = config.metric_activation(i);
         int64_t metricId = metricActivation.metric_id();
         if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
-            ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
+            ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
             return false;
         }
         metricToActivationMap.insert({metricId, i});
@@ -402,9 +414,8 @@
         metricMap.insert({metric.id(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
-                                         metric.has_dimensions_in_what(),
-                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
-                                         trackerIndex)) {
+                                         metric.has_dimensions_in_what(), allAtomMatchers,
+                                         logTrackerMap, trackerToMetricMap, trackerIndex)) {
             return false;
         }
 
@@ -438,10 +449,10 @@
 
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
+        bool success = handleMetricActivation(
+                config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap);
         if (!success) return false;
 
         sp<MetricProducer> countProducer =
@@ -544,10 +555,10 @@
 
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
+        bool success = handleMetricActivation(
+                config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap);
         if (!success) return false;
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
@@ -591,10 +602,10 @@
 
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
+        bool success = handleMetricActivation(
+                config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap);
         if (!success) return false;
 
         sp<MetricProducer> eventMetric =
@@ -626,9 +637,8 @@
         metricMap.insert({metric.id(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
-                                         metric.has_dimensions_in_what(),
-                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
-                                         trackerIndex)) {
+                                         metric.has_dimensions_in_what(), allAtomMatchers,
+                                         logTrackerMap, trackerToMetricMap, trackerIndex)) {
             return false;
         }
 
@@ -718,9 +728,8 @@
         metricMap.insert({metric.id(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
-                                         metric.has_dimensions_in_what(),
-                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
-                                         trackerIndex)) {
+                                         metric.has_dimensions_in_what(), allAtomMatchers,
+                                         logTrackerMap, trackerToMetricMap, trackerIndex)) {
             return false;
         }
 
@@ -775,10 +784,10 @@
 
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
+        bool success = handleMetricActivation(
+                config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap);
         if (!success) return false;
 
         sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
@@ -813,8 +822,7 @@
     return true;
 }
 
-bool initAlerts(const StatsdConfig& config,
-                const unordered_map<int64_t, int>& metricProducerMap,
+bool initAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap,
                 unordered_map<int64_t, int>& alertTrackerMap,
                 const sp<AlarmMonitor>& anomalyAlarmMonitor,
                 vector<sp<MetricProducer>>& allMetricProducers,
@@ -832,8 +840,8 @@
             return false;
         }
         if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
-            ALOGW("invalid alert: threshold=%f num_buckets= %d",
-                  alert.trigger_if_sum_gt(), alert.num_buckets());
+            ALOGW("invalid alert: threshold=%f num_buckets= %d", alert.trigger_if_sum_gt(),
+                  alert.num_buckets());
             return false;
         }
         const int metricIndex = itr->second;
@@ -853,14 +861,13 @@
         }
         if (subscription.subscriber_information_case() ==
             Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
-            ALOGW("subscription \"%lld\" has no subscriber info.\"",
-                (long long)subscription.id());
+            ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id());
             return false;
         }
         const auto& itr = alertTrackerMap.find(subscription.rule_id());
         if (itr == alertTrackerMap.end()) {
             ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
-                (long long)subscription.id(), (long long)subscription.rule_id());
+                  (long long)subscription.id(), (long long)subscription.rule_id());
             return false;
         }
         const int anomalyTrackerIndex = itr->second;
@@ -870,12 +877,11 @@
 }
 
 bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
-                const sp<AlarmMonitor>& periodicAlarmMonitor,
-                const int64_t timeBaseNs, const int64_t currentTimeNs,
-                vector<sp<AlarmTracker>>& allAlarmTrackers) {
+                const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                const int64_t currentTimeNs, vector<sp<AlarmTracker>>& allAlarmTrackers) {
     unordered_map<int64_t, int> alarmTrackerMap;
     int64_t startMillis = timeBaseNs / 1000 / 1000;
-    int64_t currentTimeMillis = currentTimeNs / 1000 /1000;
+    int64_t currentTimeMillis = currentTimeNs / 1000 / 1000;
     for (int i = 0; i < config.alarm_size(); i++) {
         const Alarm& alarm = config.alarm(i);
         if (alarm.offset_millis() <= 0) {
@@ -888,8 +894,7 @@
         }
         alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
         allAlarmTrackers.push_back(
-            new AlarmTracker(startMillis, currentTimeMillis,
-                             alarm, key, periodicAlarmMonitor));
+                new AlarmTracker(startMillis, currentTimeMillis, alarm, key, periodicAlarmMonitor));
     }
     for (int i = 0; i < config.subscription_size(); ++i) {
         const Subscription& subscription = config.subscription(i);
@@ -898,14 +903,13 @@
         }
         if (subscription.subscriber_information_case() ==
             Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
-            ALOGW("subscription \"%lld\" has no subscriber info.\"",
-                (long long)subscription.id());
+            ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id());
             return false;
         }
         const auto& itr = alarmTrackerMap.find(subscription.rule_id());
         if (itr == alarmTrackerMap.end()) {
             ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
-                (long long)subscription.id(), (long long)subscription.rule_id());
+                  (long long)subscription.id(), (long long)subscription.rule_id());
             return false;
         }
         const int trackerIndex = itr->second;
@@ -914,12 +918,13 @@
     return true;
 }
 
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
                       const sp<StatsPullerManager>& pullerManager,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
                       const int64_t currentTimeNs, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
+                      unordered_map<int64_t, int>& logTrackerMap,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
@@ -930,9 +935,7 @@
                       unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
                       unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
                       unordered_map<int64_t, int>& alertTrackerMap,
-                      vector<int>& metricsWithActivation,
-                      std::set<int64_t>& noReportMetricIds) {
-    unordered_map<int64_t, int> logTrackerMap;
+                      vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) {
     unordered_map<int64_t, int> conditionTrackerMap;
     vector<ConditionState> initialConditionCache;
     unordered_map<int64_t, int> metricProducerMap;
@@ -969,8 +972,8 @@
         ALOGE("initAlerts failed");
         return false;
     }
-    if (!initAlarms(config, key, periodicAlarmMonitor,
-                    timeBaseNs, currentTimeNs, allPeriodicAlarmTrackers)) {
+    if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+                    allPeriodicAlarmTrackers)) {
         ALOGE("initAlarms failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
similarity index 86%
rename from cmds/statsd/src/metrics/metrics_manager_util.h
rename to cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index 96b5c26..ed9951f 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -20,16 +20,28 @@
 #include <unordered_map>
 #include <vector>
 
-#include "../anomaly/AlarmTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/LogMatchingTracker.h"
-#include "../metrics/MetricProducer.h"
+#include "anomaly/AlarmTracker.h"
+#include "condition/ConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/LogMatchingTracker.h"
+#include "metrics/MetricProducer.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
+// Helper functions for creating individual config components from StatsdConfig.
+// Should only be called from metrics_manager_util and config_update_utils.
+
+// Create a LogMatchingTracker.
+// input:
+// [logMatcher]: the input AtomMatcher from the StatsdConfig
+// [index]: the index of the matcher
+// output:
+// new LogMatchingTracker, or null if the tracker is unable to be created
+sp<LogMatchingTracker> createLogTracker(const AtomMatcher& logMatcher, const int index,
+                                        const sp<UidMap>& uidMap);
+
 // Helper functions for MetricsManager to initialize from StatsdConfig.
 // *Note*: only initStatsdConfig() should be called from outside.
 // All other functions are intermediate
@@ -44,8 +56,7 @@
 // [logTrackerMap]: this map should contain matcher name to index mapping
 // [allAtomMatchers]: should store the sp to all the LogMatchingTracker
 // [allTagIds]: contains the set of all interesting tag ids to this config.
-bool initLogTrackers(const StatsdConfig& config,
-                     const UidMap& uidMap,
+bool initLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
                      std::unordered_map<int64_t, int>& logTrackerMap,
                      std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                      std::set<int>& allTagIds);
@@ -97,7 +108,7 @@
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 bool initMetrics(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
-        const int64_t currentTimeNs, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
         const std::unordered_map<int64_t, int>& logTrackerMap,
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
@@ -116,12 +127,13 @@
 
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
                       const sp<StatsPullerManager>& pullerManager,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
                       const int64_t currentTimeNs, std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
+                      std::unordered_map<int64_t, int>& logTrackerMap,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
@@ -132,8 +144,7 @@
                       unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
                       unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
                       std::unordered_map<int64_t, int>& alertTrackerMap,
-                      vector<int>& metricsWithActivation,
-                      std::set<int64_t>& noReportMetricIds);
+                      vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index fd883c2..9d8f0c2 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -191,7 +191,7 @@
     mProto.clear();
     int count = 0;
     for (const auto& event : data) {
-        if (matchesSimple(*mUidMap, matcher, *event)) {
+        if (matchesSimple(mUidMap, matcher, *event)) {
             count++;
             uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
                                               util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
@@ -209,7 +209,7 @@
 
     mProto.clear();
     for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) {
-        if (matchesSimple(*mUidMap, matcher, event)) {
+        if (matchesSimple(mUidMap, matcher, event)) {
             uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
                                               util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
             event.ToProto(mProto);
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 6264c07..92cd04f 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -110,7 +110,7 @@
 }  // anonymous namespace
 
 TEST(AtomMatcherTest, TestSimpleMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
 
     // Set up the matcher
     AtomMatcher matcher;
@@ -129,7 +129,7 @@
 }
 
 TEST(AtomMatcherTest, TestAttributionMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     std::vector<int> attributionUids = {1111, 2222, 3333};
     std::vector<string> attributionTags = {"location1", "location2", "location3"};
 
@@ -204,7 +204,7 @@
             "pkg0");
     EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
-    uidMap.updateMap(
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -356,8 +356,8 @@
 }
 
 TEST(AtomMatcherTest, TestUidFieldMatcher) {
-    UidMap uidMap;
-    uidMap.updateMap(
+    sp<UidMap> uidMap = new UidMap();
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -392,8 +392,8 @@
 }
 
 TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
-    UidMap uidMap;
-    uidMap.updateMap(
+    sp<UidMap> uidMap = new UidMap();
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -453,8 +453,8 @@
 }
 
 TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
-    UidMap uidMap;
-    uidMap.updateMap(
+    sp<UidMap> uidMap = new UidMap();
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -517,7 +517,7 @@
 }
 
 TEST(AtomMatcherTest, TestBoolMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -550,7 +550,7 @@
 }
 
 TEST(AtomMatcherTest, TestStringMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -568,7 +568,7 @@
 }
 
 TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -597,7 +597,7 @@
 }
 
 TEST(AtomMatcherTest, TestIntComparisonMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -654,7 +654,7 @@
 }
 
 TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 6259757..8dd6083 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -28,7 +28,7 @@
 #include "src/metrics/GaugeMetricProducer.h"
 #include "src/metrics/MetricProducer.h"
 #include "src/metrics/ValueMetricProducer.h"
-#include "src/metrics/metrics_manager_util.h"
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
 #include "src/state/StateManager.h"
 #include "statsd_test_util.h"
 
@@ -48,7 +48,6 @@
 
 namespace {
 const ConfigKey kConfigKey(0, 12345);
-const long kAlertId = 3;
 
 const long timeBaseSec = 1000;
 
@@ -90,287 +89,6 @@
     metric->set_bucket(ONE_MINUTE);
     metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
     metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
-    config.add_no_report_metric(3);
-
-    auto alert = config.add_alert();
-    alert->set_id(kAlertId);
-    alert->set_metric_id(3);
-    alert->set_num_buckets(10);
-    alert->set_refractory_period_secs(100);
-    alert->set_trigger_if_sum_gt(100);
-    return config;
-}
-
-StatsdConfig buildCircleMatchers() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
-    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher(StringToId("SCREEN_IS_ON"));
-    // Circle dependency
-    combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
-
-    return config;
-}
-
-StatsdConfig buildAlertWithUnknownMetric() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    CountMetric* metric = config.add_count_metric();
-    metric->set_id(3);
-    metric->set_what(StringToId("SCREEN_IS_ON"));
-    metric->set_bucket(ONE_MINUTE);
-    metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
-    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
-    auto alert = config.add_alert();
-    alert->set_id(3);
-    alert->set_metric_id(2);
-    alert->set_num_buckets(10);
-    alert->set_refractory_period_secs(100);
-    alert->set_trigger_if_sum_gt(100);
-    return config;
-}
-
-StatsdConfig buildMissingMatchers() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
-    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher(StringToId("SCREEN_IS_ON"));
-    // undefined matcher
-    combination->add_matcher(StringToId("ABC"));
-
-    return config;
-}
-
-StatsdConfig buildMissingPredicate() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    CountMetric* metric = config.add_count_metric();
-    metric->set_id(3);
-    metric->set_what(StringToId("SCREEN_EVENT"));
-    metric->set_bucket(ONE_MINUTE);
-    metric->set_condition(StringToId("SOME_CONDITION"));
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_EVENT"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2);
-
-    return config;
-}
-
-StatsdConfig buildDimensionMetricsWithMultiTags() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(3);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("BATTERY_LOW"));
-
-    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
-    combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
-
-    // Count process state changes, slice by uid, while SCREEN_IS_OFF
-    CountMetric* metric = config.add_count_metric();
-    metric->set_id(3);
-    metric->set_what(StringToId("BATTERY_LOW"));
-    metric->set_bucket(ONE_MINUTE);
-    // This case is interesting. We want to dimension across two atoms.
-    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
-    auto alert = config.add_alert();
-    alert->set_id(kAlertId);
-    alert->set_metric_id(3);
-    alert->set_num_buckets(10);
-    alert->set_refractory_period_secs(100);
-    alert->set_trigger_if_sum_gt(100);
-    return config;
-}
-
-StatsdConfig buildCirclePredicates() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
-    auto condition = config.add_predicate();
-    condition->set_id(StringToId("SCREEN_IS_ON"));
-    SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
-    simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
-    simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
-
-    condition = config.add_predicate();
-    condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
-    Predicate_Combination* combination = condition->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_predicate(StringToId("SCREEN_IS_ON"));
-    combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
-    return config;
-}
-
-StatsdConfig buildConfigWithDifferentPredicates() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    auto pulledAtomMatcher =
-            CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE);
-    *config.add_atom_matcher() = pulledAtomMatcher;
-    auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = screenOnAtomMatcher;
-    auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher();
-    *config.add_atom_matcher() = screenOffAtomMatcher;
-    auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher();
-    *config.add_atom_matcher() = batteryNoneAtomMatcher;
-    auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher();
-    *config.add_atom_matcher() = batteryUsbAtomMatcher;
-
-    // Simple condition with InitialValue set to default (unknown).
-    auto screenOnUnknownPredicate = CreateScreenIsOnPredicate();
-    *config.add_predicate() = screenOnUnknownPredicate;
-
-    // Simple condition with InitialValue set to false.
-    auto screenOnFalsePredicate = config.add_predicate();
-    screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse"));
-    SimplePredicate* simpleScreenOnFalsePredicate =
-            screenOnFalsePredicate->mutable_simple_predicate();
-    simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id());
-    simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id());
-    simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
-    // Simple condition with InitialValue set to false.
-    auto onBatteryFalsePredicate = config.add_predicate();
-    onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse"));
-    SimplePredicate* simpleOnBatteryFalsePredicate =
-            onBatteryFalsePredicate->mutable_simple_predicate();
-    simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id());
-    simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id());
-    simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
-    // Combination condition with both simple condition InitialValues set to false.
-    auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate();
-    screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse"));
-    screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation(
-            LogicalOperation::AND);
-    addPredicateToPredicateCombination(*screenOnFalsePredicate,
-                                       screenOnFalseOnBatteryFalsePredicate);
-    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
-                                       screenOnFalseOnBatteryFalsePredicate);
-
-    // Combination condition with one simple condition InitialValue set to unknown and one set to
-    // false.
-    auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate();
-    screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse"));
-    screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation(
-            LogicalOperation::AND);
-    addPredicateToPredicateCombination(screenOnUnknownPredicate,
-                                       screenOnUnknownOnBatteryFalsePredicate);
-    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
-                                       screenOnUnknownOnBatteryFalsePredicate);
-
-    // Simple condition metric with initial value false.
-    ValueMetric* metric1 = config.add_value_metric();
-    metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse"));
-    metric1->set_what(pulledAtomMatcher.id());
-    *metric1->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric1->set_bucket(FIVE_MINUTES);
-    metric1->set_condition(screenOnFalsePredicate->id());
-
-    // Simple condition metric with initial value unknown.
-    ValueMetric* metric2 = config.add_value_metric();
-    metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown"));
-    metric2->set_what(pulledAtomMatcher.id());
-    *metric2->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric2->set_bucket(FIVE_MINUTES);
-    metric2->set_condition(screenOnUnknownPredicate.id());
-
-    // Combination condition metric with initial values false and false.
-    ValueMetric* metric3 = config.add_value_metric();
-    metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse"));
-    metric3->set_what(pulledAtomMatcher.id());
-    *metric3->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric3->set_bucket(FIVE_MINUTES);
-    metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id());
-
-    // Combination condition metric with initial values unknown and false.
-    ValueMetric* metric4 = config.add_value_metric();
-    metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse"));
-    metric4->set_what(pulledAtomMatcher.id());
-    *metric4->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric4->set_bucket(FIVE_MINUTES);
-    metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id());
-
     return config;
 }
 
@@ -379,274 +97,6 @@
 }
 }  // anonymous namespace
 
-TEST(MetricsManagerTest, TestInitialConditions) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildConfigWithDifferentPredicates();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_TRUE(initStatsdConfig(
-            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers,
-            allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-            trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap,
-            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
-            noReportMetricIds));
-    ASSERT_EQ(4u, allMetricProducers.size());
-    ASSERT_EQ(5u, allConditionTrackers.size());
-
-    ConditionKey queryKey;
-    vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
-
-    allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
-    allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
-    EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[1]);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[2]);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[3]);
-    EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]);
-
-    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition);
-    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition);
-    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition);
-    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition);
-}
-
-TEST(MetricsManagerTest, TestGoodConfig) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildGoodConfig();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                 periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                 allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                 allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                 trackerToMetricMap, trackerToConditionMap,
-                                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                 alertTrackerMap, metricsWithActivation,
-                                 noReportMetricIds));
-    ASSERT_EQ(1u, allMetricProducers.size());
-    ASSERT_EQ(1u, allAnomalyTrackers.size());
-    ASSERT_EQ(1u, noReportMetricIds.size());
-    ASSERT_EQ(1u, alertTrackerMap.size());
-    EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
-    EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
-}
-
-TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildDimensionMetricsWithMultiTags();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildCircleMatchers();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingMatchers) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildMissingMatchers();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingPredicate) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildMissingPredicate();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCirclePredicateDependency) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildCirclePredicates();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildAlertWithUnknownMetric();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
 TEST(MetricsManagerTest, TestLogSources) {
     string app1 = "app1";
     set<int32_t> app1Uids = {1111, 11111};
@@ -680,7 +130,7 @@
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
 
-    StatsdConfig config = buildGoodConfig();
+    StatsdConfig config;
     config.add_allowed_log_source("AID_SYSTEM");
     config.add_allowed_log_source(app1);
     config.add_default_pull_packages("AID_SYSTEM");
@@ -744,7 +194,7 @@
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
 
-    StatsdConfig config = buildGoodConfig();
+    StatsdConfig config;
     config.add_whitelisted_atom_ids(3);
     config.add_whitelisted_atom_ids(4);
 
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index caea42d..d96ff8a 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -47,7 +47,6 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
-const int64_t atomMatcherId = 678;
 const int logEventMatcherIndex = 0;
 const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -94,11 +93,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
-    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<EventMatcherWizard> eventMatcherWizard =
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -127,12 +123,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
@@ -217,12 +209,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
                                       wizard, logEventMatcherIndex, eventMatcherWizard,
@@ -301,12 +289,9 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
@@ -378,12 +363,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
@@ -428,12 +409,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     int64_t conditionChangeNs = bucketStartTimeNs + 8;
 
@@ -502,12 +479,8 @@
     dim->set_field(tagId);
     dim->add_child()->set_field(1);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, _, _))
@@ -577,12 +550,8 @@
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
                                       wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
@@ -657,12 +626,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
@@ -729,12 +694,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
@@ -807,12 +768,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 9889250..5524ebc 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -46,7 +46,6 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
-const int64_t atomMatcherId = 678;
 const int logEventMatcherIndex = 0;
 const int64_t bucketStartTimeNs = 10000000000;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -99,12 +98,8 @@
 public:
     static sp<ValueMetricProducer> createValueProducerNoConditions(
             sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -122,12 +117,8 @@
     static sp<ValueMetricProducer> createValueProducerWithCondition(
             sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
             ConditionState conditionAfterFirstBucketPrepared) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -147,12 +138,8 @@
             sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
             vector<int32_t> slicedStateAtoms,
             unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -172,12 +159,8 @@
             vector<int32_t> slicedStateAtoms,
             unordered_map<int, unordered_map<int, int64_t>> stateGroupMap,
             ConditionState conditionAfterFirstBucketPrepared) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -237,12 +220,8 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     int64_t startTimeBase = 11;
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -267,12 +246,8 @@
 TEST(ValueMetricProducerTest, TestFirstBucket) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -421,15 +396,11 @@
 TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
-    auto keyValue = atomMatcher.add_field_value_matcher();
-    keyValue->set_field(1);
-    keyValue->set_eq_int(3);
+    FieldValueMatcher fvm;
+    fvm.set_field(1);
+    fvm.set_eq_int(3);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex, {fvm});
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
@@ -698,12 +669,8 @@
 TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -759,12 +726,8 @@
 TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
@@ -820,12 +783,8 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_split_bucket_for_app_upgrade(false);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
@@ -900,12 +859,8 @@
 TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -944,12 +899,8 @@
 TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -1015,12 +966,8 @@
 
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -1360,12 +1307,8 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::MIN);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -1404,12 +1347,8 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::MAX);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -1447,12 +1386,8 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::AVG);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -1495,12 +1430,8 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::SUM);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -1539,12 +1470,8 @@
     metric.set_aggregation_type(ValueMetric::MIN);
     metric.set_use_diff(true);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -1611,12 +1538,8 @@
     metric.set_aggregation_type(ValueMetric::MIN);
     metric.set_use_diff(true);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
@@ -2140,12 +2063,8 @@
 TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
@@ -2963,12 +2882,8 @@
 TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
@@ -3001,12 +2916,8 @@
 TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
@@ -3045,12 +2956,8 @@
 TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
new file mode 100644
index 0000000..6b50fe5
--- /dev/null
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -0,0 +1,382 @@
+// 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.
+
+#include "src/metrics/parsing_utils/config_update_utils.h"
+
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <stdio.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
+#include "tests/statsd_test_util.h"
+
+using namespace testing;
+using android::sp;
+using android::os::statsd::Predicate;
+using std::map;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+ConfigKey key(123, 456);
+const int64_t timeBaseNs = 1000;
+sp<UidMap> uidMap = new UidMap();
+sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+sp<AlarmMonitor> anomalyAlarmMonitor;
+sp<AlarmMonitor> periodicAlarmMonitor;
+set<int> allTagIds;
+vector<sp<LogMatchingTracker>> oldAtomMatchers;
+unordered_map<int64_t, int> oldLogTrackerMap;
+vector<sp<ConditionTracker>> oldConditionTrackers;
+vector<sp<MetricProducer>> oldMetricProducers;
+std::vector<sp<AnomalyTracker>> oldAnomalyTrackers;
+std::vector<sp<AlarmTracker>> oldAlarmTrackers;
+unordered_map<int, std::vector<int>> conditionToMetricMap;
+unordered_map<int, std::vector<int>> trackerToMetricMap;
+unordered_map<int, std::vector<int>> trackerToConditionMap;
+unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+unordered_map<int64_t, int> alertTrackerMap;
+vector<int> metricsWithActivation;
+std::set<int64_t> noReportMetricIds;
+
+class ConfigUpdateTest : public ::testing::Test {
+public:
+    ConfigUpdateTest() {
+    }
+
+    void SetUp() override {
+        allTagIds.clear();
+        oldAtomMatchers.clear();
+        oldLogTrackerMap.clear();
+        oldConditionTrackers.clear();
+        oldMetricProducers.clear();
+        oldAnomalyTrackers.clear();
+        oldAlarmTrackers.clear();
+        conditionToMetricMap.clear();
+        trackerToMetricMap.clear();
+        trackerToConditionMap.clear();
+        activationAtomTrackerToMetricMap.clear();
+        deactivationAtomTrackerToMetricMap.clear();
+        alertTrackerMap.clear();
+        metricsWithActivation.clear();
+        noReportMetricIds.clear();
+    }
+};
+
+bool initConfig(const StatsdConfig& config) {
+    return initStatsdConfig(key, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                            periodicAlarmMonitor, timeBaseNs, timeBaseNs, allTagIds,
+                            oldAtomMatchers, oldLogTrackerMap, oldConditionTrackers,
+                            oldMetricProducers, oldAnomalyTrackers, oldAlarmTrackers,
+                            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                            alertTrackerMap, metricsWithActivation, noReportMetricIds);
+}
+
+}  // anonymous namespace
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
+    StatsdConfig config;
+    AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+    int64_t matcherId = matcher.id();
+    *config.add_atom_matcher() = matcher;
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newLogTrackerMap;
+    newLogTrackerMap[matcherId] = 0;
+    EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldLogTrackerMap, oldAtomMatchers,
+                                             newLogTrackerMap, matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) {
+    StatsdConfig config;
+    AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+    *config.add_atom_matcher() = matcher;
+
+    EXPECT_TRUE(initConfig(config));
+
+    StatsdConfig newConfig;
+    // Same id, different atom, so should be replaced.
+    AtomMatcher newMatcher = CreateSimpleAtomMatcher("TEST", /*atom=*/11);
+    int64_t matcherId = newMatcher.id();
+    EXPECT_EQ(matcherId, matcher.id());
+    *newConfig.add_atom_matcher() = newMatcher;
+
+    vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newLogTrackerMap;
+    newLogTrackerMap[matcherId] = 0;
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldLogTrackerMap, oldAtomMatchers,
+                                             newLogTrackerMap, matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) {
+    StatsdConfig config;
+    AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+    *config.add_atom_matcher() = matcher2;
+    int64_t matcher2Id = matcher2.id();
+
+    AtomMatcher matcher3;
+    matcher3.set_id(StringToId("TEST3"));
+    AtomMatcher_Combination* combination = matcher3.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(matcher1Id);
+    combination->add_matcher(matcher2Id);
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newLogTrackerMap;
+    // Same matchers, different order, all should be preserved.
+    *newConfig.add_atom_matcher() = matcher2;
+    newLogTrackerMap[matcher2Id] = 0;
+    *newConfig.add_atom_matcher() = matcher3;
+    newLogTrackerMap[matcher3Id] = 1;
+    *newConfig.add_atom_matcher() = matcher1;
+    newLogTrackerMap[matcher1Id] = 2;
+
+    vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. It should recurse the two child matchers and preserve all 3.
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldLogTrackerMap, oldAtomMatchers,
+                                             newLogTrackerMap, matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
+    EXPECT_EQ(matchersToUpdate[1], UPDATE_PRESERVE);
+    EXPECT_EQ(matchersToUpdate[2], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherReplace) {
+    StatsdConfig config;
+    AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+    *config.add_atom_matcher() = matcher2;
+    int64_t matcher2Id = matcher2.id();
+
+    AtomMatcher matcher3;
+    matcher3.set_id(StringToId("TEST3"));
+    AtomMatcher_Combination* combination = matcher3.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(matcher1Id);
+    combination->add_matcher(matcher2Id);
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change the logical operation of the combination matcher, causing a replacement.
+    matcher3.mutable_combination()->set_operation(LogicalOperation::AND);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newLogTrackerMap;
+    *newConfig.add_atom_matcher() = matcher2;
+    newLogTrackerMap[matcher2Id] = 0;
+    *newConfig.add_atom_matcher() = matcher3;
+    newLogTrackerMap[matcher3Id] = 1;
+    *newConfig.add_atom_matcher() = matcher1;
+    newLogTrackerMap[matcher1Id] = 2;
+
+    vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. The simple matchers should not be evaluated.
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldLogTrackerMap, oldAtomMatchers,
+                                             newLogTrackerMap, matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_UNKNOWN);
+    EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
+    EXPECT_EQ(matchersToUpdate[2], UPDATE_UNKNOWN);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherDepsChange) {
+    StatsdConfig config;
+    AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+    *config.add_atom_matcher() = matcher2;
+    int64_t matcher2Id = matcher2.id();
+
+    AtomMatcher matcher3;
+    matcher3.set_id(StringToId("TEST3"));
+    AtomMatcher_Combination* combination = matcher3.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(matcher1Id);
+    combination->add_matcher(matcher2Id);
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change a dependency of matcher 3.
+    matcher2.mutable_simple_atom_matcher()->set_atom_id(12);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newLogTrackerMap;
+    *newConfig.add_atom_matcher() = matcher2;
+    newLogTrackerMap[matcher2Id] = 0;
+    *newConfig.add_atom_matcher() = matcher3;
+    newLogTrackerMap[matcher3Id] = 1;
+    *newConfig.add_atom_matcher() = matcher1;
+    newLogTrackerMap[matcher1Id] = 2;
+
+    vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination.
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldLogTrackerMap, oldAtomMatchers,
+                                             newLogTrackerMap, matchersToUpdate, cycleTracker));
+    // Matcher 2 and matcher3 must be reevaluated. Matcher 1 might, but does not need to be.
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMatchers) {
+    StatsdConfig config;
+    // Will be preserved.
+    AtomMatcher simple1 = CreateSimpleAtomMatcher("SIMPLE1", /*atom=*/10);
+    int64_t simple1Id = simple1.id();
+    *config.add_atom_matcher() = simple1;
+
+    // Will be replaced.
+    AtomMatcher simple2 = CreateSimpleAtomMatcher("SIMPLE2", /*atom=*/11);
+    *config.add_atom_matcher() = simple2;
+    int64_t simple2Id = simple2.id();
+
+    // Will be removed.
+    AtomMatcher simple3 = CreateSimpleAtomMatcher("SIMPLE3", /*atom=*/12);
+    *config.add_atom_matcher() = simple3;
+    int64_t simple3Id = simple3.id();
+
+    // Will be preserved.
+    AtomMatcher combination1;
+    combination1.set_id(StringToId("combination1"));
+    AtomMatcher_Combination* combination = combination1.mutable_combination();
+    combination->set_operation(LogicalOperation::NOT);
+    combination->add_matcher(simple1Id);
+    int64_t combination1Id = combination1.id();
+    *config.add_atom_matcher() = combination1;
+
+    // Will be replaced since it depends on simple2.
+    AtomMatcher combination2;
+    combination2.set_id(StringToId("combination2"));
+    combination = combination2.mutable_combination();
+    combination->set_operation(LogicalOperation::AND);
+    combination->add_matcher(simple1Id);
+    combination->add_matcher(simple2Id);
+    int64_t combination2Id = combination2.id();
+    *config.add_atom_matcher() = combination2;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change simple2, causing simple2 and combination2 to be replaced.
+    simple2.mutable_simple_atom_matcher()->set_atom_id(111);
+
+    // 2 new matchers: simple4 and combination3:
+    AtomMatcher simple4 = CreateSimpleAtomMatcher("SIMPLE4", /*atom=*/13);
+    int64_t simple4Id = simple4.id();
+
+    AtomMatcher combination3;
+    combination3.set_id(StringToId("combination3"));
+    combination = combination3.mutable_combination();
+    combination->set_operation(LogicalOperation::AND);
+    combination->add_matcher(simple4Id);
+    combination->add_matcher(simple2Id);
+    int64_t combination3Id = combination3.id();
+
+    StatsdConfig newConfig;
+    *newConfig.add_atom_matcher() = combination3;
+    *newConfig.add_atom_matcher() = simple2;
+    *newConfig.add_atom_matcher() = combination2;
+    *newConfig.add_atom_matcher() = simple1;
+    *newConfig.add_atom_matcher() = simple4;
+    *newConfig.add_atom_matcher() = combination1;
+
+    set<int> newTagIds;
+    unordered_map<int64_t, int> newLogTrackerMap;
+    vector<sp<LogMatchingTracker>> newAtomMatchers;
+    EXPECT_TRUE(updateLogTrackers(newConfig, uidMap, oldLogTrackerMap, oldAtomMatchers, newTagIds,
+                                  newLogTrackerMap, newAtomMatchers));
+
+    ASSERT_EQ(newTagIds.size(), 3);
+    EXPECT_EQ(newTagIds.count(10), 1);
+    EXPECT_EQ(newTagIds.count(111), 1);
+    EXPECT_EQ(newTagIds.count(13), 1);
+
+    ASSERT_EQ(newLogTrackerMap.size(), 6);
+    EXPECT_EQ(newLogTrackerMap.at(combination3Id), 0);
+    EXPECT_EQ(newLogTrackerMap.at(simple2Id), 1);
+    EXPECT_EQ(newLogTrackerMap.at(combination2Id), 2);
+    EXPECT_EQ(newLogTrackerMap.at(simple1Id), 3);
+    EXPECT_EQ(newLogTrackerMap.at(simple4Id), 4);
+    EXPECT_EQ(newLogTrackerMap.at(combination1Id), 5);
+
+    ASSERT_EQ(newAtomMatchers.size(), 6);
+    // Make sure all atom matchers are initialized:
+    for (const sp<LogMatchingTracker>& tracker : newAtomMatchers) {
+        EXPECT_TRUE(tracker->mInitialized);
+    }
+    // Make sure preserved atom matchers are the same.
+    EXPECT_EQ(oldAtomMatchers[oldLogTrackerMap.at(simple1Id)],
+              newAtomMatchers[newLogTrackerMap.at(simple1Id)]);
+    EXPECT_EQ(oldAtomMatchers[oldLogTrackerMap.at(combination1Id)],
+              newAtomMatchers[newLogTrackerMap.at(combination1Id)]);
+    // Make sure replaced matchers are different.
+    EXPECT_NE(oldAtomMatchers[oldLogTrackerMap.at(simple2Id)],
+              newAtomMatchers[newLogTrackerMap.at(simple2Id)]);
+    EXPECT_NE(oldAtomMatchers[oldLogTrackerMap.at(combination2Id)],
+              newAtomMatchers[newLogTrackerMap.at(combination2Id)]);
+
+    // Validation, make sure the matchers have the proper ids. Could do more checks here.
+    EXPECT_EQ(newAtomMatchers[0]->getId(), combination3Id);
+    EXPECT_EQ(newAtomMatchers[1]->getId(), simple2Id);
+    EXPECT_EQ(newAtomMatchers[2]->getId(), combination2Id);
+    EXPECT_EQ(newAtomMatchers[3]->getId(), simple1Id);
+    EXPECT_EQ(newAtomMatchers[4]->getId(), simple4Id);
+    EXPECT_EQ(newAtomMatchers[5]->getId(), combination1Id);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
new file mode 100644
index 0000000..4e97eaf
--- /dev/null
+++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
@@ -0,0 +1,708 @@
+// 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.
+
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
+
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <stdio.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "src/condition/ConditionTracker.h"
+#include "src/matchers/LogMatchingTracker.h"
+#include "src/metrics/CountMetricProducer.h"
+#include "src/metrics/GaugeMetricProducer.h"
+#include "src/metrics/MetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
+#include "src/state/StateManager.h"
+#include "tests/metrics/metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
+
+using namespace testing;
+using android::sp;
+using android::os::statsd::Predicate;
+using std::map;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+const ConfigKey kConfigKey(0, 12345);
+const long kAlertId = 3;
+
+const long timeBaseSec = 1000;
+
+StatsdConfig buildGoodConfig() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
+
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
+    combination->add_matcher(StringToId("SCREEN_IS_OFF"));
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_IS_ON"));
+    metric->set_bucket(ONE_MINUTE);
+    metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    config.add_no_report_metric(3);
+
+    auto alert = config.add_alert();
+    alert->set_id(kAlertId);
+    alert->set_metric_id(3);
+    alert->set_num_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
+    return config;
+}
+
+StatsdConfig buildCircleMatchers() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
+    // Circle dependency
+    combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
+
+    return config;
+}
+
+StatsdConfig buildAlertWithUnknownMetric() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_IS_ON"));
+    metric->set_bucket(ONE_MINUTE);
+    metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    auto alert = config.add_alert();
+    alert->set_id(3);
+    alert->set_metric_id(2);
+    alert->set_num_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
+    return config;
+}
+
+StatsdConfig buildMissingMatchers() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
+    // undefined matcher
+    combination->add_matcher(StringToId("ABC"));
+
+    return config;
+}
+
+StatsdConfig buildMissingPredicate() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_EVENT"));
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_condition(StringToId("SOME_CONDITION"));
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_EVENT"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2);
+
+    return config;
+}
+
+StatsdConfig buildDimensionMetricsWithMultiTags() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(3);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("BATTERY_LOW"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
+    combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
+
+    // Count process state changes, slice by uid, while SCREEN_IS_OFF
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("BATTERY_LOW"));
+    metric->set_bucket(ONE_MINUTE);
+    // This case is interesting. We want to dimension across two atoms.
+    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    auto alert = config.add_alert();
+    alert->set_id(kAlertId);
+    alert->set_metric_id(3);
+    alert->set_num_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
+    return config;
+}
+
+StatsdConfig buildCirclePredicates() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
+
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+    auto condition = config.add_predicate();
+    condition->set_id(StringToId("SCREEN_IS_ON"));
+    SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
+    simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
+    simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
+
+    condition = config.add_predicate();
+    condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
+
+    Predicate_Combination* combination = condition->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_predicate(StringToId("SCREEN_IS_ON"));
+    combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
+
+    return config;
+}
+
+StatsdConfig buildConfigWithDifferentPredicates() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    auto pulledAtomMatcher =
+            CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE);
+    *config.add_atom_matcher() = pulledAtomMatcher;
+    auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnAtomMatcher;
+    auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffAtomMatcher;
+    auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher();
+    *config.add_atom_matcher() = batteryNoneAtomMatcher;
+    auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher();
+    *config.add_atom_matcher() = batteryUsbAtomMatcher;
+
+    // Simple condition with InitialValue set to default (unknown).
+    auto screenOnUnknownPredicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = screenOnUnknownPredicate;
+
+    // Simple condition with InitialValue set to false.
+    auto screenOnFalsePredicate = config.add_predicate();
+    screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse"));
+    SimplePredicate* simpleScreenOnFalsePredicate =
+            screenOnFalsePredicate->mutable_simple_predicate();
+    simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id());
+    simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id());
+    simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+
+    // Simple condition with InitialValue set to false.
+    auto onBatteryFalsePredicate = config.add_predicate();
+    onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse"));
+    SimplePredicate* simpleOnBatteryFalsePredicate =
+            onBatteryFalsePredicate->mutable_simple_predicate();
+    simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id());
+    simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id());
+    simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+
+    // Combination condition with both simple condition InitialValues set to false.
+    auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate();
+    screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse"));
+    screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation(
+            LogicalOperation::AND);
+    addPredicateToPredicateCombination(*screenOnFalsePredicate,
+                                       screenOnFalseOnBatteryFalsePredicate);
+    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
+                                       screenOnFalseOnBatteryFalsePredicate);
+
+    // Combination condition with one simple condition InitialValue set to unknown and one set to
+    // false.
+    auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate();
+    screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse"));
+    screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation(
+            LogicalOperation::AND);
+    addPredicateToPredicateCombination(screenOnUnknownPredicate,
+                                       screenOnUnknownOnBatteryFalsePredicate);
+    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
+                                       screenOnUnknownOnBatteryFalsePredicate);
+
+    // Simple condition metric with initial value false.
+    ValueMetric* metric1 = config.add_value_metric();
+    metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse"));
+    metric1->set_what(pulledAtomMatcher.id());
+    *metric1->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric1->set_bucket(FIVE_MINUTES);
+    metric1->set_condition(screenOnFalsePredicate->id());
+
+    // Simple condition metric with initial value unknown.
+    ValueMetric* metric2 = config.add_value_metric();
+    metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown"));
+    metric2->set_what(pulledAtomMatcher.id());
+    *metric2->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric2->set_bucket(FIVE_MINUTES);
+    metric2->set_condition(screenOnUnknownPredicate.id());
+
+    // Combination condition metric with initial values false and false.
+    ValueMetric* metric3 = config.add_value_metric();
+    metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse"));
+    metric3->set_what(pulledAtomMatcher.id());
+    *metric3->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric3->set_bucket(FIVE_MINUTES);
+    metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id());
+
+    // Combination condition metric with initial values unknown and false.
+    ValueMetric* metric4 = config.add_value_metric();
+    metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse"));
+    metric4->set_what(pulledAtomMatcher.id());
+    *metric4->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric4->set_bucket(FIVE_MINUTES);
+    metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id());
+
+    return config;
+}
+}  // anonymous namespace
+
+TEST(MetricsManagerTest, TestInitialConditions) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildConfigWithDifferentPredicates();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_TRUE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+    ASSERT_EQ(4u, allMetricProducers.size());
+    ASSERT_EQ(5u, allConditionTrackers.size());
+
+    ConditionKey queryKey;
+    vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
+
+    allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
+    allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
+    EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[1]);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[2]);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[3]);
+    EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]);
+
+    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition);
+    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition);
+    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition);
+    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition);
+}
+
+TEST(MetricsManagerTest, TestGoodConfig) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildGoodConfig();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_TRUE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+    ASSERT_EQ(1u, allMetricProducers.size());
+    ASSERT_EQ(1u, allAnomalyTrackers.size());
+    ASSERT_EQ(1u, noReportMetricIds.size());
+    ASSERT_EQ(1u, alertTrackerMap.size());
+    EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
+    EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
+}
+
+TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildDimensionMetricsWithMultiTags();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildCircleMatchers();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestMissingMatchers) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildMissingMatchers();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestMissingPredicate) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildMissingPredicate();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCirclePredicateDependency) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildCirclePredicates();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildAlertWithUnknownMetric();
+    set<int> allTagIds;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
+    unordered_map<int64_t, int> logTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    vector<sp<MetricProducer>> allMetricProducers;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap,
+            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
+            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+            metricsWithActivation, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCreateLogTrackerInvalidMatcher) {
+    sp<UidMap> uidMap = new UidMap();
+    AtomMatcher matcher;
+    matcher.set_id(21);
+    EXPECT_EQ(createLogTracker(matcher, 0, uidMap), nullptr);
+}
+
+TEST(MetricsManagerTest, TestCreateLogTrackerSimple) {
+    int index = 1;
+    int64_t id = 123;
+    sp<UidMap> uidMap = new UidMap();
+    AtomMatcher matcher;
+    matcher.set_id(id);
+    SimpleAtomMatcher* simpleAtomMatcher = matcher.mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(util::SCREEN_STATE_CHANGED);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+    sp<LogMatchingTracker> tracker = createLogTracker(matcher, index, uidMap);
+    EXPECT_NE(tracker, nullptr);
+
+    EXPECT_TRUE(tracker->mInitialized);
+    EXPECT_EQ(tracker->getId(), id);
+    EXPECT_EQ(tracker->mIndex, index);
+    const set<int>& atomIds = tracker->getAtomIds();
+    ASSERT_EQ(atomIds.size(), 1);
+    EXPECT_EQ(atomIds.count(util::SCREEN_STATE_CHANGED), 1);
+}
+
+TEST(MetricsManagerTest, TestCreateLogTrackerCombination) {
+    int index = 1;
+    int64_t id = 123;
+    sp<UidMap> uidMap = new UidMap();
+    AtomMatcher matcher;
+    matcher.set_id(id);
+    AtomMatcher_Combination* combination = matcher.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(123);
+    combination->add_matcher(223);
+
+    sp<LogMatchingTracker> tracker = createLogTracker(matcher, index, uidMap);
+    EXPECT_NE(tracker, nullptr);
+
+    // Combination matchers need to be initialized first.
+    EXPECT_FALSE(tracker->mInitialized);
+    EXPECT_EQ(tracker->getId(), id);
+    EXPECT_EQ(tracker->mIndex, index);
+    const set<int>& atomIds = tracker->getAtomIds();
+    ASSERT_EQ(atomIds.size(), 0);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index cee8372..0be983f 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -15,6 +15,8 @@
 #include "statsd_test_util.h"
 
 #include <aidl/android/util/StatsEventParcel.h>
+
+#include "matchers/SimpleLogMatchingTracker.h"
 #include "stats_event.h"
 
 using aidl::android::util::StatsEventParcel;
@@ -996,6 +998,20 @@
     return static_cast<int64_t>(std::hash<std::string>()(str));
 }
 
+sp<EventMatcherWizard> createEventMatcherWizard(
+        int tagId, int matcherIndex, const vector<FieldValueMatcher>& fieldValueMatchers) {
+    sp<UidMap> uidMap = new UidMap();
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    for (const FieldValueMatcher& fvm : fieldValueMatchers) {
+        *atomMatcher.add_field_value_matcher() = fvm;
+    }
+    uint64_t matcherHash = 0x12345678;
+    int64_t matcherId = 678;
+    return new EventMatcherWizard({new SimpleLogMatchingTracker(matcherId, matcherIndex,
+                                                                matcherHash, atomMatcher, uidMap)});
+}
+
 void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
                                                    const int uid, const string& tag) {
     EXPECT_EQ(value.field(), atomId);
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 3dcf4ec..1220019 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -25,6 +25,7 @@
 #include "src/StatsLogProcessor.h"
 #include "src/hash.h"
 #include "src/logd/LogEvent.h"
+#include "src/matchers/EventMatcherWizard.h"
 #include "src/packages/UidMap.h"
 #include "src/stats_log_util.h"
 #include "stats_event.h"
@@ -335,6 +336,9 @@
 
 int64_t StringToId(const string& str);
 
+sp<EventMatcherWizard> createEventMatcherWizard(
+        int tagId, int matcherIndex, const std::vector<FieldValueMatcher>& fieldValueMatchers = {});
+
 void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
                                                    const int uid, const string& tag);
 void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 522b8cc..5c4951e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3827,6 +3827,12 @@
         } catch (RemoteException e) {
             finishAfterTransition();
         }
+
+        // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
+        // be restored now.
+        if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
+            restoreAutofillSaveUi();
+        }
     }
 
     /**
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 8c3180b..4c9e400 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -39,7 +39,7 @@
     boolean injectInputEvent(in InputEvent event, boolean sync);
     void syncInputTransactions();
     boolean setRotation(int rotation);
-    Bitmap takeScreenshot(in Rect crop, int rotation);
+    Bitmap takeScreenshot(in Rect crop);
     boolean clearWindowContentFrameStats(int windowId);
     WindowContentFrameStats getWindowContentFrameStats(int windowId);
     void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 68e6561..6f3e8922 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1492,6 +1492,7 @@
         private boolean mAllowGeneratedReplies = true;
         private final @SemanticAction int mSemanticAction;
         private final boolean mIsContextual;
+        private boolean mAuthenticationRequired;
 
         /**
          * Small icon representing the action.
@@ -1528,6 +1529,7 @@
             mAllowGeneratedReplies = in.readInt() == 1;
             mSemanticAction = in.readInt();
             mIsContextual = in.readInt() == 1;
+            mAuthenticationRequired = in.readInt() == 1;
         }
 
         /**
@@ -1536,13 +1538,14 @@
         @Deprecated
         public Action(int icon, CharSequence title, PendingIntent intent) {
             this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true,
-                    SEMANTIC_ACTION_NONE, false /* isContextual */);
+                    SEMANTIC_ACTION_NONE, false /* isContextual */, false /* requireAuth */);
         }
 
         /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
         private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
                 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
-                       @SemanticAction int semanticAction, boolean isContextual) {
+                @SemanticAction int semanticAction, boolean isContextual,
+                boolean requireAuth) {
             this.mIcon = icon;
             if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
                 this.icon = icon.getResId();
@@ -1554,6 +1557,7 @@
             this.mAllowGeneratedReplies = allowGeneratedReplies;
             this.mSemanticAction = semanticAction;
             this.mIsContextual = isContextual;
+            this.mAuthenticationRequired = requireAuth;
         }
 
         /**
@@ -1624,6 +1628,17 @@
         }
 
         /**
+         * Returns whether the OS should only send this action's {@link PendingIntent} on an
+         * unlocked device.
+         *
+         * If the device is locked when the action is invoked, the OS should show the keyguard and
+         * require successful authentication before invoking the intent.
+         */
+        public boolean isAuthenticationRequired() {
+            return mAuthenticationRequired;
+        }
+
+        /**
          * Builder class for {@link Action} objects.
          */
         public static final class Builder {
@@ -1635,6 +1650,7 @@
             @Nullable private ArrayList<RemoteInput> mRemoteInputs;
             private @SemanticAction int mSemanticAction;
             private boolean mIsContextual;
+            private boolean mAuthenticationRequired;
 
             /**
              * Construct a new builder for {@link Action} object.
@@ -1654,7 +1670,7 @@
              * @param intent the {@link PendingIntent} to fire when users trigger this action
              */
             public Builder(Icon icon, CharSequence title, PendingIntent intent) {
-                this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE);
+                this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, false);
             }
 
             /**
@@ -1665,23 +1681,25 @@
             public Builder(Action action) {
                 this(action.getIcon(), action.title, action.actionIntent,
                         new Bundle(action.mExtras), action.getRemoteInputs(),
-                        action.getAllowGeneratedReplies(), action.getSemanticAction());
+                        action.getAllowGeneratedReplies(), action.getSemanticAction(),
+                        action.isAuthenticationRequired());
             }
 
             private Builder(@Nullable Icon icon, @Nullable CharSequence title,
                     @Nullable PendingIntent intent, @NonNull Bundle extras,
                     @Nullable RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
-                    @SemanticAction int semanticAction) {
+                    @SemanticAction int semanticAction, boolean authRequired) {
                 mIcon = icon;
                 mTitle = title;
                 mIntent = intent;
                 mExtras = extras;
                 if (remoteInputs != null) {
-                    mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
+                    mRemoteInputs = new ArrayList<>(remoteInputs.length);
                     Collections.addAll(mRemoteInputs, remoteInputs);
                 }
                 mAllowGeneratedReplies = allowGeneratedReplies;
                 mSemanticAction = semanticAction;
+                mAuthenticationRequired = authRequired;
             }
 
             /**
@@ -1776,6 +1794,21 @@
             }
 
             /**
+             * Sets whether the OS should only send this action's {@link PendingIntent} on an
+             * unlocked device.
+             *
+             * If this is true and the device is locked when the action is invoked, the OS will
+             * show the keyguard and require successful authentication before invoking the intent.
+             * If this is false and the device is locked, the OS will decide whether authentication
+             * should be required.
+             */
+            @NonNull
+            public Builder setAuthenticationRequired(boolean authenticationRequired) {
+                mAuthenticationRequired = authenticationRequired;
+                return this;
+            }
+
+            /**
              * Throws an NPE if we are building a contextual action missing one of the fields
              * necessary to display the action.
              */
@@ -1827,7 +1860,8 @@
                 RemoteInput[] textInputsArr = textInputs.isEmpty()
                         ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
                 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
-                        mAllowGeneratedReplies, mSemanticAction, mIsContextual);
+                        mAllowGeneratedReplies, mSemanticAction, mIsContextual,
+                        mAuthenticationRequired);
             }
         }
 
@@ -1841,7 +1875,8 @@
                     getRemoteInputs(),
                     getAllowGeneratedReplies(),
                     getSemanticAction(),
-                    isContextual());
+                    isContextual(),
+                    isAuthenticationRequired());
         }
 
         @Override
@@ -1870,6 +1905,7 @@
             out.writeInt(mAllowGeneratedReplies ? 1 : 0);
             out.writeInt(mSemanticAction);
             out.writeInt(mIsContextual ? 1 : 0);
+            out.writeInt(mAuthenticationRequired ? 1 : 0);
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<Action> CREATOR =
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index e0951bf..109205f 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -903,7 +903,7 @@
         try {
             // Calling out without a lock held.
             screenShot = mUiAutomationConnection.takeScreenshot(
-                    new Rect(0, 0, displaySize.x, displaySize.y), rotation);
+                    new Rect(0, 0, displaySize.x, displaySize.y));
             if (screenShot == null) {
                 return null;
             }
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index ce51dba..70d5201 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -180,7 +180,7 @@
     }
 
     @Override
-    public Bitmap takeScreenshot(Rect crop, int rotation) {
+    public Bitmap takeScreenshot(Rect crop) {
         synchronized (mLock) {
             throwIfCalledByNotTrustedUidLocked();
             throwIfShutdownLocked();
@@ -190,7 +190,15 @@
         try {
             int width = crop.width();
             int height = crop.height();
-            return SurfaceControl.screenshot(crop, width, height, rotation);
+            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+            final SurfaceControl.DisplayCaptureArgs captureArgs =
+                    new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+                            .setSourceCrop(crop)
+                            .setSize(width, height)
+                            .build();
+            final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+                    SurfaceControl.captureDisplay(captureArgs);
+            return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 16ddcd1..056cfc7 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -402,7 +402,7 @@
      */
     public void onFullBackup(FullBackupDataOutput data) throws IOException {
         FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this);
-        if (!backupScheme.isFullBackupContentEnabled()) {
+        if (!isDeviceToDeviceMigration() && !backupScheme.isFullBackupContentEnabled()) {
             return;
         }
 
@@ -430,24 +430,18 @@
         final Context ceContext = createCredentialProtectedStorageContext();
         final String rootDir = ceContext.getDataDir().getCanonicalPath();
         final String filesDir = ceContext.getFilesDir().getCanonicalPath();
-        final String noBackupDir = ceContext.getNoBackupFilesDir().getCanonicalPath();
         final String databaseDir = ceContext.getDatabasePath("foo").getParentFile()
                 .getCanonicalPath();
         final String sharedPrefsDir = ceContext.getSharedPreferencesPath("foo").getParentFile()
                 .getCanonicalPath();
-        final String cacheDir = ceContext.getCacheDir().getCanonicalPath();
-        final String codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath();
 
         final Context deContext = createDeviceProtectedStorageContext();
         final String deviceRootDir = deContext.getDataDir().getCanonicalPath();
         final String deviceFilesDir = deContext.getFilesDir().getCanonicalPath();
-        final String deviceNoBackupDir = deContext.getNoBackupFilesDir().getCanonicalPath();
         final String deviceDatabaseDir = deContext.getDatabasePath("foo").getParentFile()
                 .getCanonicalPath();
         final String deviceSharedPrefsDir = deContext.getSharedPreferencesPath("foo")
                 .getParentFile().getCanonicalPath();
-        final String deviceCacheDir = deContext.getCacheDir().getCanonicalPath();
-        final String deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath();
 
         final String libDir = (appInfo.nativeLibraryDir != null)
                 ? new File(appInfo.nativeLibraryDir).getCanonicalPath()
@@ -460,33 +454,36 @@
 
         // Add the directories we always exclude.
         traversalExcludeSet.add(filesDir);
-        traversalExcludeSet.add(noBackupDir);
         traversalExcludeSet.add(databaseDir);
         traversalExcludeSet.add(sharedPrefsDir);
-        traversalExcludeSet.add(cacheDir);
-        traversalExcludeSet.add(codeCacheDir);
 
         traversalExcludeSet.add(deviceFilesDir);
-        traversalExcludeSet.add(deviceNoBackupDir);
         traversalExcludeSet.add(deviceDatabaseDir);
         traversalExcludeSet.add(deviceSharedPrefsDir);
-        traversalExcludeSet.add(deviceCacheDir);
-        traversalExcludeSet.add(deviceCodeCacheDir);
 
         if (libDir != null) {
             traversalExcludeSet.add(libDir);
         }
 
+        Set<String> extraExcludedDirs = getExtraExcludeDirsIfAny(ceContext);
+        Set<String> extraExcludedDeviceDirs = getExtraExcludeDirsIfAny(deContext);
+        traversalExcludeSet.addAll(extraExcludedDirs);
+        traversalExcludeSet.addAll(extraExcludedDeviceDirs);
+
         // Root dir first.
         applyXmlFiltersAndDoFullBackupForDomain(
                 packageName, FullBackup.ROOT_TREE_TOKEN, manifestIncludeMap,
                 manifestExcludeSet, traversalExcludeSet, data);
         traversalExcludeSet.add(rootDir);
+        // Exclude the extra directories anyway, since we've already covered them if it was needed.
+        traversalExcludeSet.addAll(extraExcludedDirs);
 
         applyXmlFiltersAndDoFullBackupForDomain(
                 packageName, FullBackup.DEVICE_ROOT_TREE_TOKEN, manifestIncludeMap,
                 manifestExcludeSet, traversalExcludeSet, data);
         traversalExcludeSet.add(deviceRootDir);
+        // Exclude the extra directories anyway, since we've already covered them if it was needed.
+        traversalExcludeSet.addAll(extraExcludedDeviceDirs);
 
         // Data dir next.
         traversalExcludeSet.remove(filesDir);
@@ -545,11 +542,28 @@
         }
     }
 
+    private Set<String> getExtraExcludeDirsIfAny(Context context) throws IOException {
+        if (isDeviceToDeviceMigration()) {
+            return Collections.emptySet();
+        }
+
+        // If this is not a migration, also exclude no-backup and cache dirs.
+        Set<String> excludedDirs = new HashSet<>();
+        excludedDirs.add(context.getCacheDir().getCanonicalPath());
+        excludedDirs.add(context.getCodeCacheDir().getCanonicalPath());
+        excludedDirs.add(context.getNoBackupFilesDir().getCanonicalPath());
+        return Collections.unmodifiableSet(excludedDirs);
+    }
+
+    private boolean isDeviceToDeviceMigration() {
+        return mOperationType == OperationType.MIGRATION;
+    }
+
     /** @hide */
     @VisibleForTesting
     public IncludeExcludeRules getIncludeExcludeRules(FullBackup.BackupScheme backupScheme)
             throws IOException, XmlPullParserException {
-        if (mOperationType == OperationType.MIGRATION) {
+        if (isDeviceToDeviceMigration()) {
             return IncludeExcludeRules.emptyRules();
         }
 
@@ -892,6 +906,11 @@
     }
 
     private boolean isFileEligibleForRestore(File destination) throws IOException {
+        if (isDeviceToDeviceMigration()) {
+            // Everything is eligible for device-to-device migration.
+            return true;
+        }
+
         FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this);
         if (!bs.isFullBackupContentEnabled()) {
             if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
index 236b006..cc0af3f 100644
--- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
+++ b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
@@ -91,11 +91,13 @@
 
     private final @UserIdInt int mUserId;
     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.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled;
+        this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled;
         this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone;
     }
 
@@ -103,6 +105,7 @@
     private static TimeZoneCapabilities createFromParcel(Parcel in) {
         return new TimeZoneCapabilities.Builder(in.readInt())
                 .setConfigureAutoDetectionEnabled(in.readInt())
+                .setConfigureGeoDetectionEnabled(in.readInt())
                 .setSuggestManualTimeZone(in.readInt())
                 .build();
     }
@@ -111,6 +114,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mUserId);
         dest.writeInt(mConfigureAutoDetectionEnabled);
+        dest.writeInt(mConfigureGeoDetectionEnabled);
         dest.writeInt(mSuggestManualTimeZone);
     }
 
@@ -120,8 +124,8 @@
     }
 
     /**
-     * Returns the user's capability state for controlling automatic time zone detection via
-     * {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
+     * Returns the user's capability state for controlling whether automatic time zone detection is
+     * enabled via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
      * TimeZoneConfiguration#isAutoDetectionEnabled()}.
      */
     @CapabilityState
@@ -130,6 +134,16 @@
     }
 
     /**
+     * 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()}.
+     */
+    @CapabilityState
+    public int getConfigureGeoDetectionEnabled() {
+        return mConfigureGeoDetectionEnabled;
+    }
+
+    /**
      * Returns the user's capability state for manually setting the time zone on a device via
      * {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
      *
@@ -157,12 +171,16 @@
         TimeZoneCapabilities that = (TimeZoneCapabilities) o;
         return mUserId == that.mUserId
                 && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled
+                && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled
                 && mSuggestManualTimeZone == that.mSuggestManualTimeZone;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUserId, mConfigureAutoDetectionEnabled, mSuggestManualTimeZone);
+        return Objects.hash(mUserId,
+                mConfigureAutoDetectionEnabled,
+                mConfigureGeoDetectionEnabled,
+                mSuggestManualTimeZone);
     }
 
     @Override
@@ -170,6 +188,7 @@
         return "TimeZoneDetectorCapabilities{"
                 + "mUserId=" + mUserId
                 + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled
+                + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled
                 + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone
                 + '}';
     }
@@ -179,6 +198,7 @@
 
         private final @UserIdInt int mUserId;
         private @CapabilityState int mConfigureAutoDetectionEnabled;
+        private @CapabilityState int mConfigureGeoDetectionEnabled;
         private @CapabilityState int mSuggestManualTimeZone;
 
         /**
@@ -194,6 +214,12 @@
             return this;
         }
 
+        /** Sets the state for the geolocation time zone detection enabled config. */
+        public Builder setConfigureGeoDetectionEnabled(@CapabilityState int value) {
+            this.mConfigureGeoDetectionEnabled = value;
+            return this;
+        }
+
         /** Sets the state for the suggestManualTimeZone action. */
         public Builder setSuggestManualTimeZone(@CapabilityState int value) {
             this.mSuggestManualTimeZone = value;
@@ -204,6 +230,7 @@
         @NonNull
         public TimeZoneCapabilities build() {
             verifyCapabilitySet(mConfigureAutoDetectionEnabled, "configureAutoDetectionEnabled");
+            verifyCapabilitySet(mConfigureGeoDetectionEnabled, "configureGeoDetectionEnabled");
             verifyCapabilitySet(mSuggestManualTimeZone, "suggestManualTimeZone");
             return new TimeZoneCapabilities(this);
         }
diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
index 047d349..6f84ee2 100644
--- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
+++ b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
@@ -67,6 +67,10 @@
     @Property
     public static final String PROPERTY_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
 
+    /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */
+    @Property
+    public static final String PROPERTY_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
+
     private final Bundle mBundle;
 
     private TimeZoneConfiguration(Builder builder) {
@@ -86,7 +90,8 @@
 
     /** Returns {@code true} if all known properties are set. */
     public boolean isComplete() {
-        return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED);
+        return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
+                && hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
     }
 
     /** Returns true if the specified property is set. */
@@ -108,6 +113,28 @@
         return mBundle.getBoolean(PROPERTY_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.
+     *
+     * @throws IllegalStateException if the field 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();
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -174,6 +201,12 @@
             return this;
         }
 
+        /** Sets the desired state of the geolocation time zone detection enabled property. */
+        public Builder setGeoDetectionEnabled(boolean enabled) {
+            this.mBundle.putBoolean(PROPERTY_GEO_DETECTION_ENABLED, enabled);
+            return this;
+        }
+
         /** Returns the {@link TimeZoneConfiguration}. */
         @NonNull
         public TimeZoneConfiguration build() {
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 3522b1b..0e6a063 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -199,7 +199,7 @@
         public static final int NOTIFICATION_INTERRUPTION = 12;
 
         /**
-         * A Slice was pinned by the default launcher or the default assistant.
+         * A Slice was pinned by the default assistant.
          * @hide
          */
         @SystemApi
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 3d3759e..f14f66b 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -161,6 +161,7 @@
      * @return true if this parcelable is stable.
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     default @Stability int getStability() {
         return PARCELABLE_STABILITY_LOCAL;
     }
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index fd68c2b..26f3af0 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -178,6 +178,15 @@
     native public static long uptimeMillis();
 
     /**
+     * Returns nanoseconds since boot, not counting time spent in deep sleep.
+     *
+     * @return nanoseconds of non-sleep uptime since boot.
+     * @hide
+     */
+    @CriticalNative
+    public static native long uptimeNanos();
+
+    /**
      * Return {@link Clock} that starts at system boot, not counting time spent
      * in deep sleep.
      *
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2ce993d..6ef086b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -22,8 +22,6 @@
 import static android.graphics.Matrix.MSKEW_Y;
 import static android.graphics.Matrix.MTRANS_X;
 import static android.graphics.Matrix.MTRANS_Y;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
 import static android.view.SurfaceControlProto.HASH_CODE;
 import static android.view.SurfaceControlProto.NAME;
 
@@ -590,6 +588,26 @@
         public boolean containsSecureLayers() {
             return mContainsSecureLayers;
         }
+
+        /**
+         * Copy content of ScreenshotHardwareBuffer into a hardware bitmap and return it.
+         * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
+         * into
+         * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
+         *
+         * CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
+         * directly
+         * use the {@link HardwareBuffer} directly.
+         *
+         * @return Bitmap generated from the {@link HardwareBuffer}
+         */
+        public Bitmap asBitmap() {
+            if (mHardwareBuffer == null) {
+                Log.w(TAG, "Failed to take screenshot. Null screenshot object");
+                return null;
+            }
+            return Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
+        }
     }
 
     /**
@@ -597,7 +615,7 @@
      * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
      * @hide
      */
-    public abstract static class CaptureArgs {
+    private abstract static class CaptureArgs {
         private final int mPixelFormat;
         private final Rect mSourceCrop = new Rect();
         private final float mFrameScale;
@@ -615,7 +633,7 @@
          *
          * @param <T> A builder that extends {@link Builder}
          */
-        public abstract static class Builder<T extends Builder<T>> {
+        abstract static class Builder<T extends Builder<T>> {
             private int mPixelFormat = PixelFormat.RGBA_8888;
             private final Rect mSourceCrop = new Rect();
             private float mFrameScale = 1;
@@ -675,7 +693,6 @@
         private final int mWidth;
         private final int mHeight;
         private final boolean mUseIdentityTransform;
-        private final int mRotation;
 
         private DisplayCaptureArgs(Builder builder) {
             super(builder);
@@ -683,7 +700,6 @@
             mWidth = builder.mWidth;
             mHeight = builder.mHeight;
             mUseIdentityTransform = builder.mUseIdentityTransform;
-            mRotation = builder.mRotation;
         }
 
         /**
@@ -694,7 +710,6 @@
             private int mWidth;
             private int mHeight;
             private boolean mUseIdentityTransform;
-            private @Surface.Rotation int mRotation = Surface.ROTATION_0;
 
             /**
              * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
@@ -736,26 +751,16 @@
             }
 
             /**
-             * Replace whatever transformation (rotation, scaling, translation) the surface
-             * layers are currently using with the identity transformation while taking the
-             * screenshot.
+             * Replace the rotation transform of the display with the identity transformation while
+             * taking the screenshot. This ensures the screenshot is taken in the ROTATION_0
+             * orientation. Set this value to false if the screenshot should be taken in the
+             * current screen orientation.
              */
             public Builder setUseIdentityTransform(boolean useIdentityTransform) {
                 mUseIdentityTransform = useIdentityTransform;
                 return this;
             }
 
-            /**
-             * Apply a custom clockwise rotation to the screenshot, i.e.
-             * Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take screenshots in its
-             * native portrait orientation by default, so this is useful for returning screenshots
-             * that are independent of device orientation.
-             */
-            public Builder setRotation(@Surface.Rotation int rotation) {
-                mRotation = rotation;
-                return this;
-            }
-
             @Override
             Builder getThis() {
                 return this;
@@ -2221,130 +2226,16 @@
     }
 
     /**
-     * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
+     * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
+     * the content.
+     *
      * @hide
      */
-    @UnsupportedAppUsage
-    public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
-        return screenshot(sourceCrop, width, height, false, rotation);
-    }
-
-    /**
-     * Copy the current screen contents into a hardware bitmap and return it.
-     * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into
-     * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
-     *
-     * CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use
-     * unless absolutely necessary; prefer the versions that use a {@link HardwareBuffer} such as
-     * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
-     *
-     * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static Bitmap screenshot(Rect sourceCrop, int width, int height,
-            boolean useIdentityTransform, int rotation) {
-        // TODO: should take the display as a parameter
-        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
-        if (displayToken == null) {
-            Log.w(TAG, "Failed to take screenshot because internal display is disconnected");
-            return null;
-        }
-
-        if (rotation == ROTATION_90 || rotation == ROTATION_270) {
-            rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90;
-        }
-
-        SurfaceControl.rotateCropForSF(sourceCrop, rotation);
-        final ScreenshotHardwareBuffer buffer = screenshotToBuffer(displayToken, sourceCrop, width,
-                height, useIdentityTransform, rotation);
-
-        if (buffer == null) {
-            Log.w(TAG, "Failed to take screenshot");
-            return null;
-        }
-        return Bitmap.wrapHardwareBuffer(buffer.getHardwareBuffer(), buffer.getColorSpace());
-    }
-
-    /**
-     * Captures all the surfaces in a display and returns a {@link HardwareBuffer} with the content.
-     *
-     * @param display              The display to take the screenshot of.
-     * @param sourceCrop           The portion of the screen to capture into the Bitmap; caller may
-     *                             pass in 'new Rect()' if no cropping is desired.
-     * @param width                The desired width of the returned bitmap; the raw screen will be
-     *                             scaled down to this size; caller may pass in 0 if no scaling is
-     *                             desired.
-     * @param height               The desired height of the returned bitmap; the raw screen will
-     *                             be scaled down to this size; caller may pass in 0 if no scaling
-     *                             is desired.
-     * @param useIdentityTransform Replace whatever transformation (rotation, scaling, translation)
-     *                             the surface layers are currently using with the identity
-     *                             transformation while taking the screenshot.
-     * @param rotation             Apply a custom clockwise rotation to the screenshot, i.e.
-     *                             Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take
-     *                             screenshots in its native portrait orientation by default, so
-     *                             this is useful for returning screenshots that are independent of
-     *                             device orientation.
-     * @return Returns a HardwareBuffer that contains the captured content.
-     * @hide
-     */
-    public static ScreenshotHardwareBuffer screenshotToBuffer(IBinder display, Rect sourceCrop,
-            int width, int height, boolean useIdentityTransform, int rotation) {
-        if (display == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
-        }
-
-        DisplayCaptureArgs captureArgs = new DisplayCaptureArgs.Builder(display)
-                .setSourceCrop(sourceCrop)
-                .setSize(width, height)
-                .setUseIdentityTransform(useIdentityTransform)
-                .setRotation(rotation)
-                .build();
-
+    public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) {
         return nativeCaptureDisplay(captureArgs);
     }
 
     /**
-     * Like screenshotToBuffer, but if the caller is AID_SYSTEM, allows
-     * for the capture of secure layers. This is used for the screen rotation
-     * animation where the system server takes screenshots but does
-     * not persist them or allow them to leave the server. However in other
-     * cases in the system server, we mostly want to omit secure layers
-     * like when we take a screenshot on behalf of the assistant.
-     *
-     * @hide
-     */
-    public static ScreenshotHardwareBuffer screenshotToBufferWithSecureLayersUnsafe(IBinder display,
-            Rect sourceCrop, int width, int height, boolean useIdentityTransform,
-            int rotation) {
-        if (display == null) {
-            throw new IllegalArgumentException("displayToken must not be null");
-        }
-
-        DisplayCaptureArgs captureArgs = new DisplayCaptureArgs.Builder(display)
-                .setSourceCrop(sourceCrop)
-                .setSize(width, height)
-                .setUseIdentityTransform(useIdentityTransform)
-                .setRotation(rotation)
-                .setCaptureSecureLayers(true)
-                .build();
-
-        return nativeCaptureDisplay(captureArgs);
-    }
-
-    private static void rotateCropForSF(Rect crop, int rot) {
-        if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
-            int tmp = crop.top;
-            crop.top = crop.left;
-            crop.left = tmp;
-            tmp = crop.right;
-            crop.right = crop.bottom;
-            crop.bottom = tmp;
-        }
-    }
-
-    /**
      * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
      *
      * @param layer            The root layer to capture.
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index e814ec6..eb67191 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -29,7 +29,7 @@
 oneway interface IWindowMagnificationConnection {
 
     /**
-     * Enables window magnification on specifed display with specified center and scale.
+     * Enables window magnification on specified display with given center and scale and animation.
      *
      * @param displayId The logical display id.
      * @param scale magnification scale.
@@ -41,7 +41,7 @@
     void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
 
     /**
-     * Sets the scale of the window magnifier on specifed display.
+     * Sets the scale of the window magnifier on specified display.
      *
      * @param displayId The logical display id.
      * @param scale magnification scale.
@@ -49,14 +49,14 @@
     void setScale(int displayId, float scale);
 
      /**
-     * Disables window magnification on specifed display.
+     * Disables window magnification on specified display with animation.
      *
      * @param displayId The logical display id.
      */
     void disableWindowMagnification(int displayId);
 
     /**
-     * Moves the window magnifier on the specifed display.
+     * Moves the window magnifier on the specified display. It has no effect while animating.
      *
      * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
      *                current screen pixels.
diff --git a/services/core/java/com/android/server/wm/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
similarity index 88%
rename from services/core/java/com/android/server/wm/ProtoLogGroup.java
rename to core/java/com/android/internal/protolog/ProtoLogGroup.java
index 51725ce..73d148c 100644
--- a/services/core/java/com/android/server/wm/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.wm;
+package com.android.internal.protolog;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.protolog.common.IProtoLogGroup;
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.IProtoLogGroup;
 
 /**
  * Defines logging groups for ProtoLog.
@@ -118,16 +116,6 @@
         this.mLogToLogcat = logToLogcat;
     }
 
-    /**
-     * Test function for automated integration tests. Can be also called manually from adb shell.
-     */
-    @VisibleForTesting
-    public static void testProtoLog() {
-        ProtoLog.e(ProtoLogGroup.TEST_GROUP,
-                "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
-                true, 1, 2, 3, 0.4, 0.5, 0.6, "ok");
-    }
-
     private static class Consts {
         private static final String TAG_WM = "WindowManager";
 
diff --git a/services/core/java/com/android/server/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
similarity index 92%
rename from services/core/java/com/android/server/protolog/ProtoLogImpl.java
rename to core/java/com/android/internal/protolog/ProtoLogImpl.java
index c9d42c8..6874f10 100644
--- a/services/core/java/com/android/server/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog;
+package com.android.internal.protolog;
 
-import static com.android.server.protolog.ProtoLogFileProto.LOG;
-import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER;
-import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
-import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
-import static com.android.server.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
-import static com.android.server.protolog.ProtoLogFileProto.VERSION;
-import static com.android.server.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
-import static com.android.server.protolog.ProtoLogMessage.DOUBLE_PARAMS;
-import static com.android.server.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
-import static com.android.server.protolog.ProtoLogMessage.MESSAGE_HASH;
-import static com.android.server.protolog.ProtoLogMessage.SINT64_PARAMS;
-import static com.android.server.protolog.ProtoLogMessage.STR_PARAMS;
+import static com.android.internal.protolog.ProtoLogFileProto.LOG;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
+import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
+import static com.android.internal.protolog.ProtoLogFileProto.VERSION;
+import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
+import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH;
+import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
 
 import android.annotation.Nullable;
 import android.os.ShellCommand;
@@ -36,10 +36,9 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.protolog.common.IProtoLogGroup;
-import com.android.server.protolog.common.LogDataType;
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogDataType;
 import com.android.internal.util.TraceBuffer;
-import com.android.server.wm.ProtoLogGroup;
 
 import java.io.File;
 import java.io.IOException;
@@ -62,7 +61,7 @@
      * Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
      * starting / stopping proto log, or enabling / disabling log groups.
      */
-    static Runnable sCacheUpdater = () -> { };
+    public static Runnable sCacheUpdater = () -> { };
 
     private static void addLogGroupEnum(IProtoLogGroup[] config) {
         for (IProtoLogGroup group : config) {
@@ -289,9 +288,7 @@
         }
     }
 
-
-    @VisibleForTesting
-    ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) {
+    public ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) {
         mLogFile = file;
         mBuffer = new TraceBuffer(bufferCapacity);
         mViewerConfig = viewerConfig;
diff --git a/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
similarity index 87%
rename from services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java
rename to core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 4944217..e381d30 100644
--- a/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog;
-
-import static com.android.server.protolog.ProtoLogImpl.logAndPrintln;
+package com.android.internal.protolog;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -80,16 +78,17 @@
                     // Not a messageHash - skip it
                 }
             }
-            logAndPrintln(pw, "Loaded " + mLogMessageMap.size() + " log definitions from "
-                    + viewerConfigFilename);
+            ProtoLogImpl.logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
+                    + " log definitions from " + viewerConfigFilename);
         } catch (FileNotFoundException e) {
-            logAndPrintln(pw, "Unable to load log definitions: File "
+            ProtoLogImpl.logAndPrintln(pw, "Unable to load log definitions: File "
                     + viewerConfigFilename + " not found." + e);
         } catch (IOException e) {
-            logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
+            ProtoLogImpl.logAndPrintln(pw,
+                    "Unable to load log definitions: IOException while reading "
                     + viewerConfigFilename + ". " + e);
         } catch (JSONException e) {
-            logAndPrintln(pw,
+            ProtoLogImpl.logAndPrintln(pw,
                     "Unable to load log definitions: JSON parsing exception while reading "
                             + viewerConfigFilename + ". " + e);
         }
diff --git a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java b/core/java/com/android/internal/protolog/common/BitmaskConversionException.java
similarity index 88%
rename from services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
rename to core/java/com/android/internal/protolog/common/BitmaskConversionException.java
index 7bb27b2..68b9d69 100644
--- a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
+++ b/core/java/com/android/internal/protolog/common/BitmaskConversionException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.internal.protolog.common;
 
 /**
  * Error while converting a bitmask representing a list of LogDataTypes.
diff --git a/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java b/core/java/com/android/internal/protolog/common/IProtoLogGroup.java
similarity index 93%
rename from services/core/java/com/android/server/protolog/common/IProtoLogGroup.java
rename to core/java/com/android/internal/protolog/common/IProtoLogGroup.java
index 2c65341..e3db468 100644
--- a/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/common/IProtoLogGroup.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.internal.protolog.common;
 
 /**
  * Defines a log group configuration object for ProtoLog. Should be implemented as en enum.
diff --git a/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java b/core/java/com/android/internal/protolog/common/InvalidFormatStringException.java
similarity index 89%
rename from services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
rename to core/java/com/android/internal/protolog/common/InvalidFormatStringException.java
index 947bf98..97d3dfb 100644
--- a/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
+++ b/core/java/com/android/internal/protolog/common/InvalidFormatStringException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.internal.protolog.common;
 
 /**
  * Unsupported/invalid message format string error.
diff --git a/services/core/java/com/android/server/protolog/common/LogDataType.java b/core/java/com/android/internal/protolog/common/LogDataType.java
similarity index 96%
rename from services/core/java/com/android/server/protolog/common/LogDataType.java
rename to core/java/com/android/internal/protolog/common/LogDataType.java
index e73b41a..651932a 100644
--- a/services/core/java/com/android/server/protolog/common/LogDataType.java
+++ b/core/java/com/android/internal/protolog/common/LogDataType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.internal.protolog.common;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/services/core/java/com/android/server/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/common/ProtoLog.java
similarity index 97%
rename from services/core/java/com/android/server/protolog/common/ProtoLog.java
rename to core/java/com/android/internal/protolog/common/ProtoLog.java
index b631bcb..ab58d35 100644
--- a/services/core/java/com/android/server/protolog/common/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/common/ProtoLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.internal.protolog.common;
 
 /**
  * ProtoLog API - exposes static logging methods. Usage of this API is similar
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 9bf0513..a23fc4b 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -291,7 +291,7 @@
             };
 
             Message msg = Message.obtain(null, screenshotType, screenshotRequest);
-            final ServiceConnection myConn = mScreenshotConnection;
+
             Handler h = new Handler(handler.getLooper()) {
                 @Override
                 public void handleMessage(Message msg) {
@@ -304,8 +304,8 @@
                             break;
                         case SCREENSHOT_MSG_PROCESS_COMPLETE:
                             synchronized (mScreenshotLock) {
-                                if (myConn != null && mScreenshotConnection == myConn) {
-                                    mContext.unbindService(myConn);
+                                if (mScreenshotConnection != null) {
+                                    mContext.unbindService(mScreenshotConnection);
                                     mScreenshotConnection = null;
                                     mScreenshotService = null;
                                 }
@@ -368,6 +368,7 @@
                 }
             } else {
                 Messenger messenger = new Messenger(mScreenshotService);
+
                 try {
                     messenger.send(msg);
                 } catch (RemoteException e) {
diff --git a/core/java/com/android/internal/util/StatLogger.java b/core/java/com/android/internal/util/StatLogger.java
index 29568d5..2d65090 100644
--- a/core/java/com/android/internal/util/StatLogger.java
+++ b/core/java/com/android/internal/util/StatLogger.java
@@ -84,7 +84,7 @@
      * give it back to the {@link #logDurationStat(int, long)}} after the event.
      */
     public long getTime() {
-        return SystemClock.elapsedRealtimeNanos() / 1000;
+        return SystemClock.uptimeNanos() / 1000;
     }
 
     /**
diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp
index b1f6000..2fba445 100644
--- a/core/jni/android_os_SystemClock.cpp
+++ b/core/jni/android_os_SystemClock.cpp
@@ -37,10 +37,12 @@
 static_assert(std::is_same<int64_t, jlong>::value, "jlong isn't an int64_t");
 static_assert(std::is_same<decltype(uptimeMillis()), int64_t>::value,
         "uptimeMillis signature change, expected int64_t return value");
+static_assert(std::is_same<decltype(uptimeNanos()), int64_t>::value,
+        "uptimeNanos signature change, expected int64_t return value");
 static_assert(std::is_same<decltype(elapsedRealtime()), int64_t>::value,
-        "uptimeMillis signature change, expected int64_t return value");
+        "elapsedRealtime signature change, expected int64_t return value");
 static_assert(std::is_same<decltype(elapsedRealtimeNano()), int64_t>::value,
-        "uptimeMillis signature change, expected int64_t return value");
+        "elapsedRealtimeNano signature change, expected int64_t return value");
 
 /*
  * native public static long currentThreadTimeMillis();
@@ -76,6 +78,7 @@
     // All of these are @CriticalNative, so we can defer directly to SystemClock.h for
     // some of these
     { "uptimeMillis", "()J", (void*) uptimeMillis },
+    { "uptimeNanos", "()J", (void*) uptimeNanos },
     { "elapsedRealtime", "()J", (void*) elapsedRealtime },
     { "elapsedRealtimeNanos", "()J", (void*) elapsedRealtimeNano },
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index d6a773f..85b4fe1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -116,7 +116,6 @@
     jfieldID width;
     jfieldID height;
     jfieldID useIdentityTransform;
-    jfieldID rotation;
 } gDisplayCaptureArgsClassInfo;
 
 static struct {
@@ -325,8 +324,6 @@
     captureArgs.useIdentityTransform =
             env->GetBooleanField(displayCaptureArgsObject,
                                  gDisplayCaptureArgsClassInfo.useIdentityTransform);
-    captureArgs.rotation = ui::toRotation(
-            env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.rotation));
     return captureArgs;
 }
 
@@ -1849,8 +1846,6 @@
             GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
     gDisplayCaptureArgsClassInfo.useIdentityTransform =
             GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
-    gDisplayCaptureArgsClassInfo.rotation =
-            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mRotation", "I");
 
     jclass layerCaptureArgsClazz =
             FindClassOrDie(env, "android/view/SurfaceControl$LayerCaptureArgs");
diff --git a/core/proto/android/app/tvsettings_enums.proto b/core/proto/android/app/tvsettings_enums.proto
index 31c5dd6..4a3c594 100644
--- a/core/proto/android/app/tvsettings_enums.proto
+++ b/core/proto/android/app/tvsettings_enums.proto
@@ -168,7 +168,11 @@
     // Google Assistant > Personal results (toggle)
     ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_PERSONAL_RESULTS = 0x12134000;
 
-    // Reserving [0x12140000, 0x12190000] for possible future settings
+    // TvSettings > Account & Sign In (Slice) > [A regular account] >
+    // Apps only mode (toggle)
+    ACCOUNT_SLICE_REG_ACCOUNT_APPS_ONLY_MODE = 0x12140000;
+
+    // Reserving [0x12150000, 0x12190000] for possible future settings
 
     // TvSettings > Account & Sign In (Slice) > [A regular account] > Remove
     ACCOUNT_SLICE_REG_ACCOUNT_REMOVE = 0x121A0000;
diff --git a/core/proto/android/server/protolog.proto b/core/proto/android/internal/protolog.proto
similarity index 98%
rename from core/proto/android/server/protolog.proto
rename to core/proto/android/internal/protolog.proto
index 34dc55b..fee7a87 100644
--- a/core/proto/android/server/protolog.proto
+++ b/core/proto/android/internal/protolog.proto
@@ -16,7 +16,7 @@
 
 syntax = "proto2";
 
-package com.android.server.protolog;
+package com.android.internal.protolog;
 
 option java_multiple_files = true;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fe290f3..edb9727 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1342,7 +1342,7 @@
         android:priority="800" />
 
     <!-- Allows an application to access data from sensors that the user uses to
-         measure what is happening inside his/her body, such as heart rate.
+         measure what is happening inside their body, such as heart rate.
          <p>Protection level: dangerous -->
     <permission android:name="android.permission.BODY_SENSORS"
         android:permissionGroup="android.permission-group.UNDEFINED"
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
index 630e16a..0f9bc4b 100644
--- a/core/tests/coretests/src/android/app/WindowContextTest.java
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -30,7 +30,6 @@
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -47,7 +46,6 @@
  * <p>This test class is a part of Window Manager Service tests and specified in
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
-@FlakyTest(bugId = 150812449, detail = "Remove after confirmed it's stable.")
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
index 8a36f1d..72391f4 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
@@ -33,9 +33,11 @@
     public void testEquals() {
         TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
         TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
         {
             TimeZoneCapabilities one = builder1.build();
@@ -57,6 +59,20 @@
             assertEquals(one, two);
         }
 
+        builder2.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
+        {
+            TimeZoneCapabilities one = builder1.build();
+            TimeZoneCapabilities two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder1.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
+        {
+            TimeZoneCapabilities one = builder1.build();
+            TimeZoneCapabilities two = builder2.build();
+            assertEquals(one, two);
+        }
+
         builder2.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
         {
             TimeZoneCapabilities one = builder1.build();
@@ -76,12 +92,16 @@
     public void testParcelable() {
         TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
         assertRoundTripParcelable(builder.build());
 
         builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
         assertRoundTripParcelable(builder.build());
 
+        builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
+        assertRoundTripParcelable(builder.build());
+
         builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
         assertRoundTripParcelable(builder.build());
     }
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
index ac7e9c4..00dc73e 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
@@ -29,8 +29,9 @@
 
     @Test
     public void testBuilder_copyConstructor() {
-        TimeZoneConfiguration.Builder builder1 =
-                new TimeZoneConfiguration.Builder().setAutoDetectionEnabled(true);
+        TimeZoneConfiguration.Builder builder1 = new TimeZoneConfiguration.Builder()
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true);
         TimeZoneConfiguration configuration1 = builder1.build();
 
         TimeZoneConfiguration configuration2 =
@@ -41,16 +42,15 @@
 
     @Test
     public void testIsComplete() {
-        TimeZoneConfiguration incompleteConfiguration =
-                new TimeZoneConfiguration.Builder()
-                        .build();
-        assertFalse(incompleteConfiguration.isComplete());
+        TimeZoneConfiguration.Builder builder =
+                new TimeZoneConfiguration.Builder();
+        assertFalse(builder.build().isComplete());
 
-        TimeZoneConfiguration completeConfiguration =
-                new TimeZoneConfiguration.Builder()
-                        .setAutoDetectionEnabled(true)
-                        .build();
-        assertTrue(completeConfiguration.isComplete());
+        builder.setAutoDetectionEnabled(true);
+        assertFalse(builder.build().isComplete());
+
+        builder.setGeoDetectionEnabled(true);
+        assertTrue(builder.build().isComplete());
     }
 
     @Test
@@ -122,6 +122,27 @@
             TimeZoneConfiguration two = builder2.build();
             assertEquals(one, two);
         }
+
+        builder1.setGeoDetectionEnabled(true);
+        {
+            TimeZoneConfiguration one = builder1.build();
+            TimeZoneConfiguration two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder2.setGeoDetectionEnabled(false);
+        {
+            TimeZoneConfiguration one = builder1.build();
+            TimeZoneConfiguration two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder1.setGeoDetectionEnabled(false);
+        {
+            TimeZoneConfiguration one = builder1.build();
+            TimeZoneConfiguration two = builder2.build();
+            assertEquals(one, two);
+        }
     }
 
     @Test
@@ -135,5 +156,11 @@
 
         builder.setAutoDetectionEnabled(false);
         assertRoundTripParcelable(builder.build());
+
+        builder.setGeoDetectionEnabled(false);
+        assertRoundTripParcelable(builder.build());
+
+        builder.setGeoDetectionEnabled(true);
+        assertRoundTripParcelable(builder.build());
     }
 }
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
index ddc977d..96df9dd 100644
--- a/core/tests/coretests/src/android/view/WindowMetricsTest.java
+++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java
@@ -27,7 +27,6 @@
 import android.os.Handler;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -46,7 +45,6 @@
  * <p>This test class is a part of Window Manager Service tests and specified in
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
-@FlakyTest(bugId = 148789183, detail = "Remove after confirmed it's stable.")
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 6e3fb19..d4fb1be 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -147,6 +147,19 @@
     // {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_LOOP_BACK} flag.
     public static final int TYPE_REMOTE_SUBMIX = 25;
 
+    /**
+     * A device type describing a Bluetooth Low Energy (BLE) audio headset or headphones.
+     * Headphones are grouped with headsets when the device is a sink:
+     * the features of headsets and headphones with regard to playback are the same.
+     */
+    public static final int TYPE_BLE_HEADSET   = 26;
+
+    /**
+     * A device type describing a Bluetooth Low Energy (BLE) audio speaker.
+     */
+    public static final int TYPE_BLE_SPEAKER   = 27;
+
+
     /** @hide */
     @IntDef(flag = false, prefix = "TYPE", value = {
             TYPE_BUILTIN_EARPIECE,
@@ -171,7 +184,9 @@
             TYPE_HEARING_AID,
             TYPE_BUILTIN_MIC,
             TYPE_FM_TUNER,
-            TYPE_TV_TUNER }
+            TYPE_TV_TUNER,
+            TYPE_BLE_HEADSET,
+            TYPE_BLE_SPEAKER}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceType {}
@@ -193,7 +208,8 @@
             TYPE_LINE_ANALOG,
             TYPE_LINE_DIGITAL,
             TYPE_IP,
-            TYPE_BUS }
+            TYPE_BUS,
+            TYPE_BLE_HEADSET}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceTypeIn {}
@@ -219,7 +235,9 @@
             TYPE_AUX_LINE,
             TYPE_IP,
             TYPE_BUS,
-            TYPE_HEARING_AID }
+            TYPE_HEARING_AID,
+            TYPE_BLE_HEADSET,
+            TYPE_BLE_SPEAKER}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceTypeOut {}
@@ -248,7 +266,8 @@
             case TYPE_BUS:
             case TYPE_HEARING_AID:
             case TYPE_BUILTIN_SPEAKER_SAFE:
-            case TYPE_REMOTE_SUBMIX:
+            case TYPE_BLE_HEADSET:
+            case TYPE_BLE_SPEAKER:
                 return true;
             default:
                 return false;
@@ -275,6 +294,7 @@
             case TYPE_IP:
             case TYPE_BUS:
             case TYPE_REMOTE_SUBMIX:
+            case TYPE_BLE_HEADSET:
                 return true;
             default:
                 return false;
@@ -527,6 +547,8 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_SPEAKER_SAFE,
                 TYPE_BUILTIN_SPEAKER_SAFE);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_HEADSET, TYPE_BLE_HEADSET);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_SPEAKER, TYPE_BLE_SPEAKER);
 
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -547,6 +569,7 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_IP, TYPE_IP);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUS, TYPE_BUS);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLE_HEADSET, TYPE_BLE_HEADSET);
 
         // privileges mapping to output device
         EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray();
@@ -576,6 +599,8 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BUILTIN_SPEAKER_SAFE,
                 AudioSystem.DEVICE_OUT_SPEAKER_SAFE);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
+        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
+        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
     }
 }
 
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 42d0f0c..f6b0454 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -70,7 +70,9 @@
      * {@link AudioManager#DEVICE_IN_USB_DEVICE}) use an address composed of the ALSA card number
      * and device number: "card=2;device=1"
      * - Bluetooth devices ({@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO},
-     * {@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, {@link AudioManager#DEVICE_OUT_BLUETOOTH_A2DP})
+     * {@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO},
+     * {@link AudioManager#DEVICE_OUT_BLUETOOTH_A2DP}),
+     * {@link AudioManager#DEVICE_OUT_BLE_HEADSET}, {@link AudioManager#DEVICE_OUT_BLE_SPEAKER})
      * use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by
      * {@link BluetoothDevice#getAddress()}.
      * - Deivces that do not have an address will indicate an empty string "".
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 408f34b..b2e0538 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4413,6 +4413,18 @@
      */
     public static final int DEVICE_OUT_FM = AudioSystem.DEVICE_OUT_FM;
     /** @hide
+     * The audio output device code for echo reference injection point.
+     */
+    public static final int DEVICE_OUT_ECHO_CANCELLER = AudioSystem.DEVICE_OUT_ECHO_CANCELLER;
+    /** @hide
+     * The audio output device code for a BLE audio headset.
+     */
+    public static final int DEVICE_OUT_BLE_HEADSET = AudioSystem.DEVICE_OUT_BLE_HEADSET;
+    /** @hide
+     * The audio output device code for a BLE audio speaker.
+     */
+    public static final int DEVICE_OUT_BLE_SPEAKER = AudioSystem.DEVICE_OUT_BLE_SPEAKER;
+    /** @hide
      * This is not used as a returned value from {@link #getDevicesForStream}, but could be
      *  used in the future in a set method to select whatever default device is chosen by the
      *  platform-specific implementation.
@@ -4496,6 +4508,14 @@
      * The audio input device code for audio loopback
      */
     public static final int DEVICE_IN_LOOPBACK = AudioSystem.DEVICE_IN_LOOPBACK;
+    /** @hide
+     * The audio input device code for an echo reference capture point.
+     */
+    public static final int DEVICE_IN_ECHO_REFERENCE = AudioSystem.DEVICE_IN_ECHO_REFERENCE;
+    /** @hide
+     * The audio input device code for a BLE audio headset.
+     */
+    public static final int DEVICE_IN_BLE_HEADSET = AudioSystem.DEVICE_IN_BLE_HEADSET;
 
     /**
      * Return true if the device code corresponds to an output device.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index da52cfe..5fe5c05 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -866,6 +866,12 @@
     public static final int DEVICE_OUT_USB_HEADSET = 0x4000000;
     /** @hide */
     public static final int DEVICE_OUT_HEARING_AID = 0x8000000;
+    /** @hide */
+    public static final int DEVICE_OUT_ECHO_CANCELLER = 0x10000000;
+    /** @hide */
+    public static final int DEVICE_OUT_BLE_HEADSET = 0x20000000;
+    /** @hide */
+    public static final int DEVICE_OUT_BLE_SPEAKER = 0x20000001;
 
     /** @hide */
     public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
@@ -890,6 +896,8 @@
     public static final Set<Integer> DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO_SET;
     /** @hide */
     public static final Set<Integer> DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER_SET;
+    /** @hide */
+    public static final Set<Integer> DEVICE_OUT_ALL_BLE_SET;
     static {
         DEVICE_OUT_ALL_SET = new HashSet<>();
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_EARPIECE);
@@ -920,6 +928,9 @@
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_PROXY);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_USB_HEADSET);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_HEARING_AID);
+        DEVICE_OUT_ALL_SET.add(DEVICE_OUT_ECHO_CANCELLER);
+        DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_HEADSET);
+        DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_SPEAKER);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_DEFAULT);
 
         DEVICE_OUT_ALL_A2DP_SET = new HashSet<>();
@@ -945,6 +956,10 @@
         DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER_SET = new HashSet<>();
         DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER_SET.addAll(DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO_SET);
         DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER_SET.add(DEVICE_OUT_SPEAKER);
+
+        DEVICE_OUT_ALL_BLE_SET = new HashSet<>();
+        DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_HEADSET);
+        DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_SPEAKER);
     }
 
     // input devices
@@ -1019,6 +1034,8 @@
     /** @hide */
     public static final int DEVICE_IN_ECHO_REFERENCE = DEVICE_BIT_IN | 0x10000000;
     /** @hide */
+    public static final int DEVICE_IN_BLE_HEADSET = DEVICE_BIT_IN | 0x20000000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
 
@@ -1056,6 +1073,7 @@
         DEVICE_IN_ALL_SET.add(DEVICE_IN_BLUETOOTH_BLE);
         DEVICE_IN_ALL_SET.add(DEVICE_IN_HDMI_ARC);
         DEVICE_IN_ALL_SET.add(DEVICE_IN_ECHO_REFERENCE);
+        DEVICE_IN_ALL_SET.add(DEVICE_IN_BLE_HEADSET);
         DEVICE_IN_ALL_SET.add(DEVICE_IN_DEFAULT);
 
         DEVICE_IN_ALL_SCO_SET = new HashSet<>();
@@ -1118,6 +1136,9 @@
     /** @hide */ public static final String DEVICE_OUT_PROXY_NAME = "proxy";
     /** @hide */ public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
     /** @hide */ public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out";
+    /** @hide */ public static final String DEVICE_OUT_ECHO_CANCELLER_NAME = "echo_canceller";
+    /** @hide */ public static final String DEVICE_OUT_BLE_HEADSET_NAME = "ble_headset";
+    /** @hide */ public static final String DEVICE_OUT_BLE_SPEAKER_NAME = "ble_speaker";
 
     /** @hide */ public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
     /** @hide */ public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
@@ -1145,6 +1166,7 @@
     /** @hide */ public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble";
     /** @hide */ public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference";
     /** @hide */ public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc";
+    /** @hide */ public static final String DEVICE_IN_BLE_HEADSET_NAME = "ble_headset";
 
     /** @hide */
     @UnsupportedAppUsage
@@ -1207,6 +1229,12 @@
             return DEVICE_OUT_USB_HEADSET_NAME;
         case DEVICE_OUT_HEARING_AID:
             return DEVICE_OUT_HEARING_AID_NAME;
+        case DEVICE_OUT_ECHO_CANCELLER:
+            return DEVICE_OUT_ECHO_CANCELLER_NAME;
+        case DEVICE_OUT_BLE_HEADSET:
+            return DEVICE_OUT_BLE_HEADSET_NAME;
+        case DEVICE_OUT_BLE_SPEAKER:
+            return DEVICE_OUT_BLE_SPEAKER_NAME;
         case DEVICE_OUT_DEFAULT:
         default:
             return Integer.toString(device);
@@ -1269,6 +1297,8 @@
             return DEVICE_IN_ECHO_REFERENCE_NAME;
         case DEVICE_IN_HDMI_ARC:
             return DEVICE_IN_HDMI_ARC_NAME;
+        case DEVICE_IN_BLE_HEADSET:
+            return DEVICE_IN_BLE_HEADSET_NAME;
         case DEVICE_IN_DEFAULT:
         default:
             return Integer.toString(device);
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index e959e8e..e1bff03 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -225,13 +225,50 @@
             job.updateStatusAndResult(TranscodingJob.STATUS_FINISHED,
                     TranscodingJob.RESULT_ERROR);
 
-            // Notifies client the job is done.
+            // Notifies client the job failed.
             if (job.mListener != null && job.mListenerExecutor != null) {
                 job.mListenerExecutor.execute(() -> job.mListener.onTranscodingFinished(job));
             }
         }
     }
 
+    private void handleTranscodingProgressUpdate(int jobId, int newProgress) {
+        synchronized (mPendingTranscodingJobs) {
+            // Gets the job associated with the jobId.
+            final TranscodingJob job = mPendingTranscodingJobs.get(jobId);
+
+            if (job == null) {
+                // This should not happen in reality.
+                Log.e(TAG, "Job " + jobId + " is not in PendingJobs");
+                return;
+            }
+
+            // Updates the job progress.
+            job.updateProgress(newProgress);
+
+            // Notifies client the progress update.
+            if (job.mProgressUpdateExecutor != null && job.mProgressUpdateListener != null) {
+                job.mProgressUpdateExecutor.execute(
+                        () -> job.mProgressUpdateListener.onProgressUpdate(newProgress));
+            }
+        }
+    }
+
+    private void updateStatus(int jobId, int status) {
+        synchronized (mPendingTranscodingJobs) {
+            final TranscodingJob job = mPendingTranscodingJobs.get(jobId);
+
+            if (job == null) {
+                // This should not happen in reality.
+                Log.e(TAG, "Job " + jobId + " is not in PendingJobs");
+                return;
+            }
+
+            // Updates the job status.
+            job.updateStatus(status);
+        }
+    }
+
     // Just forwards all the events to the event handler.
     private ITranscodingClientCallback mTranscodingClientCallback =
             new ITranscodingClientCallback.Stub() {
@@ -263,17 +300,17 @@
 
                 @Override
                 public void onTranscodingStarted(int jobId) throws RemoteException {
-
+                    updateStatus(jobId, TranscodingJob.STATUS_RUNNING);
                 }
 
                 @Override
                 public void onTranscodingPaused(int jobId) throws RemoteException {
-
+                    updateStatus(jobId, TranscodingJob.STATUS_PAUSED);
                 }
 
                 @Override
                 public void onTranscodingResumed(int jobId) throws RemoteException {
-
+                    updateStatus(jobId, TranscodingJob.STATUS_RUNNING);
                 }
 
                 @Override
@@ -294,8 +331,8 @@
                 }
 
                 @Override
-                public void onProgressUpdate(int jobId, int progress) throws RemoteException {
-                    //TODO(hkuang): Implement this.
+                public void onProgressUpdate(int jobId, int newProgress) throws RemoteException {
+                    handleTranscodingProgressUpdate(jobId, newProgress);
                 }
             };
 
@@ -661,11 +698,14 @@
         public static final int STATUS_RUNNING = 2;
         /** The job is finished. */
         public static final int STATUS_FINISHED = 3;
+        /** The job is paused. */
+        public static final int STATUS_PAUSED = 4;
 
         @IntDef(prefix = { "STATUS_" }, value = {
                 STATUS_PENDING,
                 STATUS_RUNNING,
                 STATUS_FINISHED,
+                STATUS_PAUSED,
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface Status {}
@@ -821,17 +861,12 @@
             return mResult;
         }
 
-        private void setJobProgress(int newProgress) {
-            synchronized (this) {
-                mProgress = newProgress;
-            }
+        private synchronized void updateProgress(int newProgress) {
+            mProgress = newProgress;
+        }
 
-            // Notify listener.
-            OnProgressUpdateListener onProgressUpdateListener = mProgressUpdateListener;
-            if (mProgressUpdateListener != null) {
-                mProgressUpdateExecutor.execute(
-                        () -> onProgressUpdateListener.onProgressUpdate(mProgress));
-            }
+        private synchronized void updateStatus(int newStatus) {
+            mStatus = newStatus;
         }
     }
 
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 3a5e692..8fe1088 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -33,10 +33,12 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /*
  * Functional tests for MediaTranscodeManager in the media framework.
@@ -230,5 +232,73 @@
                 30, TimeUnit.MILLISECONDS);
         assertTrue("Fails to cancel transcoding", finishedOnTime);
     }
+
+
+    @Test
+    public void testTranscodingProgressUpdate() throws Exception {
+        Log.d(TAG, "Starting: testMediaTranscodeManager");
+
+        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+        final CountDownLatch statusLatch = new CountDownLatch(1);
+
+        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        // The full path of this file is:
+        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+                + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
+
+        TranscodingRequest request =
+                new TranscodingRequest.Builder()
+                        .setSourceUri(mSourceHEVCVideoUri)
+                        .setDestinationUri(destinationUri)
+                        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                        .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                        .setVideoTrackFormat(createMediaFormat())
+                        .build();
+        Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+        Log.i(TAG, "transcoding to " + createMediaFormat());
+
+        TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+                transcodingJob -> {
+                    Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+                    assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+                    transcodeCompleteSemaphore.release();
+                });
+        assertNotNull(job);
+
+        AtomicInteger progressUpdateCount = new AtomicInteger(0);
+
+        // Set progress update executor and use the same executor as result listener.
+        job.setOnProgressUpdateListener(listenerExecutor,
+                new TranscodingJob.OnProgressUpdateListener() {
+                    int mPreviousProgress = 0;
+
+                    @Override
+                    public void onProgressUpdate(int newProgress) {
+                        assertTrue("Invalid proress update", newProgress > mPreviousProgress);
+                        assertTrue("Invalid proress update", newProgress <= 100);
+                        if (newProgress > 0) {
+                            statusLatch.countDown();
+                        }
+                        mPreviousProgress = newProgress;
+                        progressUpdateCount.getAndIncrement();
+                        Log.i(TAG, "Get progress update " + newProgress);
+                    }
+                });
+
+        try {
+            statusLatch.await();
+            // The transcoding should not be finished yet as the clip is long.
+            assertTrue("Invalid status", job.getStatus() == TranscodingJob.STATUS_RUNNING);
+        } catch (InterruptedException e) { }
+
+        Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
+        boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+                TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue("Transcode failed to complete in time.", finishedOnTime);
+        assertTrue("Failed to receive at least 10 progress updates",
+                progressUpdateCount.get() > 10);
+    }
 }
 
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 70ef69d..51e7287 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -5596,6 +5596,7 @@
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
     method public int getSemanticAction();
+    method public boolean isAuthenticationRequired();
     method public boolean isContextual();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
@@ -5625,6 +5626,7 @@
     method @NonNull public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
     method @NonNull public android.os.Bundle getExtras();
     method @NonNull public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean);
+    method @NonNull public android.app.Notification.Action.Builder setAuthenticationRequired(boolean);
     method @NonNull public android.app.Notification.Action.Builder setContextual(boolean);
     method @NonNull public android.app.Notification.Action.Builder setSemanticAction(int);
   }
@@ -24047,6 +24049,8 @@
     method public boolean isSink();
     method public boolean isSource();
     field public static final int TYPE_AUX_LINE = 19; // 0x13
+    field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
+    field public static final int TYPE_BLE_SPEAKER = 27; // 0x1b
     field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
     field public static final int TYPE_BLUETOOTH_SCO = 7; // 0x7
     field public static final int TYPE_BUILTIN_EARPIECE = 1; // 0x1
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 86ac3e4..35b483b 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -54,6 +54,7 @@
   }
 
   public interface Parcelable {
+    method public default int getStability();
     field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
     field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
   }
diff --git a/packages/CarSystemUI/samples/sample1/rro/Android.bp b/packages/CarSystemUI/samples/sample1/rro/Android.bp
new file mode 100644
index 0000000..5b0347f
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample1/rro/Android.bp
@@ -0,0 +1,27 @@
+//
+// 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.
+//
+
+android_app {
+    name: "CarSystemUISampleOneRRO",
+    resource_dirs: ["res"],
+    certificate: "platform",
+    platform_apis: true,
+    manifest: "AndroidManifest.xml",
+    aaptflags: [
+        "--no-resource-deduping",
+        "--no-resource-removal",
+     ]
+}
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample1/rro/AndroidManifest.xml b/packages/CarSystemUI/samples/sample1/rro/AndroidManifest.xml
new file mode 100644
index 0000000..5c25056
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample1/rro/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.systemui.rro">
+    <overlay
+        android:targetPackage="com.android.systemui"
+        android:isStatic="false"
+        android:resourcesMap="@xml/car_sysui_overlays"
+    />
+</manifest>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample1/rro/res/values/config.xml b/packages/CarSystemUI/samples/sample1/rro/res/values/config.xml
new file mode 100644
index 0000000..854ab7d
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample1/rro/res/values/config.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <!-- Configure which system bars should be displayed. -->
+    <bool name="config_enableTopNavigationBar">true</bool>
+    <bool name="config_enableLeftNavigationBar">true</bool>
+    <bool name="config_enableRightNavigationBar">true</bool>
+    <bool name="config_enableBottomNavigationBar">true</bool>
+
+    <!-- Configure the type of each system bar. Each system bar must have a unique type. -->
+    <!--    STATUS_BAR = 0-->
+    <!--    NAVIGATION_BAR = 1-->
+    <!--    STATUS_BAR_EXTRA = 2-->
+    <!--    NAVIGATION_BAR_EXTRA = 3-->
+    <integer name="config_topSystemBarType">0</integer>
+    <integer name="config_leftSystemBarType">2</integer>
+    <integer name="config_rightSystemBarType">3</integer>
+    <integer name="config_bottomSystemBarType">1</integer>
+
+    <!-- Configure the relative z-order among the system bars. When two system bars overlap (e.g.
+         if both top bar and left bar are enabled, it creates an overlapping space in the upper left
+         corner), the system bar with the higher z-order takes the overlapping space and padding is
+         applied to the other bar.-->
+    <!-- NOTE: If two overlapping system bars have the same z-order, SystemBarConfigs will throw a
+         RuntimeException, since their placing order cannot be determined. Bars that do not overlap
+         are allowed to have the same z-order. -->
+    <!-- NOTE: If the z-order of a bar is 10 or above, it will also appear on top of HUN's.    -->
+    <integer name="config_topSystemBarZOrder">1</integer>
+    <integer name="config_leftSystemBarZOrder">0</integer>
+    <integer name="config_rightSystemBarZOrder">0</integer>
+    <integer name="config_bottomSystemBarZOrder">10</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml
new file mode 100644
index 0000000..7bcb8e1
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml
@@ -0,0 +1,33 @@
+
+<!--
+  ~ 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.
+  -->
+
+<overlay>
+    <item target="bool/config_enableTopNavigationBar" value="@bool/config_enableTopNavigationBar"/>
+    <item target="bool/config_enableLeftNavigationBar" value="@bool/config_enableLeftNavigationBar"/>
+    <item target="bool/config_enableRightNavigationBar" value="@bool/config_enableRightNavigationBar"/>
+    <item target="bool/config_enableBottomNavigationBar" value="@bool/config_enableBottomNavigationBar"/>
+
+    <item target="integer/config_topSystemBarType" value="@integer/config_topSystemBarType"/>
+    <item target="integer/config_leftSystemBarType" value="@integer/config_leftSystemBarType"/>
+    <item target="integer/config_rightSystemBarType" value="@integer/config_rightSystemBarType"/>
+    <item target="integer/config_bottomSystemBarType" value="@integer/config_bottomSystemBarType"/>
+
+    <item target="integer/config_topSystemBarZOrder" value="@integer/config_topSystemBarZOrder"/>
+    <item target="integer/config_leftSystemBarZOrder" value="@integer/config_leftSystemBarZOrder"/>
+    <item target="integer/config_rightSystemBarZOrder" value="@integer/config_rightSystemBarZOrder"/>
+    <item target="integer/config_bottomSystemBarZOrder" value="@integer/config_bottomSystemBarZOrder"/>
+</overlay>
\ No newline at end of file
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index 8efbad6..2c4545e 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -155,41 +155,6 @@
         assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location);
     }
 
-    @Test
-    public void testBypassRequest() throws Exception {
-        LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000,
-                0, false).setQuality(LocationRequest.POWER_HIGH).setLocationSettingsIgnored(true);
-
-        mProvider.setRequest(
-                new ProviderRequest.Builder()
-                        .setInterval(1000)
-                        .setLocationSettingsIgnored(true)
-                        .setLocationRequests(Collections.singletonList(request))
-                        .build(),
-                new WorkSource());
-
-        boolean containsNetworkBypass = false;
-        for (LocationRequest iRequest : mLocationManager.getTestProviderCurrentRequests(
-                NETWORK_PROVIDER)) {
-            if (iRequest.isLocationSettingsIgnored()) {
-                containsNetworkBypass = true;
-                break;
-            }
-        }
-
-        boolean containsGpsBypass = false;
-        for (LocationRequest iRequest : mLocationManager.getTestProviderCurrentRequests(
-                GPS_PROVIDER)) {
-            if (iRequest.isLocationSettingsIgnored()) {
-                containsGpsBypass = true;
-                break;
-            }
-        }
-
-        assertThat(containsNetworkBypass).isTrue();
-        assertThat(containsGpsBypass).isTrue();
-    }
-
     private static class LocationProviderManagerCapture extends ILocationProviderManager.Stub {
 
         private final LinkedBlockingQueue<Location> mLocations;
diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java
index 8e01619..85f2552 100644
--- a/packages/Shell/src/com/android/shell/Screenshooter.java
+++ b/packages/Shell/src/com/android/shell/Screenshooter.java
@@ -17,11 +17,8 @@
 package com.android.shell;
 
 import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
+import android.os.IBinder;
 import android.util.Log;
-import android.view.Display;
 import android.view.SurfaceControl;
 
 /**
@@ -40,22 +37,17 @@
      * @return The screenshot bitmap on success, null otherwise.
      */
     static Bitmap takeScreenshot() {
-        Display display = DisplayManagerGlobal.getInstance()
-                .getRealDisplay(Display.DEFAULT_DISPLAY);
-        Point displaySize = new Point();
-        display.getRealSize(displaySize);
-        final int displayWidth = displaySize.x;
-        final int displayHeight = displaySize.y;
-
-        int rotation = display.getRotation();
-        Rect crop = new Rect(0, 0, displayWidth, displayHeight);
-        Log.d(TAG, "Taking screenshot of dimensions " + displayWidth + " x " + displayHeight);
+        Log.d(TAG, "Taking fullscreen screenshot");
         // Take the screenshot
-        Bitmap screenShot =
-                SurfaceControl.screenshot(crop, displayWidth, displayHeight, rotation);
+        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+        final SurfaceControl.DisplayCaptureArgs captureArgs =
+                new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+                        .build();
+        final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+                SurfaceControl.captureDisplay(captureArgs);
+        final Bitmap screenShot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
         if (screenShot == null) {
-            Log.e(TAG, "Failed to take screenshot of dimensions " + displayWidth + " x "
-                    + displayHeight);
+            Log.e(TAG, "Failed to take fullscreen screenshot");
             return null;
         }
 
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 53eb234..401f3e3 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -27,6 +27,7 @@
         <item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
         <item name="android:textSize">14dp</item>
         <item name="android:background">@drawable/kg_emergency_button_background</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:paddingLeft">12dp</item>
         <item name="android:paddingRight">12dp</item>
     </style>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index ee87002..bac915d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laai tans aanbevelings"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Versteek die huidige sessie."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Versteek"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Maak toe"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 03eb396..aedbded 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ደብቅ"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 95d54a2..fee86ca 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1093,7 +1093,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"إخفاء"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"غير نشط، تحقّق من التطبيق."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 3023a27..3778df8 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"লুকুৱাওক"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"সক্ৰিয় নহয়, এপ্‌টো পৰীক্ষা কৰক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 3c8e04b..874bf74 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Gizlədin"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 4b400eb..2a120c5 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1075,7 +1075,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index f1064fb..e4bb67a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Схаваць"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактыўна, праверце праграму"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 8ba2a1e..c51e58c 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Препоръките се зареждат"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мултимедия"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Скриване на текущата сесия."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Скриване"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, проверете прилож."</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5861c44..3d00ca8 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"লুকান"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"বন্ধ আছে, অ্যাপ চেক করুন"</string>
@@ -1084,8 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"নতুন কন্ট্রোল দেখতে পাওয়ার বোতাম টিপে ধরে থাকুন"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"কন্ট্রোল যোগ করুন"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"কন্ট্রোল এডিট করুন"</string>
-    <!-- no translation found for one_handed_tutorial_title (6312198435090726656) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (7674850272340517174) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"\'এক হাতে ব্যবহার করার মোড\'-এর ব্যবহার"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"বেরিয়ে আসার জন্য, স্ক্রিনের নিচ থেকে উপরের দিকে সোয়াইপ করুন অথবা অ্যাপের আইকনের উপরে যেকোনও জায়গায় ট্যাপ করুন"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 984cbeb..1425984 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1075,7 +1075,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, vidite aplikaciju"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index b8c41ef..b150155 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Amaga"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index f946cc4..ae6cc13 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skrýt"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index a6de8a6..09f7f1a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medie"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skjul"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 372cc11..2410e47 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ausblenden"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string>
@@ -1084,8 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Zum Anzeigen der Karten für neue Geräte Ein-/Aus-Taste gedrückt halten"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Steuerelemente hinzufügen"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Steuerelemente bearbeiten"</string>
-    <!-- no translation found for one_handed_tutorial_title (6312198435090726656) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (7674850272340517174) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Einhandmodus verwenden"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Wenn du die App schließen möchtest, wische vom unteren Rand des Displays nach oben oder tippe auf eine beliebige Stelle oberhalb der App"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index b467347..f66c5a8 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Απόκρυψη"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Ανενεργό, έλεγχος εφαρμογής"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 7fa7f83..0e22b58 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index f73dad3..acf087d 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 7fa7f83..0e22b58 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 7fa7f83..0e22b58 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 6140bf1..4f4238a 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‎‎Loading recommendations‎‏‎‎‏‎"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎Media‎‏‎‎‏‎"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎Hide the current session.‎‏‎‎‏‎"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎Hide‎‏‎‎‏‎"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎Dismiss‎‏‎‎‏‎"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎Resume‎‏‎‎‏‎"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎Settings‎‏‎‎‏‎"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎Inactive, check app‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 6818116..45e0724 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index f09a0d2..f7ebbd0 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 6236f25..1e5e49e 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Peida"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 167a09c..38b2103d 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ezkutatu"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Baztertu"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktibo; egiaztatu aplikazioa"</string>
@@ -1085,5 +1085,5 @@
     <string name="controls_menu_add" msgid="4447246119229920050">"Gehitu aukerak"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editatu aukerak"</string>
     <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Esku bakarreko modua erabiltzea"</string>
-    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ateratzeko, pasatu hatza pantailaren behealdetik gora edo sakatu aplikazioaren gainean, edonon"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ateratzeko, pasatu hatza pantailaren behealdetik gora edo sakatu aplikazioaren gainaldea"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 0555052..d39d0c3 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"درحال بار کردن توصیه‌ها"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"رسانه"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"جلسه فعلی پنهان شود."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"پنهان کردن"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"رد کردن"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"غیرفعال، برنامه را بررسی کنید"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ec9bbc4..fa076c4 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Piilota"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 6c5387f..9c418f7 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Masquer"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3a2ae07..3e2f5fd 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Masquer"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 25c7b4b..9ecd75e 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 17cc5a4..6e05a54 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"છુપાવો"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"નિષ્ક્રિય, ઍપને ચેક કરો"</string>
@@ -1084,8 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"નવા નિયંત્રણ જોવા માટે પાવર બટનને દબાવી રાખો"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"નિયંત્રણો ઉમેરો"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"નિયંત્રણોમાં ફેરફાર કરો"</string>
-    <!-- no translation found for one_handed_tutorial_title (6312198435090726656) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (7674850272340517174) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"એક-હાથે વાપરો મોડનો ઉપયોગ કરી રહ્યાં છીએ"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"બહાર નીકળવા માટે, સ્ક્રીનની નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરો અથવા ઍપના આઇકન પર ગમે ત્યાં ટૅપ કરો"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 97ae7b6..de4fc48 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1071,7 +1071,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सुझाव लोड हो रहे हैं"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"इस मीडिया सेशन को छिपाएं."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"छिपाएं"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"खारिज करें"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"काम नहीं कर रहा, ऐप जांचें"</string>
@@ -1086,6 +1086,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"नए कंट्रोल देखने के लिए पावर बटन दबाकर रखें"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"कंट्राेल जोड़ें"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"कंट्रोल मेन्यू में बदलाव करें"</string>
-    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"वन-हैंडेड मोड का इस्तेमाल करें"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"वन-हैंडेड मोड का इस्तेमाल करना"</string>
     <string name="one_handed_tutorial_description" msgid="7674850272340517174">"इसे बंद करने के लिए, स्क्रीन के सबसे निचले हिस्से से ऊपर की ओर स्वाइप करें या ऐप्लिकेशन के आइकॉन के ऊपर कहीं भी टैप करें"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9e17735..4bebe79 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1075,7 +1075,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, provjerite aplik."</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 804587f..42e251f 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Elrejtés"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenőrizze az appot"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 08ef489..e6b7139 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Բեռնման խորհուրդներ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Մեդիա"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Թաքցրեք ընթացիկ աշխատաշրջանը"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Թաքցնել"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Ակտիվ չէ, ստուգեք հավելվածը"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index ba9eca3..fec4205 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sembunyikan"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 6295813..3a9e63b 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fela"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index ef862cb..3eca501 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Nascondi"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 358946d..e88c951 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"הסתרה"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"לא פעיל, יש לבדוק את האפליקציה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 9cb872bf..130f682 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"メディア"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"非表示"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"再開"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"無効: アプリをご確認ください"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index ef6ff128..d050875 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"მიმდინარეობს რეკომენდაციების ჩატვირთვა"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"მედია"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"დაიმალოს მიმდინარე სესია"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"დამალვა"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"არააქტიურია, გადაამოწმეთ აპი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 08b1f62..36c726e 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Жасыру"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Өшірулі. Қолданба тексеріңіз."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 6d80ee4..9d87c58 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុក​ការណែនាំ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គ​បច្ចុប្បន្ន។"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"លាក់"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"អសកម្ម ពិនិត្យមើល​កម្មវិធី"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index db20864..901f024 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ಮರೆಮಾಡಿ"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b43eb41..8a64155 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"미디어"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"숨기기"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"비활성. 앱을 확인하세요."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 02e468a..3cf4126 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Жашыруу"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Жигерсиз. Колдонмону текшериңиз"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 030bdc1..b1c5a5c 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ເຊື່ອງ"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6f48741..4e9b802 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medija"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Slėpti"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index bdea26a..a4c4666 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1075,7 +1075,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Paslēpt"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index c9f3777..7e623ad 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Сокриј"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивна, провери апликација"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index c968c28..35e6427 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്‌ക്കുക."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"മറയ്‌ക്കുക"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"നിഷ്‌ക്രിയം, ആപ്പ് പരിശോധിക്കൂ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 9ce6779..b4690d4 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Нуух"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Идэвхгүй байна, аппыг шалгана уу"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index d712679..4ea965a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"लपवा"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय, ॲप तपासा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 01e918a..38ee25c 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sembunyikan"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 23b0a06..150ed94e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"အကြံပြုချက်များ ဖွင့်နေသည်"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"မီဒီယာ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"လက်ရှိ စက်ရှင်ကို ဖျောက်ထားမည်။"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ဖျောက်ထားမည်"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ပယ်ရန်"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ရပ်နေသည်၊ အက်ပ်ကို စစ်ဆေးပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index b0a593f..d872a89 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laster inn anbefalinger"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medier"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den nåværende økten."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skjul"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 09462cf..1974e80 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -731,11 +731,11 @@
     <string name="see_more_title" msgid="7409317011708185729">"थप हेर्नुहोस्"</string>
     <string name="appops_camera" msgid="5215967620896725715">"यो अनुप्रयोगले क्यामेराको प्रयोग गर्दै छ।"</string>
     <string name="appops_microphone" msgid="8805468338613070149">"यो अनुप्रयोगले माइक्रोफोनको प्रयोग गर्दै छ।"</string>
-    <string name="appops_overlay" msgid="4822261562576558490">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमाथि प्रदर्शन गर्दै छ।"</string>
+    <string name="appops_overlay" msgid="4822261562576558490">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य एपमाथि प्रदर्शन गर्दै छ।"</string>
     <string name="appops_camera_mic" msgid="7032239823944420431">"यो अनुप्रयोगले माइक्रोफोन र क्यामेराको प्रयोग गर्दै छ।"</string>
-    <string name="appops_camera_overlay" msgid="6466845606058816484">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमाथि प्रदर्शन गर्नुका साथै क्यामेराको प्रयोग गर्दै छ।"</string>
-    <string name="appops_mic_overlay" msgid="4609326508944233061">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमाथि प्रदर्शन गर्नुका साथै माइक्रोफोनको प्रयोग गर्दै छ।"</string>
-    <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमाथि प्रदर्शन गर्नुका साथै माइक्रोफोन र क्यामेराको प्रयोग गर्दै छ।"</string>
+    <string name="appops_camera_overlay" msgid="6466845606058816484">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य एपमाथि प्रदर्शन गर्नुका साथै क्यामेराको प्रयोग गर्दै छ।"</string>
+    <string name="appops_mic_overlay" msgid="4609326508944233061">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य एपमाथि प्रदर्शन गर्नुका साथै माइक्रोफोनको प्रयोग गर्दै छ।"</string>
+    <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य एपमाथि प्रदर्शन गर्नुका साथै माइक्रोफोन र क्यामेराको प्रयोग गर्दै छ।"</string>
     <string name="notification_appops_settings" msgid="5208974858340445174">"सेटिङहरू"</string>
     <string name="notification_appops_ok" msgid="2177609375872784124">"ठिक छ"</string>
     <string name="feedback_silenced" msgid="5382212321253328247">"सिस्टमले यो सूचना आउँदा बज्ने ध्वनि बन्द गरेको छ।"</string>
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"लुकाउनुहोस्"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string>
@@ -1084,8 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"नयाँ नियन्त्रण सुविधाहरू हेर्न पावर बटन थिचिराख्नुहोस्"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"नियन्त्रण सुविधाहरू थप्नुहोस्"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"नियन्त्रण सुविधाहरू सम्पादन गर्नु…"</string>
-    <!-- no translation found for one_handed_tutorial_title (6312198435090726656) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (7674850272340517174) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"एक हाते मोड प्रयोग गरिँदै छ"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"बाहिर निस्कन, स्क्रिनको पुछारबाट माथितिर स्वाइप गर्नुहोस् वा एपभन्दा माथि जुनसुकै ठाउँमा ट्याप गर्नुहोस्"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 2385017..9da8afa 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Verbergen"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index d5c077b..5b5cbc6 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ଲୁଚାନ୍ତୁ"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
@@ -1084,8 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"ନୂଆ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ପାୱାର ବଟନକୁ ଧରି ରଖନ୍ତୁ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ସମ୍ପାଦନ କରନ୍ତୁ"</string>
-    <!-- no translation found for one_handed_tutorial_title (6312198435090726656) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (7674850272340517174) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ଏକ-ହାତ ମୋଡ୍ ବ୍ୟବହାର କରି"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"ବାହାରି ଯିବା ପାଇଁ, ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ କିମ୍ବା ଆପ୍ ଆଇକନର ଉପରେ ଯେ କୌଣସି ସ୍ଥାନରେ ଟାପ୍ କରନ୍ତୁ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 1156782..5c31ce7 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ਲੁਕਾਓ"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
@@ -1084,8 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"ਨਵੇਂ ਕੰਟਰੋਲ ਦੇਖਣ ਲਈ ਪਾਵਰ ਬਟਨ ਦਬਾਈ ਰੱਖੋ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ਕੰਟਰੋਲਾਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
-    <!-- no translation found for one_handed_tutorial_title (6312198435090726656) -->
-    <skip />
-    <!-- no translation found for one_handed_tutorial_description (7674850272340517174) -->
-    <skip />
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ਇੱਕ ਹੱਥ ਮੋਡ ਵਰਤਣਾ"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"ਬਾਹਰ ਜਾਣ ਲਈ, ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ ਜਾਂ ਐਪ \'ਤੇ ਕਿਤੇ ਵੀ ਟੈਪ ਕਰੋ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 1f6effe..b7ee750 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ukryj"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdź aplikację"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 14d7a84..715b0e4 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a6f7f00..1a59de4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"A carregar recomendações…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculte a sessão atual."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Definições"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativa. Consulte a app."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 14d7a84..715b0e4 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 0f15cfc..49c3ba0 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1075,7 +1075,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ascunde"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verificați aplicația"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6196f88..7fb456d 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Скрыть"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Нет ответа. Проверьте приложение."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 8aca9f4..1cd1ceb 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"නිර්දේශ පූරණය කරමින්"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"මාධ්‍ය"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"වත්මන් සැසිය සඟවන්න."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"සඟවන්න"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ඉවත ලන්න"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"අක්‍රියයි, යෙදුම පරීක්ෂා කරන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 45ee562..9d7e7d1 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skryť"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index c7ad8ab..ecbf1d9 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skrij"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 3e0aecc..70245b8 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fshih"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 985041b..1d02ca6 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1075,7 +1075,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медији"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Сакриј"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно. Видите апликацију"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 2018a38..7a7448b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Dölj"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv, kolla appen"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 6eecc0e..b8d95fc 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Inapakia mapendekezo"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Maudhui"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ficha kipindi cha sasa."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ficha"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index a05e0e7..60c1cf9 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"மறை"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"செயலில் இல்லை , சரிபார்க்கவும்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 7d76abd..6cfe03d 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్‌ను దాచు."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"దాచు"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్‌లు"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ఇన్‌యాక్టివ్, యాప్ చెక్ చేయండి"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 765c224..a2c1127 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ซ่อน"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ไม่มีการใช้งาน โปรดตรวจสอบแอป"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index c2cae90..e5f0532 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Itago"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Hindi aktibo, tingnan ang app"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 74b44f2..4943e2d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medya"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Gizle"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Devre dışı, uygulamaya bakın"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 38c331a..447912e 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1081,7 +1081,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Приховати"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, перейдіть у додаток"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 69ddabb..20b5518 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"چھپائیں"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"غیر فعال، ایپ چیک کریں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index ed704b0..cf1da75 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -227,7 +227,7 @@
     <string name="data_connection_roaming" msgid="375650836665414797">"Rouming"</string>
     <string name="data_connection_edge" msgid="6316755666481405762">"EDGE"</string>
     <string name="accessibility_data_connection_wifi" msgid="4422160347472742434">"Wi-Fi"</string>
-    <string name="accessibility_no_sim" msgid="1140839832913084973">"SIM karta solinmagan."</string>
+    <string name="accessibility_no_sim" msgid="1140839832913084973">"SIM kartasiz."</string>
     <string name="accessibility_cell_data" msgid="172950885786007392">"Mobil internet"</string>
     <string name="accessibility_cell_data_on" msgid="691666434519443162">"Mobil internet yoniq"</string>
     <string name="cell_data_off_content_description" msgid="9165555931499878044">"Mobil internet yoqilmagan"</string>
@@ -1069,7 +1069,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Berkitish"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Yopish"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nofaol. Ilovani tekshiring"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 5cc2099..a12a08d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ẩn"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Không hoạt động, hãy kiểm tra ứng dụng"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index f670bf6..3382365 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒体"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隐藏"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"无效,请检查应用"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 2e26d61..1d55ca2 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隱藏"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"已停用,請檢查應用程式"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 0049d2e..e62c164 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隱藏"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"無效,請查看應用程式"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index c8c99b1..bd73912 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1069,7 +1069,8 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string>
-    <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fihla"</string>
+    <!-- no translation found for controls_media_dismiss_button (9081375542265132213) -->
+    <skip />
     <string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string>
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index a1c52e5..f05be06 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -21,5 +21,5 @@
     <string name="system_ui_date_pattern" translatable="false">@*android:string/system_ui_date_pattern</string>
 
     <!-- Date format for the always on display.  -->
-    <item type="string" name="system_ui_aod_date_pattern" translatable="false">@*android:string/system_ui_date_pattern</item>
+    <item type="string" name="system_ui_aod_date_pattern" translatable="false">EEEMMMd</item>
 </resources>
diff --git a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java
similarity index 61%
copy from services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java
index 7bb27b2..1de740a 100644
--- a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.systemui.shared.system;
 
-/**
- * Error while converting a bitmask representing a list of LogDataTypes.
- */
-public class BitmaskConversionException extends RuntimeException {
-    BitmaskConversionException(String msg) {
-        super(msg);
+import android.annotation.UserIdInt;
+import android.content.Context;
+
+public class ContextUtils {
+
+    /** Get the user associated with this context */
+    public static @UserIdInt int getUserId(Context context) {
+        return context.getUserId();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 816bcf8..d5f74a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -53,8 +53,8 @@
             ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION;
 
     @VisibleForTesting
-    protected WindowMagnificationController mWindowMagnificationController;
-    protected final ModeSwitchesController mModeSwitchesController;
+    protected WindowMagnificationAnimationController mWindowMagnificationAnimationController;
+    private final ModeSwitchesController mModeSwitchesController;
     private final Handler mHandler;
     private final AccessibilityManager mAccessibilityManager;
     private final CommandQueue mCommandQueue;
@@ -72,6 +72,11 @@
                 Context.ACCESSIBILITY_SERVICE);
         mCommandQueue = commandQueue;
         mModeSwitchesController = modeSwitchesController;
+        final WindowMagnificationController controller = new WindowMagnificationController(mContext,
+                mHandler, new SfVsyncFrameCallbackProvider(), null,
+                new SurfaceControl.Transaction(), this);
+        mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+                mContext, controller);
     }
 
     @Override
@@ -81,9 +86,7 @@
             return;
         }
         mLastConfiguration.setTo(newConfig);
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.onConfigurationChanged(configDiff);
-        }
+        mWindowMagnificationAnimationController.onConfigurationChanged(configDiff);
         if (mModeSwitchesController != null) {
             mModeSwitchesController.onConfigurationChanged(configDiff);
         }
@@ -97,39 +100,25 @@
     @MainThread
     void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController == null) {
-            mWindowMagnificationController = new WindowMagnificationController(mContext,
-                    mHandler,
-                    new SfVsyncFrameCallbackProvider(),
-                    null, new SurfaceControl.Transaction(),
-                    this);
-        }
-        mWindowMagnificationController.enableWindowMagnification(scale, centerX, centerY);
+        mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY);
     }
 
     @MainThread
     void setScale(int displayId, float scale) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.setScale(scale);
-        }
+        mWindowMagnificationAnimationController.setScale(scale);
     }
 
     @MainThread
     void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.moveWindowMagnifier(offsetX, offsetY);
-        }
+        mWindowMagnificationAnimationController.moveWindowMagnifier(offsetX, offsetY);
     }
 
     @MainThread
     void disableWindowMagnification(int displayId) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.deleteWindowMagnification();
-        }
-        mWindowMagnificationController = null;
+        mWindowMagnificationAnimationController.deleteWindowMagnification();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
new file mode 100644
index 0000000..ae51623
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -0,0 +1,272 @@
+/*
+ * 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.systemui.accessibility;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.animation.AccelerateInterpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides same functionality of {@link WindowMagnificationController}. Some methods run with
+ * the animation.
+ */
+class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUpdateListener,
+        Animator.AnimatorListener {
+
+    private static final String TAG = "WindowMagnificationBridge";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATE_DISABLED, STATE_ENABLED, STATE_DISABLING, STATE_ENABLING})
+    @interface MagnificationState {}
+
+    //The window magnification is disabled.
+    private static final int STATE_DISABLED = 0;
+    //The window magnification is enabled.
+    private static final int STATE_ENABLED = 1;
+    //The window magnification is going to be disabled when the animation is end.
+    private  static final int STATE_DISABLING = 2;
+    //The animation is running for enabling the window magnification.
+    private static final int STATE_ENABLING = 3;
+
+    private final WindowMagnificationController mController;
+    private final ValueAnimator mValueAnimator;
+    private final AnimationSpec mStartSpec = new AnimationSpec();
+    private final AnimationSpec mEndSpec = new AnimationSpec();
+    private final Context mContext;
+
+    @MagnificationState
+    private int mState = STATE_DISABLED;
+
+    WindowMagnificationAnimationController(
+            Context context, WindowMagnificationController controller) {
+        this(context, controller, newValueAnimator(context.getResources()));
+    }
+
+    @VisibleForTesting
+    WindowMagnificationAnimationController(Context context,
+            WindowMagnificationController controller, ValueAnimator valueAnimator) {
+        mContext = context;
+        mController = controller;
+        mValueAnimator = valueAnimator;
+        mValueAnimator.addUpdateListener(this);
+        mValueAnimator.addListener(this);
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)}
+     * with transition animation. If the window magnification is not enabled, the scale will start
+     * from 1.0 and the center won't be changed during the animation. If {@link #mState} is
+     * {@code STATE_DISABLING}, the animation runs in reverse.
+     *
+     * @param scale   the target scale, or {@link Float#NaN} to leave unchanged.
+     * @param centerX the screen-relative X coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     * @param centerY the screen-relative Y coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     *
+     * @see #onAnimationUpdate(ValueAnimator)
+     */
+    void enableWindowMagnification(float scale, float centerX, float centerY) {
+        if (mState == STATE_ENABLING) {
+            mValueAnimator.cancel();
+        }
+        setupEnableAnimationSpecs(scale, centerX, centerY);
+
+        if (mEndSpec.equals(mStartSpec)) {
+            setState(STATE_ENABLED);
+        } else {
+            if (mState == STATE_DISABLING) {
+                mValueAnimator.reverse();
+            } else {
+                mValueAnimator.start();
+            }
+            setState(STATE_ENABLING);
+        }
+    }
+
+    private void setupEnableAnimationSpecs(float scale, float centerX, float centerY) {
+        final float currentScale = mController.getScale();
+        final float currentCenterX = mController.getCenterX();
+        final float currentCenterY = mController.getCenterY();
+
+        if (mState == STATE_DISABLED) {
+            //We don't need to offset the center during the animation.
+            mStartSpec.set(/* scale*/ 1.0f, centerX, centerY);
+            mEndSpec.set(Float.isNaN(scale) ? mContext.getResources().getInteger(
+                    R.integer.magnification_default_scale) : scale, centerX, centerY);
+        } else {
+            mStartSpec.set(currentScale, currentCenterX, currentCenterY);
+            mEndSpec.set(Float.isNaN(scale) ? currentScale : scale,
+                    Float.isNaN(centerX) ? currentCenterX : centerX,
+                    Float.isNaN(centerY) ? currentCenterY : centerY);
+        }
+        if (DEBUG) {
+            Log.d(TAG, "SetupEnableAnimationSpecs : mStartSpec = " + mStartSpec + ", endSpec = "
+                    + mEndSpec);
+        }
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#setScale(float)}. If the animation is
+     * running, it has no effect.
+     */
+    void setScale(float scale) {
+        if (mValueAnimator.isRunning()) {
+            return;
+        }
+        mController.setScale(scale);
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition
+     * animation. If the window magnification is enabling, it runs the animation in reverse.
+     */
+    void deleteWindowMagnification() {
+        if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
+            return;
+        }
+        mStartSpec.set(/* scale*/ 1.0f, Float.NaN, Float.NaN);
+        mEndSpec.set(/* scale*/ mController.getScale(), Float.NaN, Float.NaN);
+
+        mValueAnimator.reverse();
+        setState(STATE_DISABLING);
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#moveWindowMagnifier(float, float)}. If the
+     * animation is running, it has no effect.
+     * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
+     *                current screen pixels.
+     * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
+     *                current screen pixels.
+     */
+    void moveWindowMagnifier(float offsetX, float offsetY) {
+        if (mValueAnimator.isRunning()) {
+            return;
+        }
+        mController.moveWindowMagnifier(offsetX, offsetY);
+    }
+
+    void onConfigurationChanged(int configDiff) {
+        mController.onConfigurationChanged(configDiff);
+    }
+
+    private void setState(@MagnificationState int state) {
+        if (DEBUG) {
+            Log.d(TAG, "setState from " + mState + " to " + state);
+        }
+        mState = state;
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        if (mState == STATE_DISABLING) {
+            mController.deleteWindowMagnification();
+            setState(STATE_DISABLED);
+        } else if (mState == STATE_ENABLING) {
+            setState(STATE_ENABLED);
+        } else {
+            Log.w(TAG, "onAnimationEnd unexpected state:" + mState);
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator animation) {
+        final float fract = animation.getAnimatedFraction();
+        final float sentScale = mStartSpec.mScale + (mEndSpec.mScale - mStartSpec.mScale) * fract;
+        final float centerX =
+                mStartSpec.mCenterX + (mEndSpec.mCenterX - mStartSpec.mCenterX) * fract;
+        final float centerY =
+                mStartSpec.mCenterY + (mEndSpec.mCenterY - mStartSpec.mCenterY) * fract;
+        mController.enableWindowMagnification(sentScale, centerX, centerY);
+    }
+
+    private static ValueAnimator newValueAnimator(Resources resources) {
+        final ValueAnimator valueAnimator = new ValueAnimator();
+        valueAnimator.setDuration(
+                resources.getInteger(com.android.internal.R.integer.config_longAnimTime));
+        valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
+        valueAnimator.setFloatValues(0.0f, 1.0f);
+        return valueAnimator;
+    }
+
+    private static class AnimationSpec {
+        private float mScale = Float.NaN;
+        private float mCenterX = Float.NaN;
+        private float mCenterY = Float.NaN;
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+
+            if (other == null || getClass() != other.getClass()) {
+                return false;
+            }
+
+            final AnimationSpec s = (AnimationSpec) other;
+            return mScale == s.mScale && mCenterX == s.mCenterX && mCenterY == s.mCenterY;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = (mScale != +0.0f ? Float.floatToIntBits(mScale) : 0);
+            result = 31 * result + (mCenterX != +0.0f ? Float.floatToIntBits(mCenterX) : 0);
+            result = 31 * result + (mCenterY != +0.0f ? Float.floatToIntBits(mCenterY) : 0);
+            return result;
+        }
+
+        void set(float scale, float centerX, float centerY) {
+            mScale = scale;
+            mCenterX = centerX;
+            mCenterY = centerY;
+        }
+
+        @Override
+        public String toString() {
+            return "AnimationSpec{"
+                    + "mScale=" + mScale
+                    + ", mCenterX=" + mCenterX
+                    + ", mCenterY=" + mCenterY
+                    + '}';
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 798b751..494a0f64 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -150,7 +150,7 @@
 
         mMirrorViewGeometryVsyncCallback =
                 l -> {
-                    if (mMirrorView != null && mMirrorSurface != null) {
+                    if (isWindowVisible() && mMirrorSurface != null) {
                         calculateSourceBounds(mMagnificationFrame, mScale);
                         // The final destination for the magnification surface should be at 0,0
                         // since the ViewRootImpl's position will change
@@ -203,13 +203,13 @@
      * @param configDiff a bit mask of the differences between the configurations
      */
     void onConfigurationChanged(int configDiff) {
+        if (!isWindowVisible()) {
+            return;
+        }
         if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
             updateDimensions();
-            // TODO(b/145780606): update toggle button UI.
-            if (mMirrorView != null) {
-                mWm.removeView(mMirrorView);
-                createMirrorWindow();
-            }
+            mWm.removeView(mMirrorView);
+            createMirrorWindow();
         } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
             onRotate();
         }
@@ -502,7 +502,7 @@
     /**
      * Enables window magnification with specified parameters.
      *
-     * @param scale   the target scale
+     * @param scale   the target scale, or {@link Float#NaN} to leave unchanged
      * @param centerX the screen-relative X coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
      * @param centerY the screen-relative Y coordinate around which to center,
@@ -513,10 +513,10 @@
                 : centerX - mMagnificationFrame.exactCenterX();
         final float offsetY = Float.isNaN(centerY) ? 0
                 : centerY - mMagnificationFrame.exactCenterY();
-        mScale = scale;
+        mScale = Float.isNaN(scale) ? mScale : scale;
         setMagnificationFrameBoundary();
         updateMagnificationFramePosition((int) offsetX, (int) offsetY);
-        if (mMirrorView == null) {
+        if (!isWindowVisible()) {
             createMirrorWindow();
             showControls();
         } else {
@@ -527,10 +527,10 @@
     /**
      * Sets the scale of the magnified region if it's visible.
      *
-     * @param scale the target scale
+     * @param scale the target scale, or {@link Float#NaN} to leave unchanged
      */
     void setScale(float scale) {
-        if (mMirrorView == null || mScale == scale) {
+        if (!isWindowVisible() || mScale == scale) {
             return;
         }
         enableWindowMagnification(scale, Float.NaN, Float.NaN);
@@ -552,4 +552,35 @@
             modifyWindowMagnification(mTransaction);
         }
     }
+
+    /**
+     * Gets the scale.
+     * @return {@link Float#NaN} if the window is invisible.
+     */
+    float getScale() {
+        return isWindowVisible() ? mScale : Float.NaN;
+    }
+
+    /**
+     * Returns the screen-relative X coordinate of the center of the magnified bounds.
+     *
+     * @return the X coordinate. {@link Float#NaN} if the window is invisible.
+     */
+    float getCenterX() {
+        return isWindowVisible() ? mMagnificationFrame.exactCenterX() : Float.NaN;
+    }
+
+    /**
+     * Returns the screen-relative Y coordinate of the center of the magnified bounds.
+     *
+     * @return the Y coordinate. {@link Float#NaN} if the window is invisible.
+     */
+    float getCenterY() {
+        return isWindowVisible() ? mMagnificationFrame.exactCenterY() : Float.NaN;
+    }
+
+    //The window is visible when it is existed.
+    private boolean isWindowVisible() {
+        return mMirrorView != null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
index 7e5b426..93a8df4 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
@@ -25,7 +25,9 @@
     private int mUid;
     private String mPackageName;
     private long mTimeStarted;
-    private String mState;
+    private StringBuilder mState;
+    // This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO
+    private boolean mSilenced;
 
     public AppOpItem(int code, int uid, String packageName, long timeStarted) {
         this.mCode = code;
@@ -36,9 +38,8 @@
                 .append("AppOpItem(")
                 .append("Op code=").append(code).append(", ")
                 .append("UID=").append(uid).append(", ")
-                .append("Package name=").append(packageName)
-                .append(")")
-                .toString();
+                .append("Package name=").append(packageName).append(", ")
+                .append("Paused=");
     }
 
     public int getCode() {
@@ -57,8 +58,16 @@
         return mTimeStarted;
     }
 
+    public void setSilenced(boolean silenced) {
+        mSilenced = silenced;
+    }
+
+    public boolean isSilenced() {
+        return mSilenced;
+    }
+
     @Override
     public String toString() {
-        return mState;
+        return mState.append(mSilenced).append(")").toString();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 6512624..8187a223 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -19,6 +19,8 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.media.AudioRecordingConfiguration;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -63,6 +65,7 @@
     private static final boolean DEBUG = false;
 
     private final AppOpsManager mAppOps;
+    private final AudioManager mAudioManager;
     private H mBGHandler;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
@@ -73,6 +76,9 @@
     private final List<AppOpItem> mActiveItems = new ArrayList<>();
     @GuardedBy("mNotedItems")
     private final List<AppOpItem> mNotedItems = new ArrayList<>();
+    @GuardedBy("mActiveItems")
+    private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid =
+            new SparseArray<>();
 
     protected static final int[] OPS = new int[] {
             AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
@@ -88,7 +94,8 @@
             Context context,
             @Background Looper bgLooper,
             DumpManager dumpManager,
-            PermissionFlagsCache cache
+            PermissionFlagsCache cache,
+            AudioManager audioManager
     ) {
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mFlagsCache = cache;
@@ -97,6 +104,7 @@
         for (int i = 0; i < numOps; i++) {
             mCallbacksByCode.put(OPS[i], new ArraySet<>());
         }
+        mAudioManager = audioManager;
         dumpManager.registerDumpable(TAG, this);
     }
 
@@ -111,12 +119,19 @@
         if (listening) {
             mAppOps.startWatchingActive(OPS, this);
             mAppOps.startWatchingNoted(OPS, this);
+            mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
+            mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
+                    mAudioManager.getActiveRecordingConfigurations()));
+
         } else {
             mAppOps.stopWatchingActive(this);
             mAppOps.stopWatchingNoted(this);
+            mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+
             mBGHandler.removeCallbacksAndMessages(null); // null removes all
             synchronized (mActiveItems) {
                 mActiveItems.clear();
+                mRecordingsByUid.clear();
             }
             synchronized (mNotedItems) {
                 mNotedItems.clear();
@@ -189,9 +204,12 @@
             AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
             if (item == null && active) {
                 item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
+                if (code == AppOpsManager.OP_RECORD_AUDIO) {
+                    item.setSilenced(isAnyRecordingPausedLocked(uid));
+                }
                 mActiveItems.add(item);
                 if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
-                return true;
+                return !item.isSilenced();
             } else if (item != null && !active) {
                 mActiveItems.remove(item);
                 if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
@@ -215,7 +233,7 @@
             active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
         }
         if (!active) {
-            notifySuscribers(code, uid, packageName, false);
+            notifySuscribersWorker(code, uid, packageName, false);
         }
     }
 
@@ -324,7 +342,7 @@
                 AppOpItem item = mActiveItems.get(i);
                 if ((userId == UserHandle.USER_ALL
                         || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item)) {
+                        && isUserVisible(item) && !item.isSilenced()) {
                     list.add(item);
                 }
             }
@@ -343,6 +361,10 @@
         return list;
     }
 
+    private void notifySuscribers(int code, int uid, String packageName, boolean active) {
+        mBGHandler.post(() -> notifySuscribersWorker(code, uid, packageName, active));
+    }
+
     @Override
     public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
         if (DEBUG) {
@@ -360,7 +382,7 @@
         // If active is false, we only send the update if the op is not actively noted (prevent
         // early removal)
         if (!alsoNoted) {
-            mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active));
+            notifySuscribers(code, uid, packageName, active);
         }
     }
 
@@ -378,11 +400,11 @@
             alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
         }
         if (!alsoActive) {
-            mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true));
+            notifySuscribers(code, uid, packageName, true);
         }
     }
 
-    private void notifySuscribers(int code, int uid, String packageName, boolean active) {
+    private void notifySuscribersWorker(int code, int uid, String packageName, boolean active) {
         if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
             if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
             for (Callback cb: mCallbacksByCode.get(code)) {
@@ -408,6 +430,61 @@
 
     }
 
+    private boolean isAnyRecordingPausedLocked(int uid) {
+        List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid);
+        if (configs == null) return false;
+        int configsNum = configs.size();
+        for (int i = 0; i < configsNum; i++) {
+            AudioRecordingConfiguration config = configs.get(i);
+            if (config.isClientSilenced()) return true;
+        }
+        return false;
+    }
+
+    private void updateRecordingPausedStatus() {
+        synchronized (mActiveItems) {
+            int size = mActiveItems.size();
+            for (int i = 0; i < size; i++) {
+                AppOpItem item = mActiveItems.get(i);
+                if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) {
+                    boolean paused = isAnyRecordingPausedLocked(item.getUid());
+                    if (item.isSilenced() != paused) {
+                        item.setSilenced(paused);
+                        notifySuscribers(
+                                item.getCode(),
+                                item.getUid(),
+                                item.getPackageName(),
+                                !item.isSilenced()
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    private AudioManager.AudioRecordingCallback mAudioRecordingCallback =
+            new AudioManager.AudioRecordingCallback() {
+        @Override
+        public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
+            synchronized (mActiveItems) {
+                mRecordingsByUid.clear();
+                final int recordingsCount = configs.size();
+                for (int i = 0; i < recordingsCount; i++) {
+                    AudioRecordingConfiguration recording = configs.get(i);
+
+                    ArrayList<AudioRecordingConfiguration> recordings = mRecordingsByUid.get(
+                            recording.getClientUid());
+                    if (recordings == null) {
+                        recordings = new ArrayList<>();
+                        mRecordingsByUid.put(recording.getClientUid(), recordings);
+                    }
+                    recordings.add(recording);
+                }
+            }
+            updateRecordingPausedStatus();
+        }
+    };
+
     protected class H extends Handler {
         H(Looper looper) {
             super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt
index 8b953fa..be089b1 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt
@@ -21,7 +21,7 @@
 
 enum class AssistantSessionEvent(private val id: Int) : UiEventLogger.UiEventEnum {
     @UiEvent(doc = "Unknown assistant session event")
-    ASSISTANT_SESSION_UNKNOWN(523),
+    ASSISTANT_SESSION_UNKNOWN(0),
 
     @UiEvent(doc = "Assistant session dismissed due to timeout")
     ASSISTANT_SESSION_TIMEOUT_DISMISS(524),
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index fce545b..f774358 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -72,7 +73,8 @@
         subcomponents = {StatusBarComponent.class,
                 NotificationRowComponent.class,
                 DozeComponent.class,
-                ExpandableNotificationRowComponent.class})
+                ExpandableNotificationRowComponent.class,
+                NotificationShelfComponent.class})
 public abstract class SystemUIModule {
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 8a51c85..e77e499 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
@@ -97,6 +98,7 @@
     dumpManager: DumpManager,
     mediaTimeoutListener: MediaTimeoutListener,
     mediaResumeListener: MediaResumeListener,
+    private val activityStarter: ActivityStarter,
     private var useMediaResumption: Boolean,
     private val useQsMediaPlayer: Boolean
 ) : Dumpable {
@@ -113,10 +115,11 @@
         dumpManager: DumpManager,
         broadcastDispatcher: BroadcastDispatcher,
         mediaTimeoutListener: MediaTimeoutListener,
-        mediaResumeListener: MediaResumeListener
+        mediaResumeListener: MediaResumeListener,
+        activityStarter: ActivityStarter
     ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
             broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
-            Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context))
+            activityStarter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context))
 
     private val appChangeReceiver = object : BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
@@ -403,10 +406,13 @@
                 }
                 val runnable = if (action.actionIntent != null) {
                     Runnable {
-                        try {
-                            action.actionIntent.send()
-                        } catch (e: PendingIntent.CanceledException) {
-                            Log.d(TAG, "Intent canceled", e)
+                        if (action.isAuthenticationRequired()) {
+                            activityStarter.dismissKeyguardThenExecute ({
+                                var result = sendPendingIntent(action.actionIntent)
+                                result
+                            }, {}, true)
+                        } else {
+                            sendPendingIntent(action.actionIntent)
                         }
                     }
                 } else {
@@ -449,6 +455,15 @@
         return null
     }
 
+    private fun sendPendingIntent(intent: PendingIntent): Boolean {
+        return try {
+            intent.send()
+            true
+        } catch (e: PendingIntent.CanceledException) {
+            Log.d(TAG, "Intent canceled", e)
+            false
+        }
+    }
     /**
      * Load a bitmap from a URI
      * @param uri the uri to load
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 0167028..f1c8b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -186,20 +186,23 @@
     private class PipManagerPinnedStackListener extends PinnedStackListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-            if (mState == STATE_PIP) {
-                if (mImeVisible != imeVisible) {
-                    if (imeVisible) {
-                        // Save the IME height adjustment, and offset to not occlude the IME
-                        mPipBounds.offset(0, -imeHeight);
-                        mImeHeightAdjustment = imeHeight;
-                    } else {
-                        // Apply the inverse adjustment when the IME is hidden
-                        mPipBounds.offset(0, mImeHeightAdjustment);
+            mHandler.post(() -> {
+                mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight);
+                if (mState == STATE_PIP) {
+                    if (mImeVisible != imeVisible) {
+                        if (imeVisible) {
+                            // Save the IME height adjustment, and offset to not occlude the IME
+                            mPipBounds.offset(0, -imeHeight);
+                            mImeHeightAdjustment = imeHeight;
+                        } else {
+                            // Apply the inverse adjustment when the IME is hidden
+                            mPipBounds.offset(0, mImeHeightAdjustment);
+                        }
+                        mImeVisible = imeVisible;
+                        resizePinnedStack(STATE_PIP);
                     }
-                    mImeVisible = imeVisible;
-                    resizePinnedStack(STATE_PIP);
                 }
-            }
+            });
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index d2aaaede..255513a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.qs.tileimpl;
 
+import static androidx.lifecycle.Lifecycle.State.CREATED;
 import static androidx.lifecycle.Lifecycle.State.DESTROYED;
 import static androidx.lifecycle.Lifecycle.State.RESUMED;
 import static androidx.lifecycle.Lifecycle.State.STARTED;
@@ -173,6 +174,7 @@
 
         mState = newTileState();
         mTmpState = newTileState();
+        mUiHandler.post(() -> mLifecycle.setCurrentState(CREATED));
     }
 
     protected final void resetStates() {
@@ -453,6 +455,9 @@
                 if (DEBUG) Log.d(TAG, "handleSetListening true");
                 handleSetListening(listening);
                 mUiHandler.post(() -> {
+                    // This tile has been destroyed, the state should not change anymore and we
+                    // should not refresh it anymore.
+                    if (mLifecycle.getCurrentState().equals(DESTROYED)) return;
                     mLifecycle.setCurrentState(RESUMED);
                     refreshState(); // Ensure we get at least one refresh after listening.
                 });
@@ -461,7 +466,11 @@
             if (mListeners.remove(listener) && mListeners.size() == 0) {
                 if (DEBUG) Log.d(TAG, "handleSetListening false");
                 handleSetListening(listening);
-                mUiHandler.post(() -> mLifecycle.setCurrentState(STARTED));
+                mUiHandler.post(() -> {
+                    // This tile has been destroyed, the state should not change anymore.
+                    if (mLifecycle.getCurrentState().equals(DESTROYED)) return;
+                    mLifecycle.setCurrentState(STARTED);
+                });
             }
         }
         updateIsFullQs();
@@ -488,11 +497,14 @@
         mQSLogger.logTileDestroyed(mTileSpec, "Handle destroy");
         if (mListeners.size() != 0) {
             handleSetListening(false);
+            mListeners.clear();
         }
         mCallbacks.clear();
         mHandler.removeCallbacksAndMessages(null);
         // This will force it to be removed from all controllers that may have it registered.
-        mLifecycle.setCurrentState(DESTROYED);
+        mUiHandler.post(() -> {
+            mLifecycle.setCurrentState(DESTROYED);
+        });
     }
 
     protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index c535230..6747281 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -50,6 +50,7 @@
 import android.media.MediaActionSound;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
@@ -556,11 +557,18 @@
     private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
         // copy the input Rect, since SurfaceControl.screenshot can mutate it
         Rect screenRect = new Rect(crop);
-        int rot = mDisplay.getRotation();
         int width = crop.width();
         int height = crop.height();
-        saveScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
-                Insets.NONE, true);
+        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+        final SurfaceControl.DisplayCaptureArgs captureArgs =
+                new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+                        .setSourceCrop(crop)
+                        .setSize(width, height)
+                        .build();
+        final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+                SurfaceControl.captureDisplay(captureArgs);
+        final Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+        saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
     }
 
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 6b023c0..9586795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -161,7 +161,9 @@
                 ActivityManager.getService().resumeAppSwitches();
             } catch (RemoteException e) {
             }
-            return mCallback.handleRemoteViewClick(view, pendingIntent, () -> {
+            Notification.Action action = getActionFromView(view, entry, pendingIntent);
+            return mCallback.handleRemoteViewClick(view, pendingIntent,
+                    action == null ? false : action.isAuthenticationRequired(), () -> {
                 Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
                 options.second.setLaunchWindowingMode(
                         WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
@@ -170,23 +172,45 @@
             });
         }
 
+        private @Nullable Notification.Action getActionFromView(View view,
+                NotificationEntry entry, PendingIntent actionIntent) {
+            Integer actionIndex = (Integer)
+                    view.getTag(com.android.internal.R.id.notification_action_index_tag);
+            if (actionIndex == null) {
+                return null;
+            }
+            if (entry == null) {
+                Log.w(TAG, "Couldn't determine notification for click.");
+                return null;
+            }
+
+            // Notification may be updated before this function is executed, and thus play safe
+            // here and verify that the action object is still the one that where the click happens.
+            StatusBarNotification statusBarNotification = entry.getSbn();
+            Notification.Action[] actions = statusBarNotification.getNotification().actions;
+            if (actions == null || actionIndex >= actions.length) {
+                Log.w(TAG, "statusBarNotification.getNotification().actions is null or invalid");
+                return null ;
+            }
+            final Notification.Action action =
+                    statusBarNotification.getNotification().actions[actionIndex];
+            if (!Objects.equals(action.actionIntent, actionIntent)) {
+                Log.w(TAG, "actionIntent does not match");
+                return null;
+            }
+            return action;
+        }
+
         private void logActionClick(
                 View view,
                 NotificationEntry entry,
                 PendingIntent actionIntent) {
-            Integer actionIndex = (Integer)
-                    view.getTag(com.android.internal.R.id.notification_action_index_tag);
-            if (actionIndex == null) {
-                // Custom action button, not logging.
+            Notification.Action action = getActionFromView(view, entry, actionIntent);
+            if (action == null) {
                 return;
             }
             ViewParent parent = view.getParent();
-            if (entry == null) {
-                Log.w(TAG, "Couldn't determine notification for click.");
-                return;
-            }
-            StatusBarNotification statusBarNotification = entry.getSbn();
-            String key = statusBarNotification.getKey();
+            String key = entry.getSbn().getKey();
             int buttonIndex = -1;
             // If this is a default template, determine the index of the button.
             if (view.getId() == com.android.internal.R.id.action0 &&
@@ -198,19 +222,6 @@
             final int rank = mEntryManager
                     .getActiveNotificationUnfiltered(key).getRanking().getRank();
 
-            // Notification may be updated before this function is executed, and thus play safe
-            // here and verify that the action object is still the one that where the click happens.
-            Notification.Action[] actions = statusBarNotification.getNotification().actions;
-            if (actions == null || actionIndex >= actions.length) {
-                Log.w(TAG, "statusBarNotification.getNotification().actions is null or invalid");
-                return;
-            }
-            final Notification.Action action =
-                    statusBarNotification.getNotification().actions[actionIndex];
-            if (!Objects.equals(action.actionIntent, actionIntent)) {
-                Log.w(TAG, "actionIntent does not match");
-                return;
-            }
             NotificationVisibility.NotificationLocation location =
                     NotificationLogger.getNotificationLocation(
                             mEntryManager.getActiveNotificationUnfiltered(key));
@@ -813,11 +824,12 @@
          *
          * @param view
          * @param pendingIntent
+         * @param appRequestedAuth
          * @param defaultHandler
          * @return  true iff the click was handled
          */
         boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
-                ClickHandler defaultHandler);
+                boolean appRequestedAuth, ClickHandler defaultHandler);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d798692..8f3033e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN_REVERSE;
 import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -26,7 +25,6 @@
 import android.graphics.Rect;
 import android.os.SystemProperties;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.MathUtils;
 import android.view.DisplayCutout;
 import android.view.View;
@@ -36,10 +34,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -48,14 +44,10 @@
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.ViewState;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
 /**
  * A notification shelf view that is placed inside the notification scroller. It manages the
  * overflow icons that don't fit into the regular list anymore.
@@ -69,7 +61,6 @@
             = SystemProperties.getBoolean("debug.icon_scroll_animations", true);
     private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
     private static final String TAG = "NotificationShelf";
-    private final KeyguardBypassController mBypassController;
 
     private NotificationIconContainer mShelfIcons;
     private int[] mTmp = new int[2];
@@ -77,9 +68,8 @@
     private int mIconAppearTopPadding;
     private float mHiddenShelfIconSize;
     private int mStatusBarHeight;
-    private int mStatusBarPaddingStart;
     private AmbientState mAmbientState;
-    private NotificationStackScrollLayout mHostLayout;
+    private NotificationStackScrollLayoutController mHostLayoutController;
     private int mMaxLayoutHeight;
     private int mPaddingBetweenElements;
     private int mNotGoneIndex;
@@ -88,7 +78,6 @@
     private int mScrollFastThreshold;
     private int mIconSize;
     private int mStatusBarState;
-    private float mMaxShelfEnd;
     private int mRelativeOffset;
     private boolean mInteractive;
     private float mOpenedAmount;
@@ -99,13 +88,10 @@
     private Rect mClipRect = new Rect();
     private int mCutoutHeight;
     private int mGapHeight;
+    private NotificationShelfController mController;
 
-    @Inject
-    public NotificationShelf(@Named(VIEW_CONTEXT) Context context,
-            AttributeSet attrs,
-            KeyguardBypassController keyguardBypassController) {
+    public NotificationShelf(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mBypassController = keyguardBypassController;
     }
 
     @Override
@@ -128,29 +114,16 @@
         initDimens();
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
-                .addCallback(this, SysuiStatusBarStateController.RANK_SHELF);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        Dependency.get(StatusBarStateController.class).removeCallback(this);
-    }
-
-    public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
+    public void bind(AmbientState ambientState,
+            NotificationStackScrollLayoutController hostLayoutController) {
         mAmbientState = ambientState;
-        mHostLayout = hostLayout;
+        mHostLayoutController = hostLayoutController;
     }
 
     private void initDimens() {
         Resources res = getResources();
         mIconAppearTopPadding = res.getDimensionPixelSize(R.dimen.notification_icon_appear_padding);
         mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
-        mStatusBarPaddingStart = res.getDimensionPixelOffset(R.dimen.status_bar_padding_start);
         mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
 
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
@@ -276,8 +249,8 @@
         float firstElementRoundness = 0.0f;
         ActivatableNotificationView previousAnv = null;
 
-        for (int i = 0; i < mHostLayout.getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+        for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) mHostLayoutController.getChildAt(i);
 
             if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
                 continue;
@@ -315,9 +288,7 @@
                     transitionAmount = inShelfAmount;
                 }
                 // We don't want to modify the color if the notification is hun'd
-                boolean canModifyColor = mAmbientState.isShadeExpanded()
-                        && !(mAmbientState.isOnKeyguard() && mBypassController.getBypassEnabled());
-                if (isLastChild && canModifyColor) {
+                if (isLastChild && mController.canModifyColorOfNotifications()) {
                     if (colorOfViewBeforeLast == NO_COLOR) {
                         colorOfViewBeforeLast = ownColorUntinted;
                     }
@@ -384,8 +355,8 @@
         mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
         mShelfIcons.calculateIconTranslations();
         mShelfIcons.applyIconStates();
-        for (int i = 0; i < mHostLayout.getChildCount(); i++) {
-            View child = mHostLayout.getChildAt(i);
+        for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
+            View child = mHostLayoutController.getChildAt(i);
             if (!(child instanceof ExpandableNotificationRow)
                     || child.getVisibility() == GONE) {
                 continue;
@@ -408,8 +379,8 @@
      * swipes quickly.
      */
     private void clipTransientViews() {
-        for (int i = 0; i < mHostLayout.getTransientViewCount(); i++) {
-            View transientView = mHostLayout.getTransientView(i);
+        for (int i = 0; i < mHostLayoutController.getTransientViewCount(); i++) {
+            View transientView = mHostLayoutController.getTransientView(i);
             if (transientView instanceof ExpandableView) {
                 ExpandableView transientExpandableView = (ExpandableView) transientView;
                 updateNotificationClipHeight(transientExpandableView, getTranslationY(), -1);
@@ -648,7 +619,7 @@
             // We need to persist this, since after the expansion, the behavior should still be the
             // same.
             float position = mAmbientState.getIntrinsicPadding()
-                    + mHostLayout.getPositionInLinearLayout(view);
+                    + mHostLayoutController.getPositionInLinearLayout(view);
             int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight();
             if (position < maxShelfStart && position + view.getIntrinsicHeight() >= maxShelfStart
                     && view.getTranslationY() < position) {
@@ -999,10 +970,6 @@
         return mInteractive;
     }
 
-    public void setMaxShelfEnd(float maxShelfEnd) {
-        mMaxShelfEnd = maxShelfEnd;
-    }
-
     public void setAnimationsEnabled(boolean enabled) {
         mAnimationsEnabled = enabled;
         if (!enabled) {
@@ -1044,6 +1011,10 @@
         updateBackgroundColors();
     }
 
+    public void setController(NotificationShelfController notificationShelfController) {
+        mController = notificationShelfController;
+    }
+
     private class ShelfState extends ExpandableViewState {
         private float openedAmount;
         private boolean hasItemsInStableShelf;
@@ -1056,7 +1027,6 @@
             }
 
             super.applyToView(view);
-            setMaxShelfEnd(maxShelfEnd);
             setOpenedAmount(openedAmount);
             updateAppearance();
             setHasItemsInStableShelf(hasItemsInStableShelf);
@@ -1070,7 +1040,6 @@
             }
 
             super.animateTo(child, properties);
-            setMaxShelfEnd(maxShelfEnd);
             setOpenedAmount(openedAmount);
             updateAppearance();
             setHasItemsInStableShelf(hasItemsInStableShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
new file mode 100644
index 0000000..77abcfa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
@@ -0,0 +1,122 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.view.View;
+
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationIconContainer;
+import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter;
+
+import javax.inject.Inject;
+
+/**
+ * Controller class for {@link NotificationShelf}.
+ */
+@NotificationRowScope
+public class NotificationShelfController {
+    private final NotificationShelf mView;
+    private final ActivatableNotificationViewController mActivatableNotificationViewController;
+    private final KeyguardBypassController mKeyguardBypassController;
+    private final SysuiStatusBarStateController mStatusBarStateController;
+    private final View.OnAttachStateChangeListener mOnAttachStateChangeListener;
+    private AmbientState mAmbientState;
+
+    @Inject
+    public NotificationShelfController(NotificationShelf notificationShelf,
+            ActivatableNotificationViewController activatableNotificationViewController,
+            KeyguardBypassController keyguardBypassController,
+            SysuiStatusBarStateController statusBarStateController) {
+        mView = notificationShelf;
+        mActivatableNotificationViewController = activatableNotificationViewController;
+        mKeyguardBypassController = keyguardBypassController;
+        mStatusBarStateController = statusBarStateController;
+        mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                mStatusBarStateController.addCallback(
+                        mView, SysuiStatusBarStateController.RANK_SHELF);
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                mStatusBarStateController.removeCallback(mView);
+            }
+        };
+    }
+
+    public void init() {
+        mActivatableNotificationViewController.init();
+        mView.setController(this);
+        mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+        if (mView.isAttachedToWindow()) {
+            mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
+        }
+    }
+
+    public NotificationShelf getView() {
+        return mView;
+    }
+
+    public boolean canModifyColorOfNotifications() {
+        return mAmbientState.isShadeExpanded()
+                && !(mAmbientState.isOnKeyguard() && mKeyguardBypassController.getBypassEnabled());
+    }
+
+    public NotificationIconContainer getShelfIcons() {
+        return mView.getShelfIcons();
+    }
+
+    public void setCollapsedIcons(NotificationIconContainer notificationIcons) {
+        mView.setCollapsedIcons(notificationIcons);
+    }
+
+    public void bind(AmbientState ambientState,
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
+        mView.bind(ambientState, notificationStackScrollLayoutController);
+        mAmbientState = ambientState;
+    }
+
+    public int getHeight() {
+        return mView.getHeight();
+    }
+
+    public void updateState(AmbientState ambientState) {
+        mAmbientState = ambientState;
+        mView.updateState(ambientState);
+    }
+
+    public int getIntrinsicHeight() {
+        return mView.getIntrinsicHeight();
+    }
+
+    public void setOnActivatedListener(StatusBarNotificationPresenter presenter) {
+        mView.setOnActivatedListener(presenter);
+    }
+
+    public void setOnClickListener(View.OnClickListener onClickListener) {
+        mView.setOnClickListener(onClickListener);
+    }
+
+    public int getNotGoneIndex() {
+        return mView.getNotGoneIndex();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 7b25853..ff13c4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.ShadeController
@@ -93,7 +93,7 @@
         private set
     private val mTouchSlop: Float
     private lateinit var expansionCallback: ExpansionCallback
-    private lateinit var stackScroller: NotificationStackScrollLayout
+    private lateinit var stackScrollerController: NotificationStackScrollLayoutController
     private val mTemp2 = IntArray(2)
     private var mDraggedFarEnough: Boolean = false
     private var mStartingChild: ExpandableView? = null
@@ -315,23 +315,23 @@
     private fun findView(x: Float, y: Float): ExpandableView? {
         var totalX = x
         var totalY = y
-        stackScroller.getLocationOnScreen(mTemp2)
+        stackScrollerController.getLocationOnScreen(mTemp2)
         totalX += mTemp2[0].toFloat()
         totalY += mTemp2[1].toFloat()
-        val childAtRawPosition = stackScroller.getChildAtRawPosition(totalX, totalY)
+        val childAtRawPosition = stackScrollerController.getChildAtRawPosition(totalX, totalY)
         return if (childAtRawPosition != null && childAtRawPosition.isContentExpandable) {
             childAtRawPosition
         } else null
     }
 
     fun setUp(
-        stackScroller: NotificationStackScrollLayout,
+        stackScrollerController: NotificationStackScrollLayoutController,
         expansionCallback: ExpansionCallback,
         shadeController: ShadeController
     ) {
         this.expansionCallback = expansionCallback
         this.shadeController = shadeController
-        this.stackScroller = stackScroller
+        this.stackScrollerController = stackScrollerController
     }
 
     fun setPulsing(pulsing: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
index 7cda235..27e4ade 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
@@ -21,7 +21,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
@@ -41,22 +41,22 @@
 
     private final Context mContext;
     private final InjectionInflationController mInjectionInflationController;
-    private final NotificationRowComponent.Builder mNotificationRowComponentBuilder;
     private final LockscreenLockIconController mLockIconController;
+    private final NotificationShelfComponent.Builder mNotificationShelfComponentBuilder;
 
     private NotificationShadeWindowView mNotificationShadeWindowView;
     private StatusBarWindowView mStatusBarWindowView;
-    private NotificationShelf mNotificationShelf;
+    private NotificationShelfController mNotificationShelfController;
 
     @Inject
     public SuperStatusBarViewFactory(Context context,
             InjectionInflationController injectionInflationController,
-            NotificationRowComponent.Builder notificationRowComponentBuilder,
+            NotificationShelfComponent.Builder notificationShelfComponentBuilder,
             LockscreenLockIconController lockIconController) {
         mContext = context;
         mInjectionInflationController = injectionInflationController;
-        mNotificationRowComponentBuilder = notificationRowComponentBuilder;
         mLockIconController = lockIconController;
+        mNotificationShelfComponentBuilder = notificationShelfComponentBuilder;
     }
 
     /**
@@ -114,25 +114,27 @@
      *                  isn't immediately attached, but the layout params of this view is used
      *                  during inflation.
      */
-    public NotificationShelf getNotificationShelf(ViewGroup container) {
-        if (mNotificationShelf != null) {
-            return mNotificationShelf;
+    public NotificationShelfController getNotificationShelfController(ViewGroup container) {
+        if (mNotificationShelfController != null) {
+            return mNotificationShelfController;
         }
 
-        mNotificationShelf = (NotificationShelf) mInjectionInflationController.injectable(
-                LayoutInflater.from(mContext)).inflate(R.layout.status_bar_notification_shelf,
-                container, /* attachToRoot= */ false);
+        NotificationShelf view = (NotificationShelf) LayoutInflater.from(mContext)
+                .inflate(R.layout.status_bar_notification_shelf, container, /* attachToRoot= */
+                        false);
 
-        NotificationRowComponent component = mNotificationRowComponentBuilder
-                .activatableNotificationView(mNotificationShelf)
-                .build();
-        component.getActivatableNotificationViewController().init();
-
-        if (mNotificationShelf == null) {
+        if (view == null) {
             throw new IllegalStateException(
                     "R.layout.status_bar_notification_shelf could not be properly inflated");
         }
-        return mNotificationShelf;
+
+        NotificationShelfComponent component = mNotificationShelfComponentBuilder
+                .notificationShelf(view)
+                .build();
+        mNotificationShelfController = component.getNotificationShelfController();
+        mNotificationShelfController.init();
+
+        return mNotificationShelfController;
     }
 
     public NotificationPanelView getNotificationPanelView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index f982cf0..0469176 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -33,6 +33,7 @@
 
 import javax.inject.Inject
 import javax.inject.Singleton
+import kotlin.math.min
 
 @Singleton
 class NotificationWakeUpCoordinator @Inject constructor(
@@ -53,7 +54,7 @@
             return coordinator.mLinearVisibilityAmount
         }
     }
-    private lateinit var mStackScroller: NotificationStackScrollLayout
+    private lateinit var mStackScrollerController: NotificationStackScrollLayoutController
     private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
 
     private var mLinearDozeAmount: Float = 0.0f
@@ -79,7 +80,7 @@
                 if (mNotificationsVisible && !mNotificationsVisibleForExpansion &&
                     !bypassController.bypassEnabled) {
                     // We're waking up while pulsing, let's make sure the animation looks nice
-                    mStackScroller.wakeUpFromPulse()
+                    mStackScrollerController.wakeUpFromPulse()
                 }
                 if (bypassController.bypassEnabled && !mNotificationsVisible) {
                     // Let's make sure our huns become visible once we are waking up in case
@@ -156,10 +157,10 @@
         })
     }
 
-    fun setStackScroller(stackScroller: NotificationStackScrollLayout) {
-        mStackScroller = stackScroller
-        pulseExpanding = stackScroller.isPulseExpanding
-        stackScroller.setOnPulseHeightChangedListener {
+    fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) {
+        mStackScrollerController = stackScrollerController
+        pulseExpanding = stackScrollerController.isPulseExpanding
+        stackScrollerController.setOnPulseHeightChangedListener {
             val nowExpanding = isPulseExpanding()
             val changed = nowExpanding != pulseExpanding
             pulseExpanding = nowExpanding
@@ -169,7 +170,7 @@
         }
     }
 
-    fun isPulseExpanding(): Boolean = mStackScroller.isPulseExpanding
+    fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding
 
     /**
      * @param visible should notifications be visible
@@ -249,7 +250,7 @@
         val changed = linear != mLinearDozeAmount
         mLinearDozeAmount = linear
         mDozeAmount = eased
-        mStackScroller.setDozeAmount(mDozeAmount)
+        mStackScrollerController.setDozeAmount(mDozeAmount)
         updateHideAmount()
         if (changed && linear == 0.0f) {
             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
@@ -330,18 +331,18 @@
     }
 
     fun getWakeUpHeight(): Float {
-        return mStackScroller.wakeUpHeight
+        return mStackScrollerController.wakeUpHeight
     }
 
     private fun updateHideAmount() {
-        val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount)
-        val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount)
-        mStackScroller.setHideAmount(linearAmount, amount)
+        val linearAmount = min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount)
+        val amount = min(1.0f - mVisibilityAmount, mDozeAmount)
+        mStackScrollerController.setHideAmount(linearAmount, amount)
         notificationsFullyHidden = linearAmount == 1.0f
     }
 
     private fun notifyAnimationStart(awake: Boolean) {
-        mStackScroller.notifyHideAnimationStart(!awake)
+        mStackScrollerController.notifyHideAnimationStart(!awake)
     }
 
     override fun onDozingChanged(isDozing: Boolean) {
@@ -355,7 +356,7 @@
      * from a pulse and determines how much the notifications are expanded.
      */
     fun setPulseHeight(height: Float): Float {
-        val overflow = mStackScroller.setPulseHeight(height)
+        val overflow = mStackScrollerController.setPulseHeight(height)
         //  no overflow for the bypass experience
         return if (bypassController.bypassEnabled) 0.0f else overflow
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationShelfComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationShelfComponent.java
new file mode 100644
index 0000000..af8d6ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationShelfComponent.java
@@ -0,0 +1,60 @@
+/*
+ * 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.systemui.statusbar.notification.row.dagger;
+
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+
+import dagger.Binds;
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Subcomponent;
+
+/**
+ * Dagger subcomponent for NotificationShelf.
+ */
+@Subcomponent(modules = {ActivatableNotificationViewModule.class,
+        NotificationShelfComponent.NotificationShelfModule.class})
+@NotificationRowScope
+public interface NotificationShelfComponent {
+    /**
+     * Builder for {@link NotificationShelfComponent}.
+     */
+    @Subcomponent.Builder
+    interface Builder {
+        @BindsInstance
+        Builder notificationShelf(NotificationShelf view);
+        NotificationShelfComponent build();
+    }
+
+    /**
+     * Creates a NotificationShelfController.
+     */
+    @NotificationRowScope
+    NotificationShelfController getNotificationShelfController();
+    /**
+     * Dagger Module that extracts interesting properties from a NotificationShelf.
+     */
+    @Module
+    abstract class NotificationShelfModule {
+
+        /** NotificationShelf is provided as an instance of ActivatableNotificationView. */
+        @Binds
+        abstract ActivatableNotificationView bindNotificationShelf(NotificationShelf view);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 66a541a..2ff67ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -19,7 +19,6 @@
 import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
@@ -113,6 +112,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -209,7 +209,6 @@
     private final Paint mBackgroundPaint = new Paint();
     private final boolean mShouldDrawNotificationBackground;
     private boolean mHighPriorityBeforeSpeedBump;
-    private final boolean mAllowLongPress;
     private boolean mDismissRtl;
 
     private float mExpandedHeight;
@@ -538,6 +537,7 @@
     private int mGapHeight;
 
     private int mWaterfallTopInset;
+    private NotificationStackScrollLayoutController mController;
 
     private SysuiColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
             (colorExtractor, which) -> {
@@ -549,7 +549,6 @@
     public NotificationStackScrollLayout(
             @Named(VIEW_CONTEXT) Context context,
             AttributeSet attrs,
-            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
             NotificationRoundnessManager notificationRoundnessManager,
             DynamicPrivacyController dynamicPrivacyController,
             SysuiStatusBarStateController statusBarStateController,
@@ -572,8 +571,6 @@
         super(context, attrs, 0, 0);
         Resources res = getResources();
 
-        mAllowLongPress = allowLongPress;
-
         mRoundnessManager = notificationRoundnessManager;
 
         mLockscreenUserManager = notificationLockscreenUserManager;
@@ -714,9 +711,6 @@
         inflateEmptyShadeView();
         inflateFooterView();
         mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
-        if (mAllowLongPress) {
-            setLongPressListener(mNotificationGutsManager::openGuts);
-        }
     }
 
     /**
@@ -5419,28 +5413,23 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setShelf(NotificationShelf shelf) {
+    public void setShelfController(NotificationShelfController notificationShelfController) {
         int index = -1;
         if (mShelf != null) {
             index = indexOfChild(mShelf);
             removeView(mShelf);
         }
-        mShelf = shelf;
+        mShelf = notificationShelfController.getView();
         addView(mShelf, index);
-        mAmbientState.setShelf(shelf);
-        mStateAnimator.setShelf(shelf);
-        shelf.bind(mAmbientState, this);
+        mAmbientState.setShelf(mShelf);
+        mStateAnimator.setShelf(mShelf);
+        notificationShelfController.bind(mAmbientState, mController);
         if (ANCHOR_SCROLLING) {
             mScrollAnchorView = mShelf;
         }
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationShelf getNotificationShelf() {
-        return mShelf;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
         if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
             mMaxDisplayedNotifications = maxDisplayedNotifications;
@@ -5908,6 +5897,15 @@
         return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
     }
 
+    public void setController(
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
+        mController = notificationStackScrollLayoutController;
+    }
+
+    public NotificationStackScrollLayoutController getController() {
+        return mController;
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
new file mode 100644
index 0000000..8150593
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -0,0 +1,540 @@
+/*
+ * 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.systemui.statusbar.notification.stack;
+
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+
+import android.graphics.PointF;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+
+import java.util.function.BiConsumer;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Controller for {@link NotificationStackScrollLayout}.
+ */
+@StatusBarComponent.StatusBarScope
+public class NotificationStackScrollLayoutController {
+    private final boolean mAllowLongPress;
+    private final NotificationGutsManager mNotificationGutsManager;
+    private NotificationStackScrollLayout mView;
+
+    @Inject
+    public NotificationStackScrollLayoutController(
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
+            NotificationGutsManager notificationGutsManager) {
+        mAllowLongPress = allowLongPress;
+        mNotificationGutsManager = notificationGutsManager;
+    }
+
+    public void attach(NotificationStackScrollLayout view) {
+        mView = view;
+        mView.setController(this);
+
+        if (mAllowLongPress) {
+            mView.setLongPressListener(mNotificationGutsManager::openGuts);
+        }
+    }
+
+    public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
+        mView.addOnExpandedHeightChangedListener(listener);
+    }
+
+    public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
+        mView.removeOnExpandedHeightChangedListener(listener);
+    }
+
+    public void addOnLayoutChangeListener(View.OnLayoutChangeListener listener) {
+        mView.addOnLayoutChangeListener(listener);
+    }
+
+    public void removeOnLayoutChangeListener(View.OnLayoutChangeListener listener) {
+        mView.removeOnLayoutChangeListener(listener);
+    }
+
+    public void setHeadsUpAppearanceController(HeadsUpAppearanceController controller) {
+        mView.setHeadsUpAppearanceController(controller);
+    }
+
+    public void requestLayout() {
+        mView.requestLayout();
+    }
+
+    public Display getDisplay() {
+        return mView.getDisplay();
+    }
+
+    public WindowInsets getRootWindowInsets() {
+        return mView.getRootWindowInsets();
+    }
+
+    public int getRight() {
+        return mView.getRight();
+    }
+
+    public boolean isLayoutRtl() {
+        return mView.isLayoutRtl();
+    }
+
+    public float getLeft() {
+        return  mView.getLeft();
+    }
+
+    public float getTranslationX() {
+        return mView.getTranslationX();
+    }
+
+    public void setOnHeightChangedListener(
+            ExpandableView.OnHeightChangedListener listener) {
+        mView.setOnHeightChangedListener(listener);
+    }
+
+    public void setOverscrollTopChangedListener(
+            NotificationStackScrollLayout.OnOverscrollTopChangedListener listener) {
+        mView.setOverscrollTopChangedListener(listener);
+    }
+
+    public void setOnEmptySpaceClickListener(
+            NotificationStackScrollLayout.OnEmptySpaceClickListener listener) {
+        mView.setOnEmptySpaceClickListener(listener);
+    }
+
+    public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) {
+        mView.setTrackingHeadsUp(expandableNotificationRow);
+    }
+
+    public void wakeUpFromPulse() {
+        mView.wakeUpFromPulse();
+    }
+
+    public boolean isPulseExpanding() {
+        return mView.isPulseExpanding();
+    }
+
+    public void setOnPulseHeightChangedListener(Runnable listener) {
+        mView.setOnPulseHeightChangedListener(listener);
+    }
+
+    public void setDozeAmount(float amount) {
+        mView.setDozeAmount(amount);
+    }
+
+    public float getWakeUpHeight() {
+        return mView.getWakeUpHeight();
+    }
+
+    public void setHideAmount(float linearAmount, float amount) {
+        mView.setHideAmount(linearAmount, amount);
+    }
+
+    public void notifyHideAnimationStart(boolean hide) {
+        mView.notifyHideAnimationStart(hide);
+    }
+
+    public float setPulseHeight(float height) {
+        return mView.setPulseHeight(height);
+    }
+
+    public void getLocationOnScreen(int[] outLocation) {
+        mView.getLocationOnScreen(outLocation);
+    }
+
+    public ExpandableView getChildAtRawPosition(float x, float y) {
+        return mView.getChildAtRawPosition(x, y);
+    }
+
+    public FrameLayout.LayoutParams getLayoutParams() {
+        return (FrameLayout.LayoutParams) mView.getLayoutParams();
+    }
+
+    public void setLayoutParams(FrameLayout.LayoutParams lp) {
+        mView.setLayoutParams(lp);
+    }
+
+    public void setIsFullWidth(boolean isFullWidth) {
+        mView.setIsFullWidth(isFullWidth);
+    }
+
+    public boolean isAddOrRemoveAnimationPending() {
+        return mView.isAddOrRemoveAnimationPending();
+    }
+
+    public int getVisibleNotificationCount() {
+        return mView.getVisibleNotificationCount();
+    }
+
+    public int getIntrinsicContentHeight() {
+        return mView.getIntrinsicContentHeight();
+    }
+
+    public void setIntrinsicPadding(int intrinsicPadding) {
+        mView.setIntrinsicPadding(intrinsicPadding);
+    }
+
+    public int getHeight() {
+        return mView.getHeight();
+    }
+
+    public int getChildCount() {
+        return mView.getChildCount();
+    }
+
+    public ExpandableView getChildAt(int i) {
+        return (ExpandableView) mView.getChildAt(i);
+    }
+
+    public void goToFullShade(long delay) {
+        mView.goToFullShade(delay);
+    }
+
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators) {
+        mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators);
+    }
+
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
+        mView.setOverScrollAmount(amount, onTop, animate);
+    }
+
+    public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
+        mView.setOverScrolledPixels(numPixels, onTop, animate);
+    }
+
+    public void resetScrollPosition() {
+        mView.resetScrollPosition();
+    }
+
+    public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
+        mView.setShouldShowShelfOnly(shouldShowShelfOnly);
+    }
+
+    public void cancelLongPress() {
+        mView.cancelLongPress();
+    }
+
+    public float getX() {
+        return mView.getX();
+    }
+
+    public boolean isBelowLastNotification(float x, float y) {
+        return mView.isBelowLastNotification(x, y);
+    }
+
+    public float getWidth() {
+        return mView.getWidth();
+    }
+
+    public float getOpeningHeight() {
+        return mView.getOpeningHeight();
+    }
+
+    public float getBottomMostNotificationBottom() {
+        return mView.getBottomMostNotificationBottom();
+    }
+
+    public void checkSnoozeLeavebehind() {
+        mView.checkSnoozeLeavebehind();
+    }
+
+    public void setQsExpanded(boolean expanded) {
+        mView.setQsExpanded(expanded);
+    }
+
+    public void setScrollingEnabled(boolean enabled) {
+        mView.setScrollingEnabled(enabled);
+    }
+
+    public void setQsExpansionFraction(float expansionFraction) {
+        mView.setQsExpansionFraction(expansionFraction);
+    }
+
+    public float calculateAppearFractionBypass() {
+        return mView.calculateAppearFractionBypass();
+    }
+
+    public void updateTopPadding(float qsHeight, boolean animate) {
+        mView.updateTopPadding(qsHeight, animate);
+    }
+
+    public void resetCheckSnoozeLeavebehind() {
+        mView.resetCheckSnoozeLeavebehind();
+    }
+
+    public boolean isScrolledToBottom() {
+        return mView.isScrolledToBottom();
+    }
+
+    public int getNotGoneChildCount() {
+        return mView.getNotGoneChildCount();
+    }
+
+    public float getIntrinsicPadding() {
+        return mView.getIntrinsicPadding();
+    }
+
+    public float getLayoutMinHeight() {
+        return mView.getLayoutMinHeight();
+    }
+
+    public int getEmptyBottomMargin() {
+        return mView.getEmptyBottomMargin();
+    }
+
+    public float getTopPaddingOverflow() {
+        return mView.getTopPaddingOverflow();
+    }
+
+    public int getTopPadding() {
+        return mView.getTopPadding();
+    }
+
+    public float getEmptyShadeViewHeight() {
+        return mView.getEmptyShadeViewHeight();
+    }
+
+    public void setAlpha(float alpha) {
+        mView.setAlpha(alpha);
+    }
+
+    public float getCurrentOverScrollAmount(boolean top) {
+        return mView.getCurrentOverScrollAmount(top);
+    }
+
+    public float getCurrentOverScrolledPixels(boolean top) {
+        return mView.getCurrentOverScrolledPixels(top);
+    }
+
+    public float calculateAppearFraction(float height) {
+        return mView.calculateAppearFraction(height);
+    }
+
+    public void onExpansionStarted() {
+        mView.onExpansionStarted();
+    }
+
+    public void onExpansionStopped() {
+        mView.onExpansionStopped();
+    }
+
+    public void onPanelTrackingStarted() {
+        mView.onPanelTrackingStarted();
+    }
+
+    public void onPanelTrackingStopped() {
+        mView.onPanelTrackingStopped();
+    }
+
+    public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
+        mView.setHeadsUpBoundaries(height, bottomBarHeight);
+    }
+
+    public void setUnlockHintRunning(boolean running) {
+        mView.setUnlockHintRunning(running);
+    }
+
+    public float getPeekHeight() {
+        return mView.getPeekHeight();
+    }
+
+    public boolean isFooterViewNotGone() {
+        return mView.isFooterViewNotGone();
+    }
+
+    public boolean isFooterViewContentVisible() {
+        return mView.isFooterViewContentVisible();
+    }
+
+    public int getFooterViewHeightWithPadding() {
+        return mView.getFooterViewHeightWithPadding();
+    }
+
+    public void updateEmptyShadeView(boolean visible) {
+        mView.updateEmptyShadeView(visible);
+    }
+
+    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+        mView.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+    }
+
+    public HeadsUpTouchHelper.Callback getHeadsUpCallback() {
+        return mView.getHeadsUpCallback();
+    }
+
+    public void forceNoOverlappingRendering(boolean force) {
+        mView.forceNoOverlappingRendering(force);
+    }
+
+    public void setTranslationX(float translation) {
+        mView.setTranslationX(translation);
+    }
+
+    public void setExpandingVelocity(float velocity) {
+        mView.setExpandingVelocity(velocity);
+    }
+
+    public void setExpandedHeight(float expandedHeight) {
+        mView.setExpandedHeight(expandedHeight);
+    }
+
+    public void setQsContainer(ViewGroup view) {
+        mView.setQsContainer(view);
+    }
+
+    public void setAnimationsEnabled(boolean enabled) {
+        mView.setAnimationsEnabled(enabled);
+    }
+
+    public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
+        mView.setDozing(dozing, animate, wakeUpTouchLocation);
+    }
+
+    public void setPulsing(boolean pulsing, boolean animatePulse) {
+        mView.setPulsing(pulsing, animatePulse);
+    }
+
+    public boolean hasActiveClearableNotifications(
+            @NotificationStackScrollLayout.SelectedRows int selection) {
+        return mView.hasActiveClearableNotifications(selection);
+    }
+
+    public RemoteInputController.Delegate createDelegate() {
+        return mView.createDelegate();
+    }
+
+    public void updateSectionBoundaries(String reason) {
+        mView.updateSectionBoundaries(reason);
+    }
+
+    public void updateSpeedBumpIndex() {
+        mView.updateSpeedBumpIndex();
+    }
+
+    public void updateFooter() {
+        mView.updateFooter();
+    }
+
+    public void updateIconAreaViews() {
+        mView.updateIconAreaViews();
+    }
+
+    public void onUpdateRowStates() {
+        mView.onUpdateRowStates();
+    }
+
+    public boolean hasPulsingNotifications() {
+        return mView.hasPulsingNotifications();
+    }
+
+    public ActivatableNotificationView getActivatedChild() {
+        return mView.getActivatedChild();
+    }
+
+    public void setActivatedChild(ActivatableNotificationView view) {
+        mView.setActivatedChild(view);
+    }
+
+    public void runAfterAnimationFinished(Runnable r) {
+        mView.runAfterAnimationFinished(r);
+    }
+
+    public void setNotificationPanelController(
+            NotificationPanelViewController notificationPanelViewController) {
+        mView.setNotificationPanelController(notificationPanelViewController);
+    }
+
+    public void setIconAreaController(NotificationIconAreaController controller) {
+        mView.setIconAreaController(controller);
+    }
+
+    public void setStatusBar(StatusBar statusBar) {
+        mView.setStatusBar(statusBar);
+    }
+
+    public void setGroupManager(NotificationGroupManager groupManager) {
+        mView.setGroupManager(groupManager);
+    }
+
+    public void setShelfController(NotificationShelfController notificationShelfController) {
+        mView.setShelfController(notificationShelfController);
+    }
+
+    public void setScrimController(ScrimController scrimController) {
+        mView.setScrimController(scrimController);
+    }
+
+    public ExpandableView getFirstChildNotGone() {
+        return mView.getFirstChildNotGone();
+    }
+
+    public void setInHeadsUpPinnedMode(boolean inPinnedMode) {
+        mView.setInHeadsUpPinnedMode(inPinnedMode);
+    }
+
+    public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
+        mView.generateHeadsUpAnimation(entry, isHeadsUp);
+    }
+
+    public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
+        mView.generateHeadsUpAnimation(row, isHeadsUp);
+    }
+
+    public void setMaxTopPadding(int padding) {
+        mView.setMaxTopPadding(padding);
+    }
+
+    public int getTransientViewCount() {
+        return mView.getTransientViewCount();
+    }
+
+    public View getTransientView(int i) {
+        return mView.getTransientView(i);
+    }
+
+    public int getPositionInLinearLayout(ExpandableView row) {
+        return mView.getPositionInLinearLayout(row);
+    }
+
+    public NotificationStackScrollLayout getView() {
+        return mView;
+    }
+
+    public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) {
+        return mView.calculateGapHeight(previousView, child, count);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 51c02c9..8cdaa63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
@@ -52,7 +52,7 @@
     public static final int CONTENT_FADE_DELAY = 100;
     private final NotificationIconAreaController mNotificationIconAreaController;
     private final HeadsUpManagerPhone mHeadsUpManager;
-    private final NotificationStackScrollLayout mStackScroller;
+    private final NotificationStackScrollLayoutController mStackScrollerController;
     private final HeadsUpStatusBarView mHeadsUpStatusBarView;
     private final View mCenteredIconView;
     private final View mClockView;
@@ -93,7 +93,7 @@
     public HeadsUpAppearanceController(
             NotificationIconAreaController notificationIconAreaController,
             HeadsUpManagerPhone headsUpManager,
-            View notificationShadeView,
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController,
             SysuiStatusBarStateController statusBarStateController,
             KeyguardBypassController keyguardBypassController,
             KeyguardStateController keyguardStateController,
@@ -101,10 +101,9 @@
             NotificationPanelViewController notificationPanelViewController, View statusBarView) {
         this(notificationIconAreaController, headsUpManager, statusBarStateController,
                 keyguardBypassController, wakeUpCoordinator, keyguardStateController,
-                commandQueue,
-                statusBarView.findViewById(R.id.heads_up_status_bar_view),
-                notificationShadeView.findViewById(R.id.notification_stack_scroller),
+                commandQueue, notificationStackScrollLayoutController,
                 notificationPanelViewController,
+                statusBarView.findViewById(R.id.heads_up_status_bar_view),
                 statusBarView.findViewById(R.id.clock),
                 statusBarView.findViewById(R.id.operator_name_frame),
                 statusBarView.findViewById(R.id.centered_icon_area));
@@ -119,9 +118,9 @@
             NotificationWakeUpCoordinator wakeUpCoordinator,
             KeyguardStateController keyguardStateController,
             CommandQueue commandQueue,
-            HeadsUpStatusBarView headsUpStatusBarView,
-            NotificationStackScrollLayout stackScroller,
+            NotificationStackScrollLayoutController stackScrollerController,
             NotificationPanelViewController notificationPanelViewController,
+            HeadsUpStatusBarView headsUpStatusBarView,
             View clockView,
             View operatorNameView,
             View centeredIconView) {
@@ -132,14 +131,14 @@
         mCenteredIconView = centeredIconView;
         headsUpStatusBarView.setOnDrawingRectChangedListener(
                 () -> updateIsolatedIconLocation(true /* requireUpdate */));
-        mStackScroller = stackScroller;
+        mStackScrollerController = stackScrollerController;
         mNotificationPanelViewController = notificationPanelViewController;
         notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
         notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation);
         notificationPanelViewController.setHeadsUpAppearanceController(this);
-        mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight);
-        mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
-        mStackScroller.setHeadsUpAppearanceController(this);
+        mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
+        mStackScrollerController.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
+        mStackScrollerController.setHeadsUpAppearanceController(this);
         mClockView = clockView;
         mOperatorNameView = operatorNameView;
         mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
@@ -153,7 +152,7 @@
                     updateTopEntry();
 
                     // trigger scroller to notify the latest panel translation
-                    mStackScroller.requestLayout();
+                    mStackScrollerController.requestLayout();
                 }
                 mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
             }
@@ -174,8 +173,8 @@
         mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
         mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation);
         mNotificationPanelViewController.setHeadsUpAppearanceController(null);
-        mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
-        mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
+        mStackScrollerController.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
+        mStackScrollerController.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
         mDarkIconDispatcher.removeDarkReceiver(this);
     }
 
@@ -219,12 +218,12 @@
         }
 
         int realDisplaySize = 0;
-        if (mStackScroller.getDisplay() != null) {
-            mStackScroller.getDisplay().getRealSize(mPoint);
+        if (mStackScrollerController.getDisplay() != null) {
+            mStackScrollerController.getDisplay().getRealSize(mPoint);
             realDisplaySize = mPoint.x;
         }
 
-        WindowInsets windowInset = mStackScroller.getRootWindowInsets();
+        WindowInsets windowInset = mStackScrollerController.getRootWindowInsets();
         DisplayCutout cutout = (windowInset != null) ? windowInset.getDisplayCutout() : null;
         int sysWinLeft = (windowInset != null) ? windowInset.getStableInsetLeft() : 0;
         int sysWinRight = (windowInset != null) ? windowInset.getStableInsetRight() : 0;
@@ -233,17 +232,17 @@
         int leftInset = Math.max(sysWinLeft, cutoutLeft);
         int rightInset = Math.max(sysWinRight, cutoutRight);
 
-        return leftInset + mStackScroller.getRight() + rightInset - realDisplaySize;
+        return leftInset + mStackScrollerController.getRight() + rightInset - realDisplaySize;
     }
 
     public void updatePanelTranslation() {
         float newTranslation;
-        if (mStackScroller.isLayoutRtl()) {
+        if (mStackScrollerController.isLayoutRtl()) {
             newTranslation = getRtlTranslation();
         } else {
-            newTranslation = mStackScroller.getLeft();
+            newTranslation = mStackScrollerController.getLeft();
         }
-        newTranslation += mStackScroller.getTranslationX();
+        newTranslation += mStackScrollerController.getTranslationX();
         mHeadsUpStatusBarView.setPanelTranslation(newTranslation);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 9d3e915..7bbe1c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -26,7 +26,7 @@
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -160,9 +160,9 @@
         }
     }
 
-    public void setupShelf(NotificationShelf shelf) {
-        mShelfIcons = shelf.getShelfIcons();
-        shelf.setCollapsedIcons(mNotificationIcons);
+    public void setupShelf(NotificationShelfController notificationShelfController) {
+        mShelfIcons = notificationShelfController.getShelfIcons();
+        notificationShelfController.setCollapsedIcons(mNotificationIcons);
     }
 
     public void onDensityOrFontScaleChanged(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a87311a..70fb71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -86,6 +86,7 @@
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -105,6 +106,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -174,6 +176,7 @@
     private final ZenModeController mZenModeController;
     private final ConfigurationController mConfigurationController;
     private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+    private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
     // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
     // changed.
@@ -264,7 +267,6 @@
     private KeyguardStatusView mKeyguardStatusView;
     private View mQsNavbarScrim;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
-    private NotificationStackScrollLayout mNotificationStackScroller;
     private boolean mAnimateNextPositionUpdate;
 
     private int mTrackingPointer;
@@ -454,6 +456,7 @@
 
     private boolean mAnimatingQS;
     private int mOldLayoutDirection;
+    private NotificationShelfController mNotificationShelfController;
 
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
         @Override
@@ -498,7 +501,8 @@
             MediaHierarchyManager mediaHierarchyManager,
             BiometricUnlockController biometricUnlockController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider) {
+            Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider,
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
                 latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
@@ -511,6 +515,7 @@
         mMediaHierarchyManager = mediaHierarchyManager;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardClockSwitchControllerProvider = keyguardClockSwitchControllerProvider;
+        mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mView.setWillNotDraw(!DEBUG);
         mInjectionInflationController = injectionInflationController;
         mFalsingManager = falsingManager;
@@ -590,21 +595,26 @@
         keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
 
         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
-        mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller);
-        mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
-        mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener);
-        mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener);
-        addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
+        NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
+                R.id.notification_stack_scroller);
+        mNotificationStackScrollLayoutController.attach(stackScrollLayout);
+        mNotificationStackScrollLayoutController.setOnHeightChangedListener(
+                mOnHeightChangedListener);
+        mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
+                mOnOverscrollTopChangedListener);
+        mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
+                mOnEmptySpaceClickListener);
+        addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
         mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
         mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim);
         mLastOrientation = mResources.getConfiguration().orientation;
 
         initBottomArea();
 
-        mWakeUpCoordinator.setStackScroller(mNotificationStackScroller);
+        mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
         mQsFrame = mView.findViewById(R.id.qs_frame);
         mPulseExpansionHandler.setUp(
-                mNotificationStackScroller, mExpansionCallback, mShadeController);
+                mNotificationStackScrollLayoutController, mExpansionCallback, mShadeController);
         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
             @Override
             public void onFullyHiddenChanged(boolean isFullyHidden) {
@@ -688,11 +698,11 @@
         }
 
         int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
-        lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
+        lp = mNotificationStackScrollLayoutController.getLayoutParams();
         if (lp.width != panelWidth || lp.gravity != panelGravity) {
             lp.width = panelWidth;
             lp.gravity = panelGravity;
-            mNotificationStackScroller.setLayoutParams(lp);
+            mNotificationStackScrollLayoutController.setLayoutParams(lp);
         }
     }
 
@@ -770,7 +780,7 @@
 
     private void setIsFullWidth(boolean isFullWidth) {
         mIsFullWidth = isFullWidth;
-        mNotificationStackScroller.setIsFullWidth(isFullWidth);
+        mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth);
     }
 
     private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
@@ -804,7 +814,7 @@
      * showing.
      */
     private void positionClockAndNotifications() {
-        boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+        boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
         boolean animateClock = animate || mAnimateNextPositionUpdate;
         int stackScrollerPadding;
         if (mBarState != StatusBarState.KEYGUARD) {
@@ -814,12 +824,12 @@
             int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
             int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
             boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
-            final boolean
-                    hasVisibleNotifications =
-                    !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0;
+            final boolean hasVisibleNotifications = !bypassEnabled
+                    && mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0;
             mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
             mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
-                    mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(),
+                    mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
+                    getExpandedFraction(),
                     totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f
                             - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(),
                     hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
@@ -833,7 +843,7 @@
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
         }
-        mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
+        mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
         mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
 
         mStackScrollerMeasuringPass++;
@@ -858,36 +868,51 @@
         float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
         int notificationPadding = Math.max(
                 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
-        NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
+        NotificationShelf shelf = mNotificationShelfController.getView();
         float
                 shelfSize =
                 shelf.getVisibility() == View.GONE ? 0
                         : shelf.getIntrinsicHeight() + notificationPadding;
         float
                 availableSpace =
-                mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max(
-                        mIndicationBottomPadding, mAmbientIndicationBottomPadding)
+                mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize
+                        - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)
                         - mKeyguardStatusView.getLogoutButtonHeight();
         int count = 0;
         ExpandableView previousView = null;
-        for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
+        for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) {
+            ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                continue;
+            }
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            boolean
+                    suppressedSummary =
+                    mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup(
+                            row.getEntry().getSbn());
+            if (suppressedSummary) {
+                continue;
+            }
             if (!canShowViewOnLockscreen(child)) {
                 continue;
             }
+            if (row.isRemoved()) {
+                continue;
+            }
             availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */);
             availableSpace -= count == 0 ? 0 : notificationPadding;
-            availableSpace -= mNotificationStackScroller.calculateGapHeight(previousView, child,
-                    count);
+            availableSpace -= mNotificationStackScrollLayoutController
+                    .calculateGapHeight(previousView, child, count);
             previousView = child;
             if (availableSpace >= 0 && count < maximum) {
                 count++;
             } else if (availableSpace > -shelfSize) {
                 // if we are exactly the last view, then we can show us still!
-                for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
-                    ExpandableView view = (ExpandableView) mNotificationStackScroller.getChildAt(j);
-                    if (view instanceof ExpandableNotificationRow &&
-                            canShowViewOnLockscreen(view)) {
+                int childCount = mNotificationStackScrollLayoutController.getChildCount();
+                for (int j = i + 1; j < childCount; j++) {
+                    ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j);
+                    if (view instanceof ExpandableNotificationRow
+                            && canShowViewOnLockscreen(view)) {
                         return count;
                     }
                 }
@@ -952,7 +977,7 @@
     }
 
     public void animateToFullShade(long delay) {
-        mNotificationStackScroller.goToFullShade(delay);
+        mNotificationStackScrollLayoutController.goToFullShade(delay);
         mView.requestLayout();
         mAnimateNextPositionUpdate = true;
     }
@@ -978,9 +1003,9 @@
         } else {
             closeQs();
         }
-        mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate,
+        mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate,
                 !animate /* cancelAnimators */);
-        mNotificationStackScroller.resetScrollPosition();
+        mNotificationStackScrollLayoutController.resetScrollPosition();
     }
 
     @Override
@@ -991,7 +1016,7 @@
 
         if (mQsExpanded) {
             mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
+            mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
         }
         super.collapse(delayed, speedUpFactor);
     }
@@ -1027,7 +1052,7 @@
     public void expandWithQs() {
         if (mQsExpansionEnabled) {
             mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
+            mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
         }
         if (isFullyCollapsed()) {
             expand(true /* animate */);
@@ -1088,7 +1113,7 @@
                     onQsExpansionStarted();
                     mInitialHeightOnTouch = mQsExpansionHeight;
                     mQsTracking = true;
-                    mNotificationStackScroller.cancelLongPress();
+                    mNotificationStackScrollLayoutController.cancelLongPress();
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -1124,7 +1149,7 @@
                     mInitialHeightOnTouch = mQsExpansionHeight;
                     mInitialTouchY = y;
                     mInitialTouchX = x;
-                    mNotificationStackScroller.cancelLongPress();
+                    mNotificationStackScrollLayoutController.cancelLongPress();
                     return true;
                 }
                 break;
@@ -1144,9 +1169,11 @@
 
     @Override
     protected boolean isInContentBounds(float x, float y) {
-        float stackScrollerX = mNotificationStackScroller.getX();
-        return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
-                && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
+        float stackScrollerX = mNotificationStackScrollLayoutController.getX();
+        return !mNotificationStackScrollLayoutController
+                .isBelowLastNotification(x - stackScrollerX, y)
+                && stackScrollerX < x
+                && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
     }
 
     private void initDownStates(MotionEvent event) {
@@ -1254,7 +1281,7 @@
 
     @Override
     protected float getOpeningHeight() {
-        return mNotificationStackScroller.getOpeningHeight();
+        return mNotificationStackScrollLayoutController.getOpeningHeight();
     }
 
 
@@ -1290,7 +1317,7 @@
                 < mStatusBarMinHeight) {
             mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
             mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
+            mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
             requestPanelHeightUpdate();
 
             // Normally, we start listening when the panel is expanded, but here we need to start
@@ -1302,7 +1329,7 @@
 
     private boolean isInQsArea(float x, float y) {
         return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && (
-                y <= mNotificationStackScroller.getBottomMostNotificationBottom()
+                y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
                         || y <= mQs.getView().getY() + mQs.getView().getHeight());
     }
 
@@ -1492,7 +1519,7 @@
         float height = mQsExpansionHeight - overscrollAmount;
         setQsExpansion(height);
         requestPanelHeightUpdate();
-        mNotificationStackScroller.checkSnoozeLeavebehind();
+        mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
 
         // When expanding QS, let's authenticate the user if possible,
         // this will speed up notification actions.
@@ -1665,8 +1692,8 @@
     }
 
     private void updateQsState() {
-        mNotificationStackScroller.setQsExpanded(mQsExpanded);
-        mNotificationStackScroller.setScrollingEnabled(
+        mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
+        mNotificationStackScrollLayoutController.setScrollingEnabled(
                 mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
         updateEmptyShadeView();
@@ -1726,7 +1753,7 @@
         float qsExpansionFraction = getQsExpansionFraction();
         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
         mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
-        mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
+        mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
     }
 
     private String determineAccessibilityPaneTitle() {
@@ -1785,18 +1812,18 @@
             return mClockPositionResult.stackScrollerPadding;
         }
         int collapsedPosition = mHeadsUpInset;
-        if (!mNotificationStackScroller.isPulseExpanding()) {
+        if (!mNotificationStackScrollLayoutController.isPulseExpanding()) {
             return collapsedPosition;
         } else {
             int expandedPosition = mClockPositionResult.stackScrollerPadding;
             return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
-                    mNotificationStackScroller.calculateAppearFractionBypass());
+                    mNotificationStackScrollLayoutController.calculateAppearFractionBypass());
         }
     }
 
 
     protected void requestScrollerTopPaddingUpdate(boolean animate) {
-        mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
+        mNotificationStackScrollLayoutController.updateTopPadding(calculateQsTopPadding(), animate);
         if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
             // update the position of the header
             updateQsExpansion();
@@ -1808,7 +1835,7 @@
         if (mQs != null) {
             mQs.setShowCollapsedOnKeyguard(
                     mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()
-                            && mNotificationStackScroller.isPulseExpanding());
+                            && mNotificationStackScrollLayoutController.isPulseExpanding());
         }
     }
 
@@ -1904,7 +1931,7 @@
             public void onAnimationEnd(Animator animation) {
                 mAnimatingQS = false;
                 notifyExpandingFinished();
-                mNotificationStackScroller.resetCheckSnoozeLeavebehind();
+                mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind();
                 mQsExpansionAnimator = null;
                 if (onFinishRunnable != null) {
                     onFinishRunnable.run();
@@ -1943,8 +1970,8 @@
     protected boolean canCollapsePanelOnTouch() {
         if (!isInSettings()) {
             return mBarState == StatusBarState.KEYGUARD
-                    || mNotificationStackScroller.isScrolledToBottom()
-                    || mIsPanelCollapseOnQQS;
+                    || mIsPanelCollapseOnQQS
+                    || mNotificationStackScrollLayoutController.isScrolledToBottom();
         } else {
             return true;
         }
@@ -1962,7 +1989,7 @@
     private int getMaxPanelHeightNonBypass() {
         int min = mStatusBarMinHeight;
         if (!(mBarState == StatusBarState.KEYGUARD)
-                && mNotificationStackScroller.getNotGoneChildCount() == 0) {
+                && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
             int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
             min = Math.max(min, minHeight);
         }
@@ -1988,7 +2015,7 @@
         int position =
                 mClockPositionAlgorithm.getExpandedClockPosition()
                         + mKeyguardStatusView.getHeight();
-        if (mNotificationStackScroller.getVisibleNotificationCount() != 0) {
+        if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) {
             position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
         }
         return position;
@@ -2027,8 +2054,8 @@
                 // minimum QS expansion + minStackHeight
                 float
                         panelHeightQsCollapsed =
-                        mNotificationStackScroller.getIntrinsicPadding()
-                                + mNotificationStackScroller.getLayoutMinHeight();
+                        mNotificationStackScrollLayoutController.getIntrinsicPadding()
+                                + mNotificationStackScrollLayoutController.getLayoutMinHeight();
                 float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
                 t =
                         (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
@@ -2060,16 +2087,16 @@
     }
 
     private int calculatePanelHeightShade() {
-        int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
-        int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
-        maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
+        int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin();
+        int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin;
+        maxHeight += mNotificationStackScrollLayoutController.getTopPaddingOverflow();
 
         if (mBarState == StatusBarState.KEYGUARD) {
             int
                     minKeyguardPanelBottom =
                     mClockPositionAlgorithm.getExpandedClockPosition()
                             + mKeyguardStatusView.getHeight()
-                            + mNotificationStackScroller.getIntrinsicContentHeight();
+                            + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
             return Math.max(maxHeight, minKeyguardPanelBottom);
         } else {
             return maxHeight;
@@ -2079,15 +2106,16 @@
     private int calculatePanelHeightQsExpanded() {
         float
                 notificationHeight =
-                mNotificationStackScroller.getHeight()
-                        - mNotificationStackScroller.getEmptyBottomMargin()
-                        - mNotificationStackScroller.getTopPadding();
+                mNotificationStackScrollLayoutController.getHeight()
+                        - mNotificationStackScrollLayoutController.getEmptyBottomMargin()
+                        - mNotificationStackScrollLayoutController.getTopPadding();
 
         // When only empty shade view is visible in QS collapsed state, simulate that we would have
         // it in expanded QS state as well so we don't run into troubles when fading the view in/out
         // and expanding/collapsing the whole panel from/to quick settings.
-        if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) {
-            notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
+        if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0
+                && mShowEmptyShadeView) {
+            notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight();
         }
         int maxQsHeight = mQsMaxExpansionHeight;
 
@@ -2102,12 +2130,13 @@
         float totalHeight = Math.max(maxQsHeight,
                 mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding
                         : 0) + notificationHeight
-                + mNotificationStackScroller.getTopPaddingOverflow();
-        if (totalHeight > mNotificationStackScroller.getHeight()) {
+                + mNotificationStackScrollLayoutController.getTopPaddingOverflow();
+        if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) {
             float
                     fullyCollapsedHeight =
-                    maxQsHeight + mNotificationStackScroller.getLayoutMinHeight();
-            totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
+                    maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight();
+            totalHeight = Math.max(fullyCollapsedHeight,
+                    mNotificationStackScrollLayoutController.getHeight());
         }
         return (int) totalHeight;
     }
@@ -2122,7 +2151,7 @@
                 && !mKeyguardBypassController.getBypassEnabled()) {
             alpha *= mClockPositionResult.clockAlpha;
         }
-        mNotificationStackScroller.setAlpha(alpha);
+        mNotificationStackScrollLayoutController.setAlpha(alpha);
     }
 
     private float getFadeoutAlpha() {
@@ -2138,7 +2167,8 @@
 
     @Override
     protected float getOverExpansionAmount() {
-        float result = mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
+        float result = mNotificationStackScrollLayoutController
+                .getCurrentOverScrollAmount(true /* top */);
         if (isNaN(result)) {
             Log.wtf(TAG, "OverExpansionAmount is NaN!");
         }
@@ -2148,7 +2178,8 @@
 
     @Override
     protected float getOverExpansionPixels() {
-        return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
+        return mNotificationStackScrollLayoutController
+                .getCurrentOverScrolledPixels(true /* top */);
     }
 
     /**
@@ -2165,17 +2196,19 @@
         if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
             return -mQs.getQsMinExpansionHeight();
         }
-        float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
+        float appearAmount = mNotificationStackScrollLayoutController
+                .calculateAppearFraction(mExpandedHeight);
         float startHeight = -mQsExpansionHeight;
         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
-                && mNotificationStackScroller.isPulseExpanding()) {
+                && mNotificationStackScrollLayoutController.isPulseExpanding()) {
             if (!mPulseExpansionHandler.isExpanding()
                     && !mPulseExpansionHandler.getLeavingLockscreen()) {
                 // If we aborted the expansion we need to make sure the header doesn't reappear
                 // again after the header has animated away
                 appearAmount = 0;
             } else {
-                appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
+                appearAmount = mNotificationStackScrollLayoutController
+                        .calculateAppearFractionBypass();
             }
             startHeight = -mQs.getQsMinExpansionHeight();
         }
@@ -2264,7 +2297,7 @@
     @Override
     protected void onExpandingStarted() {
         super.onExpandingStarted();
-        mNotificationStackScroller.onExpansionStarted();
+        mNotificationStackScrollLayoutController.onExpansionStarted();
         mIsExpanding = true;
         mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
         mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted &&
@@ -2282,7 +2315,7 @@
     @Override
     protected void onExpandingFinished() {
         super.onExpandingFinished();
-        mNotificationStackScroller.onExpansionStopped();
+        mNotificationStackScrollLayoutController.onExpansionStopped();
         mHeadsUpManager.onExpandingFinished();
         mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
         mIsExpanding = false;
@@ -2308,7 +2341,7 @@
             setListening(true);
         }
         mQsExpandImmediate = false;
-        mNotificationStackScroller.setShouldShowShelfOnly(false);
+        mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
         mTwoFingerQsExpandPossible = false;
         notifyListenersTrackingHeadsUp(null);
         mExpandingFromHeadsUp = false;
@@ -2340,15 +2373,16 @@
             return;
         }
         if (mBarState != StatusBarState.KEYGUARD) {
-            mNotificationStackScroller.setOnHeightChangedListener(null);
+            mNotificationStackScrollLayoutController.setOnHeightChangedListener(null);
             if (isPixels) {
-                mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */,
-                        false /* animate */);
+                mNotificationStackScrollLayoutController.setOverScrolledPixels(
+                        overExpansion, true /* onTop */, false /* animate */);
             } else {
-                mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */,
-                        false /* animate */);
+                mNotificationStackScrollLayoutController.setOverScrollAmount(
+                        overExpansion, true /* onTop */, false /* animate */);
             }
-            mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
+            mNotificationStackScrollLayoutController
+                    .setOnHeightChangedListener(mOnHeightChangedListener);
         }
     }
 
@@ -2358,12 +2392,12 @@
         super.onTrackingStarted();
         if (mQsFullyExpanded) {
             mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
+            mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
         }
         if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
             mAffordanceHelper.animateHideLeftRightIcon();
         }
-        mNotificationStackScroller.onPanelTrackingStarted();
+        mNotificationStackScrollLayoutController.onPanelTrackingStarted();
     }
 
     @Override
@@ -2371,10 +2405,10 @@
         mFalsingManager.onTrackingStopped();
         super.onTrackingStopped(expand);
         if (expand) {
-            mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
+            mNotificationStackScrollLayoutController.setOverScrolledPixels(0.0f, true /* onTop */,
                     true /* animate */);
         }
-        mNotificationStackScroller.onPanelTrackingStopped();
+        mNotificationStackScrollLayoutController.onPanelTrackingStopped();
         if (expand && (mBarState == StatusBarState.KEYGUARD
                 || mBarState == StatusBarState.SHADE_LOCKED)) {
             if (!mHintAnimationRunning) {
@@ -2384,7 +2418,8 @@
     }
 
     private void updateMaxHeadsUpTranslation() {
-        mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
+        mNotificationStackScrollLayoutController.setHeadsUpBoundaries(
+                getHeight(), mNavigationBarBottomHeight);
     }
 
     @Override
@@ -2400,19 +2435,19 @@
     @Override
     protected void onUnlockHintFinished() {
         super.onUnlockHintFinished();
-        mNotificationStackScroller.setUnlockHintRunning(false);
+        mNotificationStackScrollLayoutController.setUnlockHintRunning(false);
     }
 
     @Override
     protected void onUnlockHintStarted() {
         super.onUnlockHintStarted();
-        mNotificationStackScroller.setUnlockHintRunning(true);
+        mNotificationStackScrollLayoutController.setUnlockHintRunning(true);
     }
 
     @Override
     protected float getPeekHeight() {
-        if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
-            return mNotificationStackScroller.getPeekHeight();
+        if (mNotificationStackScrollLayoutController.getNotGoneChildCount() > 0) {
+            return mNotificationStackScrollLayoutController.getPeekHeight();
         } else {
             return mQsMinExpansionHeight;
         }
@@ -2426,7 +2461,8 @@
         }
         // Let's make sure we're not appearing but the animation will end below the appear.
         // Otherwise quick settings would jump at the end of the animation.
-        float fraction = mNotificationStackScroller.calculateAppearFraction(targetHeight);
+        float fraction = mNotificationStackScrollLayoutController
+                .calculateAppearFraction(targetHeight);
         return fraction >= 1.0f;
     }
 
@@ -2438,18 +2474,19 @@
 
     @Override
     protected boolean fullyExpandedClearAllVisible() {
-        return mNotificationStackScroller.isFooterViewNotGone()
-                && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
+        return mNotificationStackScrollLayoutController.isFooterViewNotGone()
+                && mNotificationStackScrollLayoutController.isScrolledToBottom()
+                && !mQsExpandImmediate;
     }
 
     @Override
     protected boolean isClearAllVisible() {
-        return mNotificationStackScroller.isFooterViewContentVisible();
+        return mNotificationStackScrollLayoutController.isFooterViewContentVisible();
     }
 
     @Override
     protected int getClearAllHeightWithPadding() {
-        return mNotificationStackScroller.getFooterViewHeightWithPadding();
+        return mNotificationStackScrollLayoutController.getFooterViewHeightWithPadding();
     }
 
     @Override
@@ -2500,7 +2537,8 @@
 
     private void updateEmptyShadeView() {
         // Hide "No notifications" in QS.
-        mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
+        mNotificationStackScrollLayoutController.updateEmptyShadeView(
+                mShowEmptyShadeView && !mQsExpanded);
     }
 
     public void setQsScrimEnabled(boolean qsScrimEnabled) {
@@ -2591,7 +2629,7 @@
 
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         mHeadsUpAnimatingAway = headsUpAnimatingAway;
-        mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+        mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
         updateHeadsUpVisibility();
     }
 
@@ -2603,7 +2641,7 @@
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         super.setHeadsUpManager(headsUpManager);
         mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
-                mNotificationStackScroller.getHeadsUpCallback(),
+                mNotificationStackScrollLayoutController.getHeadsUpCallback(),
                 NotificationPanelViewController.this);
     }
 
@@ -2625,7 +2663,7 @@
 
     private void setClosingWithAlphaFadeout(boolean closing) {
         mClosingWithAlphaFadeOut = closing;
-        mNotificationStackScroller.forceNoOverlappingRendering(closing);
+        mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing);
     }
 
     /**
@@ -2635,22 +2673,24 @@
      * @param x the x-coordinate the touch event
      */
     protected void updateVerticalPanelPosition(float x) {
-        if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) {
+        if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()) {
             resetHorizontalPanelPosition();
             return;
         }
-        float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
+        float leftMost = mPositionMinSideMargin
+                + mNotificationStackScrollLayoutController.getWidth() / 2;
         float
                 rightMost =
                 mView.getWidth() - mPositionMinSideMargin
-                        - mNotificationStackScroller.getWidth() / 2;
-        if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
+                        - mNotificationStackScrollLayoutController.getWidth() / 2;
+        if (Math.abs(x - mView.getWidth() / 2)
+                < mNotificationStackScrollLayoutController.getWidth() / 4) {
             x = mView.getWidth() / 2;
         }
         x = Math.min(rightMost, Math.max(leftMost, x));
         float
-                center =
-                mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
+                center = mNotificationStackScrollLayoutController.getLeft()
+                + mNotificationStackScrollLayoutController.getWidth() / 2;
         setHorizontalPanelTranslation(x - center);
     }
 
@@ -2659,7 +2699,7 @@
     }
 
     protected void setHorizontalPanelTranslation(float translation) {
-        mNotificationStackScroller.setTranslationX(translation);
+        mNotificationStackScrollLayoutController.setTranslationX(translation);
         mQsFrame.setTranslationX(translation);
         int size = mVerticalTranslationListener.size();
         for (int i = 0; i < size; i++) {
@@ -2669,13 +2709,14 @@
 
     protected void updateExpandedHeight(float expandedHeight) {
         if (mTracking) {
-            mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
+            mNotificationStackScrollLayoutController
+                    .setExpandingVelocity(getCurrentExpandVelocity());
         }
         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
             // The expandedHeight is always the full panel Height when bypassing
             expandedHeight = getMaxPanelHeightNonBypass();
         }
-        mNotificationStackScroller.setExpandedHeight(expandedHeight);
+        mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
         updateKeyguardBottomAreaAlpha();
         updateBigClockAlpha();
         updateStatusBarIcons();
@@ -2835,7 +2876,7 @@
                             mHeightListener.onQsHeightChanged();
                         }
                     });
-            mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
+            mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView());
             if (mQs instanceof QSFragment) {
                 mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
             }
@@ -2859,7 +2900,7 @@
         if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
             mAffordanceHelper.reset(false /* animate */);
         }
-        mNotificationStackScroller.setAnimationsEnabled(!disabled);
+        mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled);
     }
 
     /**
@@ -2873,7 +2914,7 @@
         if (dozing == mDozing) return;
         mView.setDozing(dozing);
         mDozing = dozing;
-        mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation);
+        mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation);
         mKeyguardBottomArea.setDozing(mDozing, animate);
 
         if (dozing) {
@@ -2901,7 +2942,7 @@
         if (!mPulsing && !mDozing) {
             mAnimateNextPositionUpdate = false;
         }
-        mNotificationStackScroller.setPulsing(pulsing, animatePulse);
+        mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
         mKeyguardStatusView.setPulsing(pulsing);
     }
 
@@ -3006,7 +3047,7 @@
     }
 
     public boolean hasActiveClearableNotifications() {
-        return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
+        return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL);
     }
 
     private void updateShowEmptyShadeView() {
@@ -3017,54 +3058,56 @@
     }
 
     public RemoteInputController.Delegate createRemoteInputDelegate() {
-        return mNotificationStackScroller.createDelegate();
+        return mNotificationStackScrollLayoutController.createDelegate();
     }
 
     void updateNotificationViews(String reason) {
-        mNotificationStackScroller.updateSectionBoundaries(reason);
-        mNotificationStackScroller.updateSpeedBumpIndex();
-        mNotificationStackScroller.updateFooter();
+        mNotificationStackScrollLayoutController.updateSectionBoundaries(reason);
+        mNotificationStackScrollLayoutController.updateSpeedBumpIndex();
+        mNotificationStackScrollLayoutController.updateFooter();
         updateShowEmptyShadeView();
-        mNotificationStackScroller.updateIconAreaViews();
+        mNotificationStackScrollLayoutController.updateIconAreaViews();
     }
 
     public void onUpdateRowStates() {
-        mNotificationStackScroller.onUpdateRowStates();
+        mNotificationStackScrollLayoutController.onUpdateRowStates();
     }
 
     public boolean hasPulsingNotifications() {
-        return mNotificationStackScroller.hasPulsingNotifications();
+        return mNotificationStackScrollLayoutController.hasPulsingNotifications();
     }
 
     public ActivatableNotificationView getActivatedChild() {
-        return mNotificationStackScroller.getActivatedChild();
+        return mNotificationStackScrollLayoutController.getActivatedChild();
     }
 
     public void setActivatedChild(ActivatableNotificationView o) {
-        mNotificationStackScroller.setActivatedChild(o);
+        mNotificationStackScrollLayoutController.setActivatedChild(o);
     }
 
     public void runAfterAnimationFinished(Runnable r) {
-        mNotificationStackScroller.runAfterAnimationFinished(r);
+        mNotificationStackScrollLayoutController.runAfterAnimationFinished(r);
     }
 
     public void setScrollingEnabled(boolean b) {
-        mNotificationStackScroller.setScrollingEnabled(b);
+        mNotificationStackScrollLayoutController.setScrollingEnabled(b);
     }
 
     public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
-            NotificationShelf notificationShelf,
+            NotificationShelfController notificationShelfController,
             NotificationIconAreaController notificationIconAreaController,
             ScrimController scrimController) {
         setStatusBar(statusBar);
         setGroupManager(mGroupManager);
-        mNotificationStackScroller.setNotificationPanelController(this);
-        mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
-        mNotificationStackScroller.setStatusBar(statusBar);
-        mNotificationStackScroller.setGroupManager(groupManager);
-        mNotificationStackScroller.setShelf(notificationShelf);
-        mNotificationStackScroller.setScrimController(scrimController);
+        mNotificationStackScrollLayoutController.setNotificationPanelController(this);
+        mNotificationStackScrollLayoutController
+                .setIconAreaController(notificationIconAreaController);
+        mNotificationStackScrollLayoutController.setStatusBar(statusBar);
+        mNotificationStackScrollLayoutController.setGroupManager(groupManager);
+        mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
+        mNotificationStackScrollLayoutController.setScrimController(scrimController);
         updateShowEmptyShadeView();
+        mNotificationShelfController = notificationShelfController;
     }
 
     public void showTransientIndication(int id) {
@@ -3228,7 +3271,8 @@
             if (needsAnimation && mInterpolatedDarkAmount == 0) {
                 mAnimateNextPositionUpdate = true;
             }
-            ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
+            ExpandableView firstChildNotGone =
+                    mNotificationStackScrollLayoutController.getFirstChildNotGone();
             ExpandableNotificationRow
                     firstRow =
                     firstChildNotGone instanceof ExpandableNotificationRow
@@ -3454,13 +3498,13 @@
     private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
         @Override
         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
-            mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode);
+            mNotificationStackScrollLayoutController.setInHeadsUpPinnedMode(inPinnedMode);
             if (inPinnedMode) {
                 mHeadsUpExistenceChangedRunnable.run();
                 updateNotificationTranslucency();
             } else {
                 setHeadsUpAnimatingAway(true);
-                mNotificationStackScroller.runAfterAnimationFinished(
+                mNotificationStackScrollLayoutController.runAfterAnimationFinished(
                         mHeadsUpExistenceChangedRunnable);
             }
             updateGestureExclusionRect();
@@ -3472,8 +3516,8 @@
         @Override
         public void onHeadsUpPinned(NotificationEntry entry) {
             if (!isOnKeyguard()) {
-                mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
-                        true);
+                mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
+                        entry.getHeadsUpAnimationView(), true);
             }
         }
 
@@ -3485,7 +3529,7 @@
             // notification
             // will stick to the top without any interaction.
             if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
-                mNotificationStackScroller.generateHeadsUpAnimation(
+                mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
                         entry.getHeadsUpAnimationView(), false);
                 entry.setHeadsUpIsVisible();
             }
@@ -3493,7 +3537,7 @@
 
         @Override
         public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-            mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
+            mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, isHeadsUp);
         }
     }
 
@@ -3508,7 +3552,7 @@
             if (mAccessibilityManager.isEnabled()) {
                 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
             }
-            mNotificationStackScroller.setMaxTopPadding(
+            mNotificationStackScrollLayoutController.setMaxTopPadding(
                     mQsMaxExpansionHeight + mQsNotificationTopPadding);
         }
     }
@@ -3570,7 +3614,7 @@
             } else if (oldState == StatusBarState.SHADE_LOCKED
                     && statusBarState == StatusBarState.KEYGUARD) {
                 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                mNotificationStackScroller.resetScrollPosition();
+                mNotificationStackScrollLayoutController.resetScrollPosition();
                 // Only animate header if the header is visible. If not, it will partially
                 // animate out
                 // the top of QS
@@ -3646,7 +3690,7 @@
                 int oldTop, int oldRight, int oldBottom) {
             DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
             super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
-            setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth());
+            setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
 
             // Update Clock Pivot
             mKeyguardStatusView.setPivotX(mView.getWidth() / 2);
@@ -3662,7 +3706,7 @@
                     mQsExpansionHeight = mQsMinExpansionHeight;
                 }
                 mQsMaxExpansionHeight = mQs.getDesiredHeight();
-                mNotificationStackScroller.setMaxTopPadding(
+                mNotificationStackScrollLayoutController.setMaxTopPadding(
                         mQsMaxExpansionHeight + mQsNotificationTopPadding);
             }
             positionClockAndNotifications();
@@ -3724,7 +3768,7 @@
                     0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p);
             p.setColor(Color.CYAN);
             canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
-                    mNotificationStackScroller.getTopPadding(), p);
+                    mNotificationStackScrollLayoutController.getTopPadding(), p);
             p.setColor(Color.GRAY);
             canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
                     mClockPositionResult.clockY, p);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 8cb54ee..e42c3dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -284,13 +284,22 @@
                 mResources.getString(R.string.accessibility_data_saver_on));
         mIconController.setIconVisibility(mSlotDataSaver, false);
 
+
         // privacy items
+        String microphoneString = mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId());
+        String microphoneDesc = mResources.getString(
+                R.string.ongoing_privacy_chip_content_multiple_apps, microphoneString);
         mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
-                mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()));
+                microphoneDesc);
         mIconController.setIconVisibility(mSlotMicrophone, false);
+
+        String cameraString = mResources.getString(PrivacyType.TYPE_CAMERA.getNameId());
+        String cameraDesc = mResources.getString(
+                R.string.ongoing_privacy_chip_content_multiple_apps, cameraString);
         mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
-                mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()));
+                cameraDesc);
         mIconController.setIconVisibility(mSlotCamera, false);
+
         mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                 mResources.getString(R.string.accessibility_location_active));
         mIconController.setIconVisibility(mSlotLocation, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index eb62628..6cd33c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -179,7 +179,6 @@
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -189,7 +188,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.ScrimView;
@@ -210,6 +209,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -1029,7 +1029,7 @@
                         mStatusBarStateController);
         mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController);
         inflateShelf();
-        mNotificationIconAreaController.setupShelf(mNotificationShelf);
+        mNotificationIconAreaController.setupShelf(mNotificationShelfController);
         mNotificationPanelViewController.setOnReinflationListener(
                 mNotificationIconAreaController::initAodIcons);
         mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
@@ -1076,7 +1076,7 @@
                     // TODO (b/136993073) Separate notification shade and status bar
                     mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                             mNotificationIconAreaController, mHeadsUpManager,
-                            mNotificationShadeWindowView,
+                            mStackScroller.getController(),
                             mStatusBarStateController, mKeyguardBypassController,
                             mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
                             mNotificationPanelViewController, mStatusBarView);
@@ -1147,7 +1147,8 @@
         });
         mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
 
-        mNotificationPanelViewController.initDependencies(this, mGroupManager, mNotificationShelf,
+        mNotificationPanelViewController.initDependencies(this, mGroupManager,
+                mNotificationShelfController,
                 mNotificationIconAreaController, mScrimController);
 
         BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
@@ -1310,7 +1311,7 @@
                 this /* statusBar */, mShadeController, mCommandQueue, mInitController,
                 mNotificationInterruptStateProvider);
 
-        mNotificationShelf.setOnActivatedListener(mPresenter);
+        mNotificationShelfController.setOnActivatedListener(mPresenter);
         mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
 
         mNotificationActivityStarter =
@@ -1386,8 +1387,9 @@
     }
 
     private void inflateShelf() {
-        mNotificationShelf = mSuperStatusBarViewFactory.getNotificationShelf(mStackScroller);
-        mNotificationShelf.setOnClickListener(mGoToLockedShadeListener);
+        mNotificationShelfController = mSuperStatusBarViewFactory
+                .getNotificationShelfController(mStackScroller);
+        mNotificationShelfController.setOnClickListener(mGoToLockedShadeListener);
     }
 
     @Override
@@ -4049,7 +4051,7 @@
     protected IStatusBarService mBarService;
 
     // all notifications
-    protected ViewGroup mStackScroller;
+    protected NotificationStackScrollLayout mStackScroller;
 
     private final NotificationGroupManager mGroupManager;
 
@@ -4085,8 +4087,7 @@
 
     private final Optional<Recents> mRecentsOptional;
 
-    protected NotificationShelf mNotificationShelf;
-    protected EmptyShadeView mEmptyShadeView;
+    protected NotificationShelfController mNotificationShelfController;
 
     private final Lazy<AssistManager> mAssistManagerLazy;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 72395e6..ac69d9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -244,9 +244,10 @@
 
     @Override
     public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
+            boolean appRequestedAuth,
             NotificationRemoteInputManager.ClickHandler defaultHandler) {
         final boolean isActivity = pendingIntent.isActivity();
-        if (isActivity) {
+        if (isActivity || appRequestedAuth) {
             mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent);
             final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
                     pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 551b7b4..d16bedc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -23,6 +23,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 
+import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardMessageArea;
 import com.android.keyguard.KeyguardSliceView;
 import com.android.systemui.dagger.SystemUIRootComponent;
@@ -126,11 +127,6 @@
         NotificationStackScrollLayout createNotificationStackScrollLayout();
 
         /**
-         * Creates the Shelf.
-         */
-        NotificationShelf creatNotificationShelf();
-
-        /**
          * Creates the KeyguardSliceView.
          */
         KeyguardSliceView createKeyguardSliceView();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index fbc8e9d..ac567e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.view.Display;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IWindowMagnificationConnection;
@@ -47,6 +48,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class IWindowMagnificationConnectionTest extends SysuiTestCase {
 
     private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@@ -57,7 +59,7 @@
     @Mock
     private IWindowMagnificationConnectionCallback mConnectionCallback;
     @Mock
-    private WindowMagnificationController mWindowMagnificationController;
+    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
     @Mock
     private ModeSwitchesController mModeSwitchesController;
     private IWindowMagnificationConnection mIWindowMagnificationConnection;
@@ -74,7 +76,8 @@
                 any(IWindowMagnificationConnection.class));
         mWindowMagnification = new WindowMagnification(getContext(),
                 getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController);
-        mWindowMagnification.mWindowMagnificationController = mWindowMagnificationController;
+        mWindowMagnification.mWindowMagnificationAnimationController =
+                mWindowMagnificationAnimationController;
         mWindowMagnification.requestWindowMagnificationConnection(true);
         assertNotNull(mIWindowMagnificationConnection);
         mIWindowMagnificationConnection.setConnectionCallback(mConnectionCallback);
@@ -86,7 +89,7 @@
                 Float.NaN);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).enableWindowMagnification(3.0f, Float.NaN,
+        verify(mWindowMagnificationAnimationController).enableWindowMagnification(3.0f, Float.NaN,
                 Float.NaN);
     }
 
@@ -99,7 +102,7 @@
         mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).deleteWindowMagnification();
+        verify(mWindowMagnificationAnimationController).deleteWindowMagnification();
     }
 
     @Test
@@ -107,7 +110,7 @@
         mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).setScale(3.0f);
+        verify(mWindowMagnificationAnimationController).setScale(3.0f);
     }
 
     @Test
@@ -115,7 +118,7 @@
         mIWindowMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f);
+        verify(mWindowMagnificationAnimationController).moveWindowMagnifier(100f, 200f);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
new file mode 100644
index 0000000..b7c198e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -0,0 +1,363 @@
+/*
+ * 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.systemui.accessibility;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.animation.ValueAnimator;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.SurfaceControl;
+import android.view.animation.AccelerateInterpolator;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+
+@MediumTest
+@RunWith(AndroidTestingRunner.class)
+public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
+
+    private static final float DEFAULT_SCALE = 3.0f;
+    private static final float DEFAULT_CENTER_X = 400.0f;
+    private static final float DEFAULT_CENTER_Y = 500.0f;
+    // The duration couldn't too short, otherwise the ValueAnimator won't work in expectation.
+    private static final long ANIMATION_DURATION_MS = 200;
+
+    private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0);
+    private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0);
+    private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0);
+    private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class);
+    private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class);
+    private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class);
+
+    @Mock
+    Handler mHandler;
+    @Mock
+    SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+    @Mock
+    WindowMagnifierCallback mWindowMagnifierCallback;
+
+    private SpyWindowMagnificationController mController;
+    private WindowMagnificationController mSpyController;
+    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
+    private Instrumentation mInstrumentation;
+    private long mWaitingAnimationPeriod;
+    private long mWaitIntermediateAnimationPeriod;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mWaitingAnimationPeriod = ANIMATION_DURATION_MS + 50;
+        mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
+        mController = new SpyWindowMagnificationController(mContext, mHandler,
+                mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(),
+                mWindowMagnifierCallback);
+        mSpyController = mController.getSpyController();
+        mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+                mContext, mController, newValueAnimator());
+    }
+
+    @Test
+    public void enableWindowMagnification_disabled_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(
+                mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verifyStartValue(mScaleCaptor, 1.0f);
+        verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
+        verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
+        verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+    }
+
+    @Test
+    public void enableWindowMagnification_enabling_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+        final float targetScale = DEFAULT_SCALE + 1.0f;
+        final float targetCenterX = DEFAULT_CENTER_X + 100;
+        final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+        mInstrumentation.runOnMainSync(() -> {
+            Mockito.reset(mSpyController);
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+        });
+
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verifyStartValue(mScaleCaptor, mCurrentScale.get());
+        verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
+        verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+    }
+
+    @Test
+    public void enableWindowMagnification_disabling_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+        final float targetScale = DEFAULT_SCALE + 1.0f;
+        final float targetCenterX = DEFAULT_CENTER_X + 100;
+        final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                            targetCenterX, targetCenterY);
+                    mCurrentScale.set(mController.getScale());
+                    mCurrentCenterX.set(mController.getCenterX());
+                    mCurrentCenterY.set(mController.getCenterY());
+                });
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(
+                mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        //Animating in reverse, so we only check if the start values are greater than current.
+        assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
+        assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
+        assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get());
+        assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f);
+        assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
+        assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+    }
+
+    @Test
+    public void enableWindowMagnificationWithSameScale_doNothing() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+                anyFloat());
+    }
+
+    @Test
+    public void setScale_enabled_expectedScale() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1));
+
+        verify(mSpyController).setScale(DEFAULT_SCALE + 1);
+        verifyFinalSpec(DEFAULT_SCALE + 1, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+    }
+
+    @Test
+    public void deleteWindowMagnification_enabled_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verify(mSpyController).deleteWindowMagnification();
+        verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
+        verifyStartValue(mCenterXCaptor, Float.NaN);
+        verifyStartValue(mCenterYCaptor, Float.NaN);
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+    }
+
+    @Test
+    public void deleteWindowMagnification_disabled_doNothing() {
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        Mockito.verifyNoMoreInteractions(mSpyController);
+    }
+
+    @Test
+    public void deleteWindowMagnification_enabling_checkStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+
+        //It just reverse the animation, so we don't need to wait the whole duration.
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.deleteWindowMagnification();
+                    mCurrentScale.set(mController.getScale());
+                    mCurrentCenterX.set(mController.getCenterX());
+                    mCurrentCenterY.set(mController.getCenterY());
+                });
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verify(mSpyController).deleteWindowMagnification();
+
+        //The animation is in verse, so we only check the start values should no be greater than
+        // the current one.
+        assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get());
+        assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
+        verifyStartValue(mCenterXCaptor, Float.NaN);
+        verifyStartValue(mCenterYCaptor, Float.NaN);
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+    }
+
+    @Test
+    public void deleteWindowMagnification_disabling_checkStartAndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verify(mSpyController).deleteWindowMagnification();
+        assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+    }
+
+    @Test
+    public void moveWindowMagnifier_enabled() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f));
+
+        verify(mSpyController).moveWindowMagnifier(100f, 200f);
+        verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f);
+    }
+
+    @Test
+    public void onConfigurationChanged_passThrough() {
+        mWindowMagnificationAnimationController.onConfigurationChanged(100);
+
+        verify(mSpyController).onConfigurationChanged(100);
+    }
+    private void verifyFinalSpec(float expectedScale, float expectedCenterX,
+            float expectedCenterY) {
+        assertEquals(expectedScale, mController.getScale(), 0f);
+        assertEquals(expectedCenterX, mController.getCenterX(), 0f);
+        assertEquals(expectedCenterY, mController.getCenterY(), 0f);
+    }
+
+    private void enableWindowMagnificationAndWaitAnimating(long duration) {
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+                            DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+                });
+        SystemClock.sleep(duration);
+    }
+
+    private void deleteWindowMagnificationAndWaitAnimating(long duration) {
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    resetMockObjects();
+                    mWindowMagnificationAnimationController.deleteWindowMagnification();
+                });
+        SystemClock.sleep(duration);
+    }
+
+    private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
+        assertEquals(startValue, captor.getAllValues().get(0), 0f);
+    }
+
+    private void resetMockObjects() {
+        Mockito.reset(mSpyController);
+    }
+
+    /**
+     * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it
+     * directly.
+     */
+    private static class SpyWindowMagnificationController extends WindowMagnificationController {
+        private WindowMagnificationController mSpyController;
+
+        SpyWindowMagnificationController(Context context, Handler handler,
+                SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
+                MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
+                WindowMagnifierCallback callback) {
+            super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction,
+                    callback);
+            mSpyController = Mockito.mock(WindowMagnificationController.class);
+        }
+
+        WindowMagnificationController getSpyController() {
+            return mSpyController;
+        }
+
+        @Override
+        void enableWindowMagnification(float scale, float centerX, float centerY) {
+            super.enableWindowMagnification(scale, centerX, centerY);
+            mSpyController.enableWindowMagnification(scale, centerX, centerY);
+        }
+
+        @Override
+        void deleteWindowMagnification() {
+            super.deleteWindowMagnification();
+            mSpyController.deleteWindowMagnification();
+        }
+
+        @Override
+        void moveWindowMagnifier(float offsetX, float offsetY) {
+            super.moveWindowMagnifier(offsetX, offsetX);
+            mSpyController.moveWindowMagnifier(offsetX, offsetY);
+        }
+
+        @Override
+        void setScale(float scale) {
+            super.setScale(scale);
+            mSpyController.setScale(scale);
+        }
+
+        @Override
+        void onConfigurationChanged(int configDiff) {
+            super.onConfigurationChanged(configDiff);
+            mSpyController.onConfigurationChanged(configDiff);
+        }
+
+    }
+
+    private static ValueAnimator newValueAnimator() {
+        final ValueAnimator valueAnimator = new ValueAnimator();
+        valueAnimator.setDuration(ANIMATION_DURATION_MS);
+        valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
+        valueAnimator.setFloatValues(0.0f, 1.0f);
+        return valueAnimator;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 2007fbb..f1f394e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.view.Choreographer.FrameCallback;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
@@ -26,8 +27,12 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
+import android.view.Display;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 import androidx.test.InstrumentationRegistry;
@@ -41,6 +46,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
@@ -57,12 +63,14 @@
     WindowMagnifierCallback mWindowMagnifierCallback;
     @Mock
     SurfaceControl.Transaction mTransaction;
+    private Context mContext;
     private WindowMagnificationController mWindowMagnificationController;
     private Instrumentation mInstrumentation;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext  = Mockito.spy(getContext());
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         doAnswer(invocation -> {
             FrameCallback callback = invocation.getArgument(0);
@@ -73,8 +81,7 @@
         when(mTransaction.remove(any())).thenReturn(mTransaction);
         when(mTransaction.setGeometry(any(), any(), any(),
                 anyInt())).thenReturn(mTransaction);
-
-        mWindowMagnificationController = new WindowMagnificationController(getContext(),
+        mWindowMagnificationController = new WindowMagnificationController(mContext,
                 mHandler, mSfVsyncFrameProvider,
                 mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
         verify(mMirrorWindowControl).setWindowDelegate(
@@ -83,9 +90,8 @@
 
     @After
     public void tearDown() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.deleteWindowMagnification();
-        });
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.deleteWindowMagnification());
     }
 
     @Test
@@ -121,4 +127,27 @@
 
         verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
     }
+
+    @Test
+    public void setScale_enabled_expectedValue() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                        Float.NaN));
+
+        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
+
+        assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
+    }
+
+    @Test
+    public void onConfigurationChanged_disabled_withoutException() {
+        Display display = Mockito.spy(mContext.getDisplay());
+        when(display.getRotation()).thenReturn(Surface.ROTATION_90);
+        when(mContext.getDisplay()).thenReturn(display);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+        });
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 4136013..936558b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.view.Display;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IWindowMagnificationConnection;
@@ -45,6 +46,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class WindowMagnificationTest extends SysuiTestCase {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 4fdc06e..8f082c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -27,6 +27,9 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -34,6 +37,8 @@
 
 import android.app.AppOpsManager;
 import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.media.AudioRecordingConfiguration;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
@@ -47,9 +52,11 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Collections;
 import java.util.List;
 
 @SmallTest
@@ -73,6 +80,12 @@
     private PermissionFlagsCache mFlagsCache;
     @Mock
     private PackageManager mPackageManager;
+    @Mock(stubOnly = true)
+    private AudioManager mAudioManager;
+    @Mock(stubOnly = true)
+    private AudioManager.AudioRecordingCallback mRecordingCallback;
+    @Mock(stubOnly = true)
+    private AudioRecordingConfiguration mPausedMockRecording;
 
     private AppOpsControllerImpl mController;
     private TestableLooper mTestableLooper;
@@ -94,11 +107,20 @@
         when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
                 eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
 
+        doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0))
+                .when(mAudioManager).registerAudioRecordingCallback(any(), any());
+        when(mPausedMockRecording.getClientUid()).thenReturn(TEST_UID);
+        when(mPausedMockRecording.isClientSilenced()).thenReturn(true);
+
+        when(mAudioManager.getActiveRecordingConfigurations())
+                .thenReturn(List.of(mPausedMockRecording));
+
         mController = new AppOpsControllerImpl(
                 mContext,
                 mTestableLooper.getLooper(),
                 mDumpManager,
-                mFlagsCache
+                mFlagsCache,
+                mAudioManager
         );
     }
 
@@ -363,6 +385,89 @@
                 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
     }
 
+    @Test
+    public void testPausedRecordingIsRetrievedOnCreation() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mTestableLooper.processAllMessages();
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never())
+                .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean());
+    }
+
+    @Test
+    public void testPausedRecordingFilteredOut() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mTestableLooper.processAllMessages();
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mController.getActiveAppOps().isEmpty());
+    }
+
+    @Test
+    public void testOnlyRecordAudioPaused() {
+        mController.addCallback(new int[]{
+                AppOpsManager.OP_RECORD_AUDIO,
+                AppOpsManager.OP_CAMERA
+        }, mCallback);
+        mTestableLooper.processAllMessages();
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true);
+        List<AppOpItem> list = mController.getActiveAppOps();
+
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+    }
+
+    @Test
+    public void testUnpausedRecordingSentActive() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mTestableLooper.processAllMessages();
+        mRecordingCallback.onRecordingConfigChanged(Collections.emptyList());
+
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+    }
+
+    @Test
+    public void testAudioPausedSentInactive() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+
+        AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class);
+        when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER);
+        when(mockARC.isClientSilenced()).thenReturn(true);
+
+        mRecordingCallback.onRecordingConfigChanged(List.of(mockARC));
+        mTestableLooper.processAllMessages();
+
+        InOrder inOrder = inOrder(mCallback);
+        inOrder.verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        inOrder.verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
+    }
+
     private class TestHandler extends AppOpsControllerImpl.H {
         TestHandler(Looper looper) {
             mController.super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index b758953..1538a32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -60,6 +60,7 @@
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
@@ -70,7 +71,9 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -79,8 +82,10 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -90,6 +95,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.util.InjectionInflationController;
 
 import com.google.common.collect.ImmutableList;
 
@@ -174,6 +180,8 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
+    private NotificationShelfComponent mNotificationShelfComponent;
+    @Mock
     private NotifPipeline mNotifPipeline;
     @Mock
     private FeatureFlags mFeatureFlagsOldPipeline;
@@ -185,11 +193,14 @@
     private IStatusBarService mStatusBarService;
     @Mock
     private LauncherApps mLauncherApps;
+    @Mock private LockscreenLockIconController mLockIconController;
 
     private BubbleData mBubbleData;
 
     private TestableLooper mTestableLooper;
 
+    private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -199,6 +210,23 @@
         mContext.addMockSystemService(FaceManager.class, mFaceManager);
         when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
 
+        mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext,
+                new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+                new NotificationShelfComponent.Builder() {
+                    @Override
+                    public NotificationShelfComponent.Builder notificationShelf(
+                            NotificationShelf view) {
+                        return this;
+                    }
+
+                    @Override
+                    public NotificationShelfComponent build() {
+                        return mNotificationShelfComponent;
+                    }
+                },
+                mLockIconController);
+
+        // Bubbles get added to status bar window view
         mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 43bf191..0e7cb79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -58,6 +58,7 @@
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
@@ -66,7 +67,9 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
@@ -75,7 +78,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
@@ -88,6 +91,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.util.InjectionInflationController;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -168,7 +172,7 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
-    private NotificationRowComponent mNotificationRowComponent;
+    private NotificationShelfComponent mNotificationShelfComponent;
     @Mock
     private NotifPipeline mNotifPipeline;
     @Mock
@@ -185,6 +189,7 @@
     private BubbleData mBubbleData;
 
     private TestableLooper mTestableLooper;
+    private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
 
     @Before
     public void setUp() throws Exception {
@@ -195,6 +200,22 @@
         mContext.addMockSystemService(FaceManager.class, mFaceManager);
         when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
 
+        mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext,
+                new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+                new NotificationShelfComponent.Builder() {
+                    @Override
+                    public NotificationShelfComponent.Builder notificationShelf(
+                            NotificationShelf view) {
+                        return this;
+                    }
+
+                    @Override
+                    public NotificationShelfComponent build() {
+                        return mNotificationShelfComponent;
+                    }
+                },
+                mLockIconController);
+
         // Bubbles get added to status bar window view
         mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 3789e6e..a4ebe1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -13,6 +13,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.SbnBuilder
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.eq
@@ -58,6 +59,7 @@
     @Mock lateinit var mediaTimeoutListener: MediaTimeoutListener
     @Mock lateinit var mediaResumeListener: MediaResumeListener
     @Mock lateinit var pendingIntent: PendingIntent
+    @Mock lateinit var activityStarter: ActivityStarter
     @JvmField @Rule val mockito = MockitoJUnit.rule()
     lateinit var mediaDataManager: MediaDataManager
     lateinit var mediaNotification: StatusBarNotification
@@ -68,8 +70,8 @@
         backgroundExecutor = FakeExecutor(FakeSystemClock())
         mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor,
                 mediaControllerFactory, broadcastDispatcher, dumpManager,
-                mediaTimeoutListener, mediaResumeListener, useMediaResumption = true,
-                useQsMediaPlayer = true)
+                mediaTimeoutListener, mediaResumeListener, activityStarter,
+                useMediaResumption = true, useQsMediaPlayer = true)
         session = MediaSession(context, "MediaDataManagerTestSession")
         mediaNotification = SbnBuilder().run {
             setPkg(PACKAGE_NAME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index bdb7166..c8e1a74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -78,7 +78,7 @@
 
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 public class QSTileHostTest extends SysuiTestCase {
 
     private static String MOCK_STATE_STRING = "MockState";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index c2579dd..3aa40de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -53,7 +53,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 class CustomTileTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index cccb65d..61a0d6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -244,6 +244,8 @@
         assertNotEquals(DESTROYED, mTile.getLifecycle().getCurrentState());
         mTile.handleDestroy();
 
+        mTestableLooper.processAllMessages();
+
         assertEquals(DESTROYED, mTile.getLifecycle().getCurrentState());
     }
 
@@ -298,6 +300,25 @@
         assertNotEquals(DESTROYED, mTile.getLifecycle().getCurrentState());
     }
 
+    @Test
+    public void testRefreshStateAfterDestroyedDoesNotCrash() {
+        mTile.destroy();
+        mTile.refreshState();
+
+        mTestableLooper.processAllMessages();
+    }
+
+    @Test
+    public void testSetListeningAfterDestroyedDoesNotCrash() {
+        Object o = new Object();
+        mTile.destroy();
+
+        mTile.setListening(o, true);
+        mTile.setListening(o, false);
+
+        mTestableLooper.processAllMessages();
+    }
+
     private void assertEvent(UiEventLogger.UiEventEnum eventType,
             UiEventLoggerFake.FakeUiEvent fakeEvent) {
         assertEquals(eventType.getId(), fakeEvent.eventId);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index f70106a..2006a75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -38,7 +38,7 @@
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class BatterySaverTileTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 8ece622..5d14898 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -58,7 +58,7 @@
 
 
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class CastTileTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index ddac2ec..fec4677 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -64,6 +64,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -197,7 +198,10 @@
         mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class));
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
 
+        NotificationShelfController notificationShelfController =
+                mock(NotificationShelfController.class);
         NotificationShelf notificationShelf = mock(NotificationShelf.class);
+        when(notificationShelfController.getView()).thenReturn(notificationShelf);
         when(mNotificationSectionsManager.createSectionsForBuckets()).thenReturn(
                 new NotificationSection[]{
                         mNotificationSection
@@ -208,7 +212,7 @@
         // holds a copy of the CUT's instances of these KeyguardBypassController, so they still
         // refer to the CUT's member variables, not the spy's member variables.
         mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null,
-                true /* allowLongPress */, mNotificationRoundnessManager,
+                mNotificationRoundnessManager,
                 mock(DynamicPrivacyController.class),
                 mock(SysuiStatusBarStateController.class),
                 mHeadsUpManager,
@@ -230,7 +234,7 @@
         verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture());
         mUserChangedListener = userChangedCaptor.getValue();
         mStackScroller = spy(mStackScrollerInternal);
-        mStackScroller.setShelf(notificationShelf);
+        mStackScroller.setShelfController(notificationShelfController);
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setScrimController(mock(ScrimController.class));
         mStackScroller.setGroupManager(mGroupManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index e546dff..8dea84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -38,7 +38,7 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Assert;
@@ -51,8 +51,8 @@
 @RunWithLooper
 public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
 
-    private final NotificationStackScrollLayout mStackScroller =
-            mock(NotificationStackScrollLayout.class);
+    private final NotificationStackScrollLayoutController mStackScrollerController =
+            mock(NotificationStackScrollLayoutController.class);
     private final NotificationPanelViewController mPanelView =
             mock(NotificationPanelViewController.class);
     private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
@@ -93,9 +93,9 @@
                 mWakeUpCoordinator,
                 mKeyguardStateController,
                 mCommandQueue,
-                mHeadsUpStatusBarView,
-                mStackScroller,
+                mStackScrollerController,
                 mPanelView,
+                mHeadsUpStatusBarView,
                 new View(mContext),
                 mOperatorNameView,
                 new View(mContext));
@@ -172,9 +172,9 @@
                 mWakeUpCoordinator,
                 mKeyguardStateController,
                 mCommandQueue,
-                mHeadsUpStatusBarView,
-                mStackScroller,
+                mStackScrollerController,
                 mPanelView,
+                mHeadsUpStatusBarView,
                 new View(mContext),
                 new View(mContext),
                 new View(mContext));
@@ -193,14 +193,14 @@
         reset(mHeadsUpManager);
         reset(mDarkIconDispatcher);
         reset(mPanelView);
-        reset(mStackScroller);
+        reset(mStackScrollerController);
         mHeadsUpAppearanceController.destroy();
         verify(mHeadsUpManager).removeListener(any());
         verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
         verify(mPanelView).removeVerticalTranslationListener(any());
         verify(mPanelView).removeTrackingHeadsUpListener(any());
         verify(mPanelView).setHeadsUpAppearanceController(any());
-        verify(mStackScroller).removeOnExpandedHeightChangedListener(any());
-        verify(mStackScroller).removeOnLayoutChangeListener(any());
+        verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any());
+        verify(mStackScrollerController).removeOnLayoutChangeListener(any());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index c7434f6..f66fd56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -63,7 +63,7 @@
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -72,8 +72,10 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -115,7 +117,7 @@
     @Mock
     private HeadsUpManagerPhone mHeadsUpManager;
     @Mock
-    private NotificationShelf mNotificationShelf;
+    private NotificationShelfController mNotificationShelfController;
     @Mock
     private NotificationGroupManager mGroupManager;
     @Mock
@@ -186,8 +188,6 @@
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock
     private KeyguardClockSwitchController mKeyguardClockSwitchController;
-    private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
-
     private NotificationPanelViewController mNotificationPanelViewController;
     private View.AccessibilityDelegate mAccessibiltyDelegate;
 
@@ -212,7 +212,8 @@
         when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
         when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer);
         when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
-        mFlingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(mDisplayMetrics);
+        FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
+                mDisplayMetrics);
 
         doAnswer((Answer<Void>) invocation -> {
             mTouchHandler = invocation.getArgument(0);
@@ -232,6 +233,11 @@
                 mock(NotificationRoundnessManager.class),
                 mStatusBarStateController,
                 new FalsingManagerFake());
+        NotificationStackScrollLayoutController notificationStackScrollLayoutController =
+                new NotificationStackScrollLayoutController(
+                        true /* allowLongPress */,
+                        mock(NotificationGutsManager.class)
+                );
         mNotificationPanelViewController = new NotificationPanelViewController(mView,
                 mInjectionInflationController,
                 coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
@@ -241,19 +247,19 @@
                 mDozeParameters, mCommandQueue, mVibratorHelper,
                 mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
                 mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController,
-                mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
+                flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
                 mConversationNotificationManager, mMediaHiearchyManager,
                 mBiometricUnlockController, mStatusBarKeyguardViewManager,
-                () -> mKeyguardClockSwitchController);
+                () -> mKeyguardClockSwitchController,
+                notificationStackScrollLayoutController);
         mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager,
-                mNotificationShelf, mNotificationAreaController, mScrimController);
+                mNotificationShelfController, mNotificationAreaController, mScrimController);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
         mNotificationPanelViewController.setBar(mPanelBar);
 
         ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor =
                 ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
-        verify(mView)
-                .setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
+        verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
         mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue();
     }
 
diff --git a/services/Android.bp b/services/Android.bp
index ef52c2a..b348b91 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -156,14 +156,10 @@
 
 java_library {
     name: "android_system_server_stubs_current",
-    defaults: ["android_stubs_dists_default"],
     srcs: [":services-stubs.sources"],
     installable: false,
     static_libs: ["android_module_lib_stubs_current"],
     sdk_version: "none",
     system_modules: "none",
     java_version: "1.8",
-    dist: {
-        dir: "apistubs/android/system-server",
-    },
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index bd25f2b..3ee5b28 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -25,10 +25,12 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Point;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.view.Display;
 import android.view.MotionEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -90,6 +92,8 @@
 
     private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
     private final int mDisplayId;
+    private final Context mContext;
+    private final Point mTempPoint = new Point();
 
     private final Queue<MotionEvent> mDebugOutputEventHistory;
 
@@ -107,7 +111,7 @@
             Slog.i(LOG_TAG,
                     "WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
         }
-
+        mContext = context;
         mWindowMagnificationMgr = windowMagnificationMgr;
         mDetectShortcutTrigger = detectShortcutTrigger;
         mDisplayId = displayId;
@@ -184,7 +188,14 @@
         if (!mDetectShortcutTrigger) {
             return;
         }
-        toggleMagnification(Float.NaN, Float.NaN);
+        final Point screenSize = mTempPoint;
+        getScreenSize(mTempPoint);
+        toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f);
+    }
+
+    private  void getScreenSize(Point outSize) {
+        final Display display = mContext.getDisplay();
+        display.getRealSize(outSize);
     }
 
     @Override
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 1c31166..a92d334 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
@@ -214,7 +215,13 @@
                     return componentName;
                 }
                 intent.addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
-                return intent.resolveActivity(packageManager);
+                final ActivityInfo ai =
+                        intent.resolveActivityInfo(packageManager, PackageManager.MATCH_INSTANT);
+                if (ai != null) {
+                    return new ComponentName(ai.applicationInfo.packageName, ai.name);
+                }
+
+                return null;
             }
         };
         final LayoutInflater inflater = LayoutInflater.from(context);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e76ec74..addaa65 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -2,38 +2,25 @@
     name: "services.core-sources",
     srcs: ["java/**/*.java"],
     path: "java",
-    visibility: ["//frameworks/base/services"],
-}
-
-java_library {
-    name: "protolog-common",
-    srcs: [
-        "java/com/android/server/protolog/common/**/*.java",
+    visibility: [
+        "//frameworks/base/services",
+        "//frameworks/base/core/java/com/android/internal/protolog",
     ],
-    host_supported: true,
-}
-
-java_library {
-    name: "services.core.wm.protologgroups",
-    srcs: [
-        "java/com/android/server/wm/ProtoLogGroup.java",
-    ],
-    static_libs: ["protolog-common"],
 }
 
 genrule {
     name: "services.core.protologsrc",
     srcs: [
-        ":services.core.wm.protologgroups",
+        ":protolog-groups",
         ":services.core-sources",
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) transform-protolog-calls " +
-      "--protolog-class com.android.server.protolog.common.ProtoLog " +
-      "--protolog-impl-class com.android.server.protolog.ProtoLogImpl " +
-      "--protolog-cache-class 'com.android.server.protolog.ProtoLog$$Cache' " +
-      "--loggroups-class com.android.server.wm.ProtoLogGroup " +
-      "--loggroups-jar $(location :services.core.wm.protologgroups) " +
+      "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+      "--protolog-impl-class com.android.internal.protolog.ProtoLogImpl " +
+      "--protolog-cache-class 'com.android.server.wm.ProtoLogCache' " +
+      "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
+      "--loggroups-jar $(location :protolog-groups) " +
       "--output-srcjar $(out) " +
       "$(locations :services.core-sources)",
     out: ["services.core.protolog.srcjar"],
@@ -42,14 +29,14 @@
 genrule {
     name: "generate-protolog.json",
     srcs: [
-        ":services.core.wm.protologgroups",
+        ":protolog-groups",
         ":services.core-sources",
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) generate-viewer-config " +
-      "--protolog-class com.android.server.protolog.common.ProtoLog " +
-      "--loggroups-class com.android.server.wm.ProtoLogGroup " +
-      "--loggroups-jar $(location :services.core.wm.protologgroups) " +
+      "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+      "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
+      "--loggroups-jar $(location :protolog-groups) " +
       "--viewer-conf $(out) " +
       "$(locations :services.core-sources)",
     out: ["services.core.protolog.json"],
@@ -109,6 +96,7 @@
     ],
 
     static_libs: [
+        "protolog-lib",
         "time_zone_distro",
         "time_zone_distro_installer",
         "android.hardware.authsecret-V1.0-java",
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 59ac09c..32d02fb 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -114,9 +114,6 @@
     private static final VibrationAttributes DEFAULT_ATTRIBUTES =
             new VibrationAttributes.Builder().build();
 
-    // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration.
-    private static final long ASYNC_TIMEOUT_MULTIPLIER = 2;
-
     // A mapping from the intensity adjustment to the scaling to apply, where the intensity
     // adjustment is defined as the delta between the default intensity level and the user selected
     // intensity level. It's important that we apply the scaling on the delta between the two so
@@ -187,8 +184,8 @@
 
     static native int[] vibratorGetSupportedEffects(long controllerPtr);
 
-    static native long vibratorPerformEffect(long effect, long strength, Vibration vibration,
-            boolean withCallback);
+    static native long vibratorPerformEffect(
+            long controllerPtr, long effect, long strength, Vibration vibration);
 
     static native void vibratorPerformComposedEffect(long controllerPtr,
             VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
@@ -898,19 +895,11 @@
         }
     }
 
-    private final Runnable mVibrationEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            onVibrationFinished();
-        }
-    };
-
     @GuardedBy("mLock")
     private void doCancelVibrateLocked() {
         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
         try {
-            mH.removeCallbacks(mVibrationEndRunnable);
             if (mThread != null) {
                 mThread.cancel();
                 mThread = null;
@@ -958,13 +947,11 @@
     private void startVibrationInnerLocked(Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
         try {
-            long timeout = 0;
             mCurrentVibration = vib;
             if (vib.effect instanceof VibrationEffect.OneShot) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
                 doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib);
-                timeout = oneShot.getDuration() * ASYNC_TIMEOUT_MULTIPLIER;
             } else if (vib.effect instanceof VibrationEffect.Waveform) {
                 // mThread better be null here. doCancelVibrate should always be
                 // called before startNextVibrationLocked or startVibrationLocked.
@@ -973,24 +960,13 @@
                 mThread.start();
             } else if (vib.effect instanceof VibrationEffect.Prebaked) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
-                timeout = doVibratorPrebakedEffectLocked(vib);
+                doVibratorPrebakedEffectLocked(vib);
             } else if (vib.effect instanceof  VibrationEffect.Composed) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 doVibratorComposedEffectLocked(vib);
-                // FIXME: We rely on the completion callback here, but I don't think we require that
-                // devices which support composition also support the completion callback. If we
-                // ever get a device that supports the former but not the latter, then we have no
-                // real way of knowing how long a given effect should last.
-                timeout = 10_000;
             } else {
                 Slog.e(TAG, "Unknown vibration type, ignoring");
             }
-            // Post extra runnable to ensure vibration will end even if the HAL or native controller
-            // never triggers the callback.
-            // TODO: Move ASYNC_TIMEOUT_MULTIPLIER here once native controller is fully integrated.
-            if (timeout > 0) {
-                mH.postDelayed(mVibrationEndRunnable, timeout);
-            }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -1354,7 +1330,7 @@
     }
 
     @GuardedBy("mLock")
-    private long doVibratorPrebakedEffectLocked(Vibration vib) {
+    private void doVibratorPrebakedEffectLocked(Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
         try {
             final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
@@ -1364,25 +1340,20 @@
             }
             // Input devices don't support prebaked effect, so skip trying it with them.
             if (!usingInputDeviceVibrators) {
-                long duration = mNativeWrapper.vibratorPerformEffect(prebaked.getId(),
-                        prebaked.getEffectStrength(), vib,
-                        hasCapability(IVibrator.CAP_PERFORM_CALLBACK));
-                long timeout = duration;
-                if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
-                    timeout *= ASYNC_TIMEOUT_MULTIPLIER;
-                }
-                if (timeout > 0) {
+                long duration = mNativeWrapper.vibratorPerformEffect(
+                        prebaked.getId(), prebaked.getEffectStrength(), vib);
+                if (duration > 0) {
                     noteVibratorOnLocked(vib.uid, duration);
-                    return timeout;
+                    return;
                 }
             }
             if (!prebaked.shouldFallback()) {
-                return 0;
+                return;
             }
             VibrationEffect effect = getFallbackEffect(prebaked.getId());
             if (effect == null) {
                 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
-                return 0;
+                return;
             }
             Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
                     vib.opPkg, vib.reason + " (fallback)");
@@ -1390,7 +1361,7 @@
             linkVibration(fallbackVib);
             applyVibrationIntensityScalingLocked(fallbackVib, intensity);
             startVibrationInnerLocked(fallbackVib);
-            return 0;
+            return;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -1789,9 +1760,9 @@
         }
 
         /** Turns vibrator on to perform one of the supported effects. */
-        public long vibratorPerformEffect(long effect, long strength, Vibration vibration,
-                boolean withCallback) {
-            return VibratorService.vibratorPerformEffect(effect, strength, vibration, withCallback);
+        public long vibratorPerformEffect(long effect, long strength, Vibration vibration) {
+            return VibratorService.vibratorPerformEffect(
+                    mNativeControllerPtr, effect, strength, vibration);
         }
 
         /** Turns vibrator on to perform one of the supported composed effects. */
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3eb26de..b83aa4f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -694,12 +694,8 @@
         }
         ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
 
-        if (!r.mAllowWhileInUsePermissionInFgs) {
-            r.mAllowWhileInUsePermissionInFgs =
-                    shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
-                            callingUid, service, r, allowBackgroundActivityStarts);
-        }
-
+        setFgsRestrictionLocked(callingPackage, callingPid,
+                callingUid, service, r, allowBackgroundActivityStarts);
         return cmp;
     }
 
@@ -1369,6 +1365,7 @@
                                     + r.shortInstanceName);
                 }
             }
+
             boolean alreadyStartedOp = false;
             boolean stopProcStatsOp = false;
             if (r.fgRequired) {
@@ -1415,6 +1412,24 @@
                     ignoreForeground = true;
                 }
 
+                if (!ignoreForeground) {
+                    if (!r.mAllowStartForeground) {
+                        if (!r.mLoggedInfoAllowStartForeground) {
+                            Slog.wtf(TAG, "Background started FGS "
+                                    + r.mInfoAllowStartForeground);
+                            r.mLoggedInfoAllowStartForeground = true;
+                        }
+                        if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
+                            Slog.w(TAG,
+                                    "Service.startForeground() not allowed due to "
+                                    + " mAllowStartForeground false: service "
+                                    + r.shortInstanceName);
+                            updateServiceForegroundLocked(r.app, true);
+                            ignoreForeground = true;
+                        }
+                    }
+                }
+
                 // Apps under strict background restrictions simply don't get to have foreground
                 // services, so now that we've enforced the startForegroundService() contract
                 // we only do the machinery of making the service foreground when the app
@@ -2067,12 +2082,7 @@
                 }
             }
 
-            if (!s.mAllowWhileInUsePermissionInFgs) {
-                s.mAllowWhileInUsePermissionInFgs =
-                        shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
-                                callingPid, callingUid,
-                                service, s, false);
-            }
+            setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false);
 
             if (s.app != null) {
                 if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
@@ -4840,21 +4850,48 @@
     }
 
     /**
-     * Should allow while-in-use permissions in foreground service or not.
-     * while-in-use permissions in FGS started from background might be restricted.
+     * There are two FGS restrictions:
+     * In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground
+     *  service or not. while-in-use permissions in FGS started from background might be restricted.
+     * In S, mAllowStartForeground is to allow FGS to startForeground or not. Service started
+     * from background may not become a FGS.
      * @param callingPackage caller app's package name.
      * @param callingUid caller app's uid.
      * @param intent intent to start/bind service.
      * @param r the service to start.
      * @return true if allow, false otherwise.
      */
-    private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
+    private void setFgsRestrictionLocked(String callingPackage,
             int callingPid, int callingUid, Intent intent, ServiceRecord r,
             boolean allowBackgroundActivityStarts) {
-        // Is the background FGS start restriction turned on?
+        // Check DeviceConfig flag.
         if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
-            return true;
+            r.mAllowWhileInUsePermissionInFgs = true;
         }
+
+        if (!r.mAllowWhileInUsePermissionInFgs || !r.mAllowStartForeground) {
+            final boolean temp = shouldAllowFgsFeatureLocked(callingPackage, callingPid,
+                    callingUid, intent, r, allowBackgroundActivityStarts);
+            if (!r.mAllowWhileInUsePermissionInFgs) {
+                r.mAllowWhileInUsePermissionInFgs = temp;
+            }
+            if (!r.mAllowStartForeground) {
+                r.mAllowStartForeground = temp;
+            }
+        }
+    }
+
+    /**
+     * Should allow FGS feature or not.
+     * @param callingPackage caller app's package name.
+     * @param callingUid caller app's uid.
+     * @param intent intent to start/bind service.
+     * @param r the service to start.
+     * @return true if allow, false otherwise.
+     */
+    private boolean shouldAllowFgsFeatureLocked(String callingPackage,
+            int callingPid, int callingUid, Intent intent, ServiceRecord r,
+            boolean allowBackgroundActivityStarts) {
         // Is the allow activity background start flag on?
         if (allowBackgroundActivityStarts) {
             return true;
@@ -4894,10 +4931,11 @@
         }
 
         // Is the calling UID at PROCESS_STATE_TOP or above?
-        final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
-        if (isCallingUidTopApp) {
+        final int uidState = mAm.getUidState(callingUid);
+        if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
             return true;
         }
+
         // Does the calling UID have any visible activity?
         final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
         if (isCallingUidVisible) {
@@ -4915,6 +4953,19 @@
         if (isDeviceOwner) {
             return true;
         }
+
+        final String info =
+                "[callingPackage: " + callingPackage
+                        + "; callingUid: " + callingUid
+                        + "; uidState: " + ProcessList.makeProcStateString(uidState)
+                        + "; intent: " + intent
+                        + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
+                        + "]";
+        if (!info.equals(r.mInfoAllowStartForeground)) {
+            r.mLoggedInfoAllowStartForeground = false;
+            r.mInfoAllowStartForeground = info;
+        }
+
         return false;
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 775119c..e9539be 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -144,6 +144,13 @@
     private static final String KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED =
             "default_background_fgs_starts_restriction_enabled";
 
+    /**
+     * Default value for mFlagFgsStartRestrictionEnabled if not explicitly set in
+     * Settings.Global.
+     */
+    private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED =
+            "default_fgs_starts_restriction_enabled";
+
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
 
@@ -293,6 +300,11 @@
     // started, the restriction is on while-in-use permissions.)
     volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true;
 
+    // Indicates whether the foreground service background start restriction is enabled.
+    // When the restriction is enabled, service is not allowed to startForeground from background
+    // at all.
+    volatile boolean mFlagFgsStartRestrictionEnabled = false;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -436,6 +448,9 @@
                             case KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED:
                                 updateBackgroundFgsStartsRestriction();
                                 break;
+                            case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED:
+                                updateFgsStartsRestriction();
+                                break;
                             case KEY_OOMADJ_UPDATE_POLICY:
                                 updateOomAdjUpdatePolicy();
                                 break;
@@ -659,6 +674,7 @@
         mFlagForegroundServiceStartsLoggingEnabled = Settings.Global.getInt(mResolver,
                 Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED, 1) == 1;
     }
+
     private void updateBackgroundFgsStartsRestriction() {
         mFlagBackgroundFgsStartRestrictionEnabled = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -666,6 +682,13 @@
                 /*defaultValue*/ true);
     }
 
+    private void updateFgsStartsRestriction() {
+        mFlagFgsStartRestrictionEnabled = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED,
+                /*defaultValue*/ false);
+    }
+
     private void updateOomAdjUpdatePolicy() {
         OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b62ff1c2..a838aad 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14470,7 +14470,7 @@
                 resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                 appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
                 realCallingPid, userId, false /* allowBackgroundActivityStarts */,
-                null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastWhitelist */);
+                null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastAllowList */);
     }
 
     @GuardedBy("this")
@@ -15314,7 +15314,7 @@
                         OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
                         realCallingPid, userId, allowBackgroundActivityStarts,
                         backgroundActivityStartsToken,
-                        null /*broadcastWhitelist*/);
+                        null /* broadcastAllowList */);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 4a27030..66677b67 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -146,6 +146,12 @@
     // the most recent package that start/bind this service.
     String mRecentCallingPackage;
 
+    // allow the service becomes foreground service? Service started from background may not be
+    // allowed to become a foreground service.
+    boolean mAllowStartForeground;
+    String mInfoAllowStartForeground;
+    boolean mLoggedInfoAllowStartForeground;
+
     String stringName;      // caching of toString
 
     private int lastStartId;    // identifier of most recent start request.
@@ -408,6 +414,10 @@
                 pw.println(mAllowWhileInUsePermissionInFgs);
         pw.print(prefix); pw.print("recentCallingPackage=");
                 pw.println(mRecentCallingPackage);
+        pw.print(prefix); pw.print("allowStartForeground=");
+        pw.println(mAllowStartForeground);
+        pw.print(prefix); pw.print("infoAllowStartForeground=");
+        pw.println(mInfoAllowStartForeground);
         if (delayed) {
             pw.print(prefix); pw.print("delayed="); pw.println(delayed);
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0658e81..7420e0a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1630,7 +1630,7 @@
             UserInfo currentUserInfo = getUserInfo(currentUserId);
             Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
             mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
-            mUiHandler.sendMessage(mHandler.obtainMessage(
+            mUiHandler.sendMessage(mUiHandler.obtainMessage(
                     START_USER_SWITCH_UI_MSG, userNames));
         } else {
             mHandler.removeMessages(START_USER_SWITCH_FG_MSG);
@@ -2887,13 +2887,18 @@
 
         void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
                 String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
-            if (!mService.mContext.getPackageManager()
+            if (mService.mContext.getPackageManager()
                     .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
-                final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser,
-                        toUser, true /* above system */, switchingFromSystemUserMessage,
-                        switchingToSystemUserMessage);
-                d.show();
+                // config_customUserSwitchUi is set to true on Automotive as CarSystemUI is
+                // responsible to show the UI; OEMs should not change that, but if they do, we
+                // should at least warn the user...
+                Slog.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
+                        + "condition if it's shown by CarSystemUI as well");
             }
+            final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser,
+                    toUser, true /* above system */, switchingFromSystemUserMessage,
+                    switchingToSystemUserMessage);
+            d.show();
         }
 
         void reportGlobalUsageEventLocked(int event) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b943966..23b0929 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1282,6 +1282,7 @@
             }
 
             if (isPlatformTelevision()) {
+                checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller);
                 synchronized (mHdmiClientLock) {
                     if (mHdmiManager != null && mHdmiPlaybackClient != null) {
                         updateHdmiCecSinkLocked(mHdmiCecSink | false);
@@ -1301,54 +1302,22 @@
         }
     }
 
-    /**
-     * Update volume states for the given device.
-     *
-     * This will initialize the volume index if no volume index is available.
-     * If the device is the currently routed device, fixed/full volume policies will be applied.
-     *
-     * @param device a single audio device, ensure that this is not a devices bitmask
-     * @param caller caller of this method
-     */
-    private void updateVolumeStatesForAudioDevice(int device, String caller) {
+    private void checkAddAllFixedVolumeDevices(int device, String caller) {
         final int numStreamTypes = AudioSystem.getNumStreamTypes();
         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-            updateVolumeStates(device, streamType, caller);
-        }
-    }
+            if (!mStreamStates[streamType].hasIndexForDevice(device)) {
+                // set the default value, if device is affected by a full/fix/abs volume rule, it
+                // will taken into account in checkFixedVolumeDevices()
+                mStreamStates[streamType].setIndex(
+                        mStreamStates[mStreamVolumeAlias[streamType]]
+                                .getIndex(AudioSystem.DEVICE_OUT_DEFAULT),
+                        device, caller, true /*hasModifyAudioSettings*/);
+            }
+            mStreamStates[streamType].checkFixedVolumeDevices();
 
-    /**
-     * Update volume states for the given device and given stream.
-     *
-     * This will initialize the volume index if no volume index is available.
-     * If the device is the currently routed device, fixed/full volume policies will be applied.
-     *
-     * @param device a single audio device, ensure that this is not a devices bitmask
-     * @param streamType streamType to be updated
-     * @param caller caller of this method
-     */
-    private void updateVolumeStates(int device, int streamType, String caller) {
-        if (!mStreamStates[streamType].hasIndexForDevice(device)) {
-            // set the default value, if device is affected by a full/fix/abs volume rule, it
-            // will taken into account in checkFixedVolumeDevices()
-            mStreamStates[streamType].setIndex(
-                    mStreamStates[mStreamVolumeAlias[streamType]]
-                            .getIndex(AudioSystem.DEVICE_OUT_DEFAULT),
-                    device, caller, true /*hasModifyAudioSettings*/);
-        }
-
-        // Check if device to be updated is routed for the given audio stream
-        List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributes(
-                new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
-        for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) {
-            if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType(
-                    device)) {
-                mStreamStates[streamType].checkFixedVolumeDevices();
-
-                // Unmute streams if required if device is full volume
-                if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) {
-                    mStreamStates[streamType].mute(false);
-                }
+            // Unmute streams if device is full volume
+            if (mFullVolumeDevices.contains(device)) {
+                mStreamStates[streamType].mute(false);
             }
         }
     }
@@ -4932,15 +4901,7 @@
         synchronized (VolumeStreamState.class) {
             for (int stream = 0; stream < mStreamStates.length; stream++) {
                 if (stream != skipStream) {
-                    int devices = mStreamStates[stream].observeDevicesForStream_syncVSS(
-                            false /*checkOthers*/);
-
-                    Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices);
-                    for (Integer device : devicesSet) {
-                        // Update volume states for devices routed for the stream
-                        updateVolumeStates(device, stream,
-                                "AudioService#observeDevicesForStreams");
-                    }
+                    mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/);
                 }
             }
         }
@@ -5009,7 +4970,7 @@
                       + Integer.toHexString(audioSystemDeviceOut) + " from:" + caller));
         // make sure we have a volume entry for this device, and that volume is updated according
         // to volume behavior
-        updateVolumeStatesForAudioDevice(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller);
+        checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller);
     }
 
     /**
@@ -7231,9 +7192,10 @@
                 // HDMI output
                 removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
             }
-            updateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI,
-                    "HdmiPlaybackClient.DisplayStatusCallback");
         }
+
+        checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI,
+                "HdmiPlaybackClient.DisplayStatusCallback");
     }
 
     private class MyHdmiControlStatusChangeListenerCallback
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 397358b..dab8c7f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -30,8 +30,6 @@
 import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
 import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
 import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -46,7 +44,6 @@
 import android.database.ContentObserver;
 import android.graphics.ColorSpace;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.hardware.SensorManager;
 import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
@@ -104,7 +101,6 @@
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.UiThread;
 import com.android.server.wm.SurfaceAnimationThread;
 import com.android.server.wm.WindowManagerInternal;
@@ -1394,9 +1390,13 @@
             }
 
             final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
-            return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
-                    displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
-                    false /* useIdentityTransform */, 0 /* rotation */);
+            final SurfaceControl.DisplayCaptureArgs captureArgs =
+                    new SurfaceControl.DisplayCaptureArgs.Builder(token)
+                            .setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight())
+                            .setUseIdentityTransform(true)
+                            .setCaptureSecureLayers(true)
+                            .build();
+            return SurfaceControl.captureDisplay(captureArgs);
         }
     }
 
@@ -1406,30 +1406,11 @@
             if (token == null) {
                 return null;
             }
-            final LogicalDisplay logicalDisplay = mLogicalDisplays.get(displayId);
-            if (logicalDisplay == null) {
-                return null;
-            }
 
-            final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
-            // Takes screenshot based on current device orientation.
-            final Display display = DisplayManagerGlobal.getInstance()
-                    .getRealDisplay(displayId);
-            if (display == null) {
-                return null;
-            }
-            final Point displaySize = new Point();
-            display.getRealSize(displaySize);
-
-            int rotation = displayInfo.rotation;
-            // TODO (b/153382624) : This workaround solution would be removed after
-            // SurfaceFlinger fixes the inconsistency with rotation direction issue.
-            if (rotation == ROTATION_90 || rotation == ROTATION_270) {
-                rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90;
-            }
-
-            return SurfaceControl.screenshotToBuffer(token, new Rect(), displaySize.x,
-                    displaySize.y, false /* useIdentityTransform */, rotation /* rotation */);
+            final SurfaceControl.DisplayCaptureArgs captureArgs =
+                    new SurfaceControl.DisplayCaptureArgs.Builder(token)
+                            .build();
+            return SurfaceControl.captureDisplay(captureArgs);
         }
     }
 
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 7bbcdaa..6672daa 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -949,7 +949,7 @@
                         if (debug) {
                             Slog.d(mTag, "Eagerly recreating service for user " + userId);
                         }
-                        getServiceForUserLocked(userId);
+                        updateCachedServiceLocked(userId);
                     }
                 }
                 onServicePackageRestartedLocked(userId);
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index d4f8c7e..c3532a8 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -1615,7 +1615,7 @@
                 case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
                     // fall through
                 case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
-                    updateService();
+                    updateRegistrations(registration -> true);
                     break;
                 default:
                     break;
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 8004ec7..850cf7f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -49,8 +49,6 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
-import android.os.PowerManager.ServiceType;
-import android.os.PowerSaveState;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -486,10 +484,6 @@
                         deviceIdleService.unregisterStationaryListener(
                                 mDeviceIdleStationaryListener);
                     }
-                    // Intentional fall-through.
-                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
-                case Intent.ACTION_SCREEN_OFF:
-                case Intent.ACTION_SCREEN_ON:
                     // Call updateLowPowerMode on handler thread so it's always called from the
                     // same thread.
                     mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
@@ -554,15 +548,6 @@
     private void updateLowPowerMode() {
         // Disable GPS if we are in device idle mode and the device is stationary.
         boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary;
-        final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION);
-        switch (result.locationMode) {
-            case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
-            case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
-                // If we are in battery saver mode and the screen is off, disable GPS.
-                disableGpsForPowerManager |=
-                        result.batterySaverEnabled && !mPowerManager.isInteractive();
-                break;
-        }
         if (disableGpsForPowerManager != mDisableGpsForPowerManager) {
             mDisableGpsForPowerManager = disableGpsForPowerManager;
             updateEnabled();
@@ -1959,10 +1944,7 @@
             IntentFilter intentFilter = new IntentFilter();
             intentFilter.addAction(ALARM_WAKEUP);
             intentFilter.addAction(ALARM_TIMEOUT);
-            intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
             intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
-            intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-            intentFilter.addAction(Intent.ACTION_SCREEN_ON);
             intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
             intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
             mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index e2f70e3..eb4ab1c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1937,7 +1937,8 @@
                 // Context#getPackageName() for getting package name that matches with the PID/UID,
                 // but it doesn't tell which package has created the MediaController, so useless.
                 return hasMediaControlPermission(controllerPid, controllerUid)
-                        || hasEnabledNotificationListener(userId, controllerPackageName, uid);
+                        || hasEnabledNotificationListener(
+                                userId, controllerPackageName, controllerUid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2001,21 +2002,21 @@
             return resolvedUserId;
         }
 
-        private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName,
-                int uid) {
-            // TODO: revisit this checking code
-            // You may not access another user's content as an enabled listener.
-            final int userId = UserHandle.getUserHandleForUid(resolvedUserId).getIdentifier();
-            if (resolvedUserId != userId) {
+        private boolean hasEnabledNotificationListener(int callingUserId,
+                String controllerPackageName, int controllerUid) {
+            int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier();
+            if (callingUserId != controllerUserId) {
+                // Enabled notification listener only works within the same user.
                 return false;
             }
-            if (mNotificationManager.hasEnabledNotificationListener(packageName,
-                    UserHandle.getUserHandleForUid(uid))) {
+
+            if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName,
+                    UserHandle.getUserHandleForUid(controllerUid))) {
                 return true;
             }
             if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
-                        + "notification listener");
+                Log.d(TAG, controllerPackageName + " (uid=" + controllerUid
+                        + ") doesn't have an enabled notification listener");
             }
             return false;
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a874f612..0d1c00d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2232,7 +2232,7 @@
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, 0 /*flags*/,
                             installerPackageName, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, null /* broadcastWhitelist */);
+                            updateUserIds, instantUserIds, null /* broadcastAllowList */);
                 }
                 // if the required verifier is defined, but, is not the installer of record
                 // for the package, it gets notified
@@ -2242,7 +2242,7 @@
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, 0 /*flags*/,
                             mRequiredVerifierPackage, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, null /* broadcastWhitelist */);
+                            updateUserIds, instantUserIds, null /* broadcastAllowList */);
                 }
                 // If package installer is defined, notify package installer about new
                 // app installed
@@ -2250,7 +2250,7 @@
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
                             mRequiredInstallerPackage, null /*finishedReceiver*/,
-                            firstUserIds, instantUserIds, null /* broadcastWhitelist */);
+                            firstUserIds, instantUserIds, null /* broadcastAllowList */);
                 }
 
                 // Send replaced for users that don't see the package for the first time
@@ -2263,19 +2263,19 @@
                         sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                                 extras, 0 /*flags*/,
                                 installerPackageName, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds, null /*broadcastWhitelist*/);
+                                updateUserIds, instantUserIds, null /*broadcastAllowList*/);
                     }
                     if (notifyVerifier) {
                         sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                                 extras, 0 /*flags*/,
                                 mRequiredVerifierPackage, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds, null /*broadcastWhitelist*/);
+                                updateUserIds, instantUserIds, null /*broadcastAllowList*/);
                     }
                     sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                             null /*package*/, null /*extras*/, 0 /*flags*/,
                             packageName /*targetPackage*/,
                             null /*finishedReceiver*/, updateUserIds, instantUserIds,
-                            null /*broadcastWhitelist*/);
+                            null /*broadcastAllowList*/);
                 } else if (launchedForRestore && !res.pkg.isSystem()) {
                     // First-install and we did a restore, so we're responsible for the
                     // first-launch broadcast.
@@ -14627,7 +14627,7 @@
     private void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
             int[] userIds, int[] instantUserIds) {
         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
-                installerPkg, null, userIds, instantUserIds, null /* broadcastWhitelist */);
+                installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */);
     }
 
     private abstract class HandlerParams {
@@ -18730,14 +18730,14 @@
             packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
                     extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList);
             packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
-                    removedPackage, null, null, null, null /* broadcastWhitelist */);
+                    removedPackage, null, null, null, null /* broadcastAllowList */);
             if (installerPackageName != null) {
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                         removedPackage, extras, 0 /*flags*/,
-                        installerPackageName, null, null, null, null /* broadcastWhitelist */);
+                        installerPackageName, null, null, null, null /* broadcastAllowList */);
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                         removedPackage, extras, 0 /*flags*/,
-                        installerPackageName, null, null, null, null /* broadcastWhitelist */);
+                        installerPackageName, null, null, null, null /* broadcastAllowList */);
             }
         }
 
@@ -25700,9 +25700,9 @@
      * @param instantUserIds User IDs where the action occurred on an instant application
      */
     void sendPackageBroadcast(final String action, final String pkg,
-        final Bundle extras, final int flags, final String targetPkg,
-        final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds,
-        @Nullable SparseArray<int[]> broadcastWhitelist);
+            final Bundle extras, final int flags, final String targetPkg,
+            final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds,
+            @Nullable SparseArray<int[]> broadcastAllowList);
     void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
             boolean includeStopped, int appId, int[] userIds, int[] instantUserIds,
             int dataLoaderType);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bf9506a..f66b4ee 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -87,6 +87,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -103,7 +104,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
@@ -141,6 +141,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Service for {@link UserManager}.
@@ -443,6 +444,11 @@
         }
     };
 
+    // TODO(b/161915546): remove once userWithName() is fixed / removed
+    // Use to debug / dump when user 0 is allocated at userWithName()
+    public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE
+    public final AtomicInteger mUser0Allocations;
+
     /**
      * Start an {@link IntentSender} when user is unlocked after disabling quiet mode.
      *
@@ -629,6 +635,7 @@
         LocalServices.addService(UserManagerInternal.class, mLocalService);
         mLockPatternUtils = new LockPatternUtils(mContext);
         mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
+        mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
     }
 
     void systemReady() {
@@ -1310,6 +1317,10 @@
      */
     private UserInfo userWithName(UserInfo orig) {
         if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) {
+            if (DBG_ALLOCATION) {
+                final int number = mUser0Allocations.incrementAndGet();
+                Slog.w(LOG_TAG, "System user instantiated at least " + number + " times");
+            }
             UserInfo withName = new UserInfo(orig);
             withName.name = getOwnerName();
             return withName;
@@ -4786,6 +4797,7 @@
                 }
             }
 
+            pw.println();
             pw.println("Device properties:");
             pw.println("  Device owner id:" + mDeviceOwnerUserId);
             pw.println();
@@ -4814,12 +4826,9 @@
                 }
                 pw.println(']');
             }
-
             synchronized (mUsersLock) {
-                pw.println();
-                pw.print("Cached user IDs: ");
+                pw.print("  Cached user IDs: ");
                 pw.println(Arrays.toString(mUserIds));
-                pw.println();
             }
 
         } // synchronized (mPackagesLock)
@@ -4835,6 +4844,10 @@
         pw.println("  Is split-system user: " + UserManager.isSplitSystemUser());
         pw.println("  Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
         pw.println("  User version: " + mUserVersion);
+        pw.println("  Owner name: " + getOwnerName());
+        if (DBG_ALLOCATION) {
+            pw.println("  System user allocations: " + mUser0Allocations.get());
+        }
 
         // Dump UserTypes
         pw.println();
@@ -4844,9 +4857,17 @@
             mUserTypes.valueAt(i).dump(pw, "        ");
         }
 
-        // Dump package whitelist
-        pw.println();
-        mSystemPackageInstaller.dump(pw);
+        // TODO: create IndentingPrintWriter at the beginning of dump() and use the proper
+        // indentation methods instead of explicit printing "  "
+        try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw)) {
+
+            // Dump SystemPackageInstaller info
+            ipw.println();
+            mSystemPackageInstaller.dump(ipw);
+
+            // NOTE: pw's not available after this point as it's auto-closed by ipw, so new dump
+            // statements should use ipw below
+        }
     }
 
     private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 492b84a..b95404f 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -28,15 +28,14 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
-import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -723,13 +722,7 @@
         return userTypeList;
     }
 
-    void dump(PrintWriter pw) {
-        try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ")) {
-            dumpIndented(ipw);
-        }
-    }
-
-    private void dumpIndented(IndentingPrintWriter pw) {
+    void dump(IndentingPrintWriter pw) {
         final int mode = getWhitelistMode();
         pw.println("Whitelisted packages per user type");
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 03771be..63aa80e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -83,6 +83,7 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
@@ -120,6 +121,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.TimingsTraceLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -2521,12 +2523,12 @@
 
         final PermissionsState permissionsState = ps.getPermissionsState();
 
-        final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
+        final int[] userIds = getAllUserIds();
 
         boolean runtimePermissionsRevoked = false;
         int[] updatedUserIds = EMPTY_INT_ARRAY;
 
-        for (int userId : currentUserIds) {
+        for (int userId : userIds) {
             if (permissionsState.isMissing(userId)) {
                 Collection<String> requestedPermissions;
                 int targetSdkVersion;
@@ -2592,7 +2594,7 @@
                 // runtime and revocation of a runtime from a shared user.
                 synchronized (mLock) {
                     updatedUserIds = revokeUnusedSharedUserPermissionsLocked(ps.getSharedUser(),
-                            currentUserIds);
+                            userIds);
                     if (!ArrayUtils.isEmpty(updatedUserIds)) {
                         runtimePermissionsRevoked = true;
                     }
@@ -2747,7 +2749,7 @@
                             // a runtime permission being downgraded to an install one.
                             // Also in permission review mode we keep dangerous permissions
                             // for legacy apps
-                            for (int userId : currentUserIds) {
+                            for (int userId : userIds) {
                                 if (origPermissions.getRuntimePermissionState(
                                         perm, userId) != null) {
                                     // Revoke the runtime permission and clear the flags.
@@ -2770,7 +2772,7 @@
                             boolean hardRestricted = bp.isHardRestricted();
                             boolean softRestricted = bp.isSoftRestricted();
 
-                            for (int userId : currentUserIds) {
+                            for (int userId : userIds) {
                                 // If permission policy is not ready we don't deal with restricted
                                 // permissions as the policy may whitelist some permissions. Once
                                 // the policy is initialized we would re-evaluate permissions.
@@ -2909,7 +2911,7 @@
                             boolean hardRestricted = bp.isHardRestricted();
                             boolean softRestricted = bp.isSoftRestricted();
 
-                            for (int userId : currentUserIds) {
+                            for (int userId : userIds) {
                                 // If permission policy is not ready we don't deal with restricted
                                 // permissions as the policy may whitelist some permissions. Once
                                 // the policy is initialized we would re-evaluate permissions.
@@ -3061,10 +3063,10 @@
 
         synchronized (mLock) {
             updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
-                    currentUserIds, updatedUserIds);
+                    userIds, updatedUserIds);
             updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
-                    permissionsState, pkg, newImplicitPermissions, currentUserIds, updatedUserIds);
-            updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, currentUserIds,
+                    permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds);
+            updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds,
                     updatedUserIds);
         }
 
@@ -3081,6 +3083,25 @@
     }
 
     /**
+     * Returns all relevant user ids.  This list include the current set of created user ids as well
+     * as pre-created user ids.
+     * @return user ids for created users and pre-created users
+     */
+    private int[] getAllUserIds() {
+        final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+        t.traceBegin("getAllUserIds");
+        List<UserInfo> users = UserManagerService.getInstance().getUsers(
+                /*excludePartial=*/ true, /*excludeDying=*/ true, /*excludePreCreated=*/ false);
+        int size = users.size();
+        final int[] userIds = new int[size];
+        for (int i = 0; i < size; i++) {
+            userIds[i] = users.get(i).id;
+        }
+        t.traceEnd();
+        return userIds;
+    }
+
+    /**
      * Revoke permissions that are not implicit anymore and that have
      * {@link PackageManager#FLAG_PERMISSION_REVOKE_WHEN_REQUESTED} set.
      *
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 2a74b3d..5aedfc1 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -28,8 +28,6 @@
 import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
-import android.app.role.OnRoleHoldersChangedListener;
-import android.app.role.RoleManager;
 import android.app.slice.ISliceManager;
 import android.app.slice.SliceSpec;
 import android.app.usage.UsageStatsManagerInternal;
@@ -41,9 +39,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
@@ -65,7 +61,6 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -77,10 +72,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.function.Supplier;
 
 public class SliceManagerService extends ISliceManager.Stub {
 
@@ -88,16 +80,13 @@
     private final Object mLock = new Object();
 
     private final Context mContext;
-    private final PackageManagerInternal mPackageManagerInternal;
     private final AppOpsManager mAppOps;
     private final AssistUtils mAssistUtils;
 
     @GuardedBy("mLock")
     private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
     @GuardedBy("mLock")
-    private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>();
-    @GuardedBy("mLock")
-    private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>();
+    private final SparseArray<String> mLastAssistantPackage = new SparseArray<>();
     private final Handler mHandler;
 
     private final SlicePermissionManager mPermissions;
@@ -110,8 +99,6 @@
     @VisibleForTesting
     SliceManagerService(Context context, Looper looper) {
         mContext = context;
-        mPackageManagerInternal = Objects.requireNonNull(
-                LocalServices.getService(PackageManagerInternal.class));
         mAppOps = context.getSystemService(AppOpsManager.class);
         mAssistUtils = new AssistUtils(context);
         mHandler = new Handler(looper);
@@ -124,7 +111,6 @@
         filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
-        mRoleObserver = new RoleObserver();
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
     }
 
@@ -174,8 +160,7 @@
         mHandler.post(() -> {
             if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
                 mAppUsageStats.reportEvent(slicePkg, user,
-                        isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
-                                ? SLICE_PINNED_PRIV : SLICE_PINNED);
+                        isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED);
             }
         });
     }
@@ -440,38 +425,19 @@
     private boolean hasFullSliceAccess(String pkg, int userId) {
         long ident = Binder.clearCallingIdentity();
         try {
-            boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
-                    || isGrantedFullAccess(pkg, userId);
-            return ret;
+            return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
     private boolean isAssistant(String pkg, int userId) {
-        return getAssistantMatcher(userId).matches(pkg);
-    }
-
-    private boolean isDefaultHomeApp(String pkg, int userId) {
-        return getHomeMatcher(userId).matches(pkg);
-    }
-
-    private PackageMatchingCache getAssistantMatcher(int userId) {
-        PackageMatchingCache matcher = mAssistantLookup.get(userId);
-        if (matcher == null) {
-            matcher = new PackageMatchingCache(() -> getAssistant(userId));
-            mAssistantLookup.put(userId, matcher);
+        if (pkg == null) return false;
+        if (!pkg.equals(mLastAssistantPackage.get(userId))) {
+            // Failed on cached value, try updating.
+            mLastAssistantPackage.put(userId, getAssistant(userId));
         }
-        return matcher;
-    }
-
-    private PackageMatchingCache getHomeMatcher(int userId) {
-        PackageMatchingCache matcher = mHomeLookup.get(userId);
-        if (matcher == null) {
-            matcher = new PackageMatchingCache(() -> getDefaultHome(userId));
-            mHomeLookup.put(userId, matcher);
-        }
-        return matcher;
+        return pkg.equals(mLastAssistantPackage.get(userId));
     }
 
     private String getAssistant(int userId) {
@@ -482,111 +448,6 @@
         return cn.getPackageName();
     }
 
-    /**
-     * A cached value of the default home app
-     */
-    private String mCachedDefaultHome = null;
-
-    // Based on getDefaultHome in ShortcutService.
-    // TODO: Unify if possible
-    @VisibleForTesting
-    protected String getDefaultHome(int userId) {
-
-        // Set VERIFY to true to run the cache in "shadow" mode for cache
-        // testing.  Do not commit set to true;
-        final boolean VERIFY = false;
-
-        if (mCachedDefaultHome != null) {
-            if (!VERIFY) {
-                return mCachedDefaultHome;
-            }
-        }
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
-
-            // Default launcher from package manager.
-            final ComponentName defaultLauncher = mPackageManagerInternal
-                    .getHomeActivitiesAsUser(allHomeCandidates, userId);
-
-            ComponentName detected = defaultLauncher;
-
-            // Cache the default launcher.  It is not a problem if the
-            // launcher is null - eventually, the default launcher will be
-            // set to something non-null.
-            mCachedDefaultHome = ((detected != null) ? detected.getPackageName() : null);
-
-            if (detected == null) {
-                // If we reach here, that means it's the first check since the user was created,
-                // and there's already multiple launchers and there's no default set.
-                // Find the system one with the highest priority.
-                // (We need to check the priority too because of FallbackHome in Settings.)
-                // If there's no system launcher yet, then no one can access slices, until
-                // the user explicitly sets one.
-                final int size = allHomeCandidates.size();
-
-                int lastPriority = Integer.MIN_VALUE;
-                for (int i = 0; i < size; i++) {
-                    final ResolveInfo ri = allHomeCandidates.get(i);
-                    if (!ri.activityInfo.applicationInfo.isSystemApp()) {
-                        continue;
-                    }
-                    if (ri.priority < lastPriority) {
-                        continue;
-                    }
-                    detected = ri.activityInfo.getComponentName();
-                    lastPriority = ri.priority;
-                }
-            }
-            final String ret = ((detected != null) ? detected.getPackageName() : null);
-            if (VERIFY) {
-                if (mCachedDefaultHome != null && !mCachedDefaultHome.equals(ret)) {
-                    Slog.e(TAG, "getDefaultHome() cache failure, is " +
-                           mCachedDefaultHome + " should be " + ret);
-                }
-            }
-            return ret;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public void invalidateCachedDefaultHome() {
-        mCachedDefaultHome = null;
-    }
-
-    /**
-     * Listen for changes in the roles, and invalidate the cached default
-     * home as necessary.
-     */
-    private RoleObserver mRoleObserver;
-
-    class RoleObserver implements OnRoleHoldersChangedListener {
-        private RoleManager mRm;
-        private final Executor mExecutor;
-
-        RoleObserver() {
-            mExecutor = mContext.getMainExecutor();
-            register();
-        }
-
-        public void register() {
-            mRm = mContext.getSystemService(RoleManager.class);
-            if (mRm != null) {
-                mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL);
-                invalidateCachedDefaultHome();
-            }
-        }
-
-        @Override
-        public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
-            if (RoleManager.ROLE_HOME.equals(roleName)) {
-                invalidateCachedDefaultHome();
-            }
-        }
-    }
-
     private boolean isGrantedFullAccess(String pkg, int userId) {
         return mPermissions.hasFullAccess(pkg, userId);
     }
@@ -635,30 +496,6 @@
         return mPermissions.getAllPackagesGranted(pkg);
     }
 
-    /**
-     * Holder that caches a package that has access to a slice.
-     */
-    static class PackageMatchingCache {
-
-        private final Supplier<String> mPkgSource;
-        private String mCurrentPkg;
-
-        public PackageMatchingCache(Supplier<String> pkgSource) {
-            mPkgSource = pkgSource;
-        }
-
-        public boolean matches(String pkgCandidate) {
-            if (pkgCandidate == null) return false;
-
-            if (Objects.equals(pkgCandidate, mCurrentPkg)) {
-                return true;
-            }
-            // Failed on cached value, try updating.
-            mCurrentPkg = mPkgSource.get();
-            return Objects.equals(pkgCandidate, mCurrentPkg);
-        }
-    }
-
     public static class Lifecycle extends SystemService {
         private SliceManagerService mService;
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index c2b0d106..0ca36e0 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -72,6 +72,10 @@
             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
@@ -90,6 +94,7 @@
     public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
         return new TimeZoneConfiguration.Builder()
                 .setAutoDetectionEnabled(isAutoDetectionEnabled())
+                .setGeoDetectionEnabled(isGeoDetectionEnabled())
                 .build();
     }
 
@@ -105,8 +110,11 @@
         // 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 value = configuration.isAutoDetectionEnabled() ? 1 : 0;
-            Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, value);
+            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.
         }
     }
 
@@ -126,6 +134,14 @@
     }
 
     @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;
+    }
+
+    @Override
     public boolean isDeviceTimeZoneInitialized() {
         // timezone.equals("GMT") will be true and only true if the time zone was
         // set to a default value by the system server (when starting, system server
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index 3d9ec64..fb7a73d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -22,6 +22,7 @@
  * The internal (in-process) system server API for the {@link
  * com.android.server.timezonedetector.TimeZoneDetectorService}.
  *
+ * <p>The methods on this class can be called from any thread.
  * @hide
  */
 public interface TimeZoneDetectorInternal extends Dumpable.Container {
@@ -29,7 +30,7 @@
     /**
      * 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
-     * available, and so on.
+     * available, and so on. This method may be implemented asynchronously.
      */
     void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 4464f7d..15412a0 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -58,6 +58,7 @@
             @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
         Objects.requireNonNull(timeZoneSuggestion);
 
+        // All strategy calls must take place on the mHandler thread.
         mHandler.post(
                 () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
     }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 7467439..d81f949 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -130,6 +130,10 @@
                         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.
         return service;
     }
 
@@ -280,7 +284,7 @@
 
     void handleConfigurationChanged() {
         // Note: we could trigger an async time zone detection operation here via a call to
-        // handleAutoTimeZoneDetectionChanged(), but that is triggered in response to the underlying
+        // 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.
@@ -288,7 +292,6 @@
         // 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++) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index f947a655..c5b7e39 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -30,9 +30,9 @@
  * <p>The strategy uses suggestions to decide whether to modify the device's time zone setting
  * and what to set it to.
  *
- * <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 so
- * implementations mustvhandle thread safety.
+ * <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.
  *
  * @hide
  */
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 9c36c39..d1369a2 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -23,6 +23,7 @@
 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;
@@ -98,6 +99,12 @@
         boolean isAutoDetectionEnabled();
 
         /**
+         * Returns whether geolocation can be used for time zone detection when {@link
+         * #isAutoDetectionEnabled()} returns {@code true}.
+         */
+        boolean isGeoDetectionEnabled();
+
+        /**
          * Returns true if the device has had an explicit time zone set.
          */
         boolean isDeviceTimeZoneInitialized();
@@ -200,7 +207,15 @@
      */
     @GuardedBy("this")
     private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
-            mSuggestionBySlotIndex = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+            mTelephonySuggestionsBySlotIndex =
+            new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+
+    /**
+     * The latest geolocation suggestion received.
+     */
+    @GuardedBy("this")
+    private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
+            new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     @GuardedBy("this")
     private final List<Dumpable> mDumpables = new ArrayList<>();
@@ -284,17 +299,26 @@
 
     private static boolean containsAutoTimeDetectionProperties(
             @NonNull TimeZoneConfiguration configuration) {
-        return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED);
+        return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
+                || configuration.hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
     }
 
     @Override
     public synchronized void suggestGeolocationTimeZone(
             @NonNull GeolocationTimeZoneSuggestion suggestion) {
-        Objects.requireNonNull(suggestion);
+        if (DBG) {
+            Slog.d(LOG_TAG, "Geolocation suggestion received. newSuggestion=" + suggestion);
+        }
 
-        // TODO Implement this.
-        throw new UnsupportedOperationException(
-                "Geo-location time zone detection is not currently implemented");
+        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()) {
+            String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
+            doAutoTimeZoneDetection(reason);
+        }
     }
 
     @Override
@@ -332,12 +356,14 @@
                 new QualifiedTelephonyTimeZoneSuggestion(suggestion, score);
 
         // Store the suggestion against the correct slotIndex.
-        mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
+        mTelephonySuggestionsBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
 
         // Now perform auto time zone detection. The new suggestion may be used to modify the time
         // zone setting.
-        String reason = "New telephony time suggested. suggestion=" + suggestion;
-        doAutoTimeZoneDetection(reason);
+        if (!mCallback.isGeoDetectionEnabled()) {
+            String reason = "New telephony time zone suggested. suggestion=" + suggestion;
+            doAutoTimeZoneDetection(reason);
+        }
     }
 
     private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
@@ -363,9 +389,7 @@
     }
 
     /**
-     * Finds the best available time zone suggestion from all slotIndexes. If it is high-enough
-     * quality and automatic time zone detection is enabled then it will be set on the device. The
-     * outcome can be that this strategy becomes / remains un-opinionated and nothing is set.
+     * Performs automatic time zone detection.
      */
     @GuardedBy("this")
     private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
@@ -374,6 +398,62 @@
             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()) {
+            doGeolocationTimeZoneDetection(detectionReason);
+        } else  {
+            doTelephonyTimeZoneDetection(detectionReason);
+        }
+    }
+
+    /**
+     * Detects the time zone using the latest available geolocation time zone suggestion, if one is
+     * available. The outcome can be that this strategy becomes / remains un-opinionated and nothing
+     * is set.
+     */
+    @GuardedBy("this")
+    private void doGeolocationTimeZoneDetection(@NonNull String detectionReason) {
+        GeolocationTimeZoneSuggestion latestGeolocationSuggestion =
+                mLatestGeoLocationSuggestion.get();
+        if (latestGeolocationSuggestion == null) {
+            return;
+        }
+
+        List<String> zoneIds = latestGeolocationSuggestion.getZoneIds();
+        if (zoneIds == null || zoneIds.isEmpty()) {
+            // This means the client has become uncertain about the time zone or it is certain there
+            // is no known zone. In either case we must leave the existing time zone setting as it
+            // is.
+            return;
+        }
+
+        // GeolocationTimeZoneSuggestion has no measure of quality. We assume all suggestions are
+        // reliable.
+        String zoneId;
+
+        // Introduce bias towards the device's current zone when there are multiple zone suggested.
+        String deviceTimeZone = mCallback.getDeviceTimeZone();
+        if (zoneIds.contains(deviceTimeZone)) {
+            if (DBG) {
+                Slog.d(LOG_TAG,
+                        "Geo tz suggestion contains current device time zone. Applying bias.");
+            }
+            zoneId = deviceTimeZone;
+        } else {
+            zoneId = zoneIds.get(0);
+        }
+        setDeviceTimeZoneIfRequired(zoneId, detectionReason);
+    }
+
+    /**
+     * Detects the time zone using the latest available telephony time zone suggestions.
+     * Finds the best available time zone suggestion from all slotIndexes. If it is high-enough
+     * quality and automatic time zone detection is enabled then it will be set on the device. The
+     * outcome can be that this strategy becomes / remains un-opinionated and nothing is set.
+     */
+    @GuardedBy("this")
+    private void doTelephonyTimeZoneDetection(@NonNull String detectionReason) {
         QualifiedTelephonyTimeZoneSuggestion bestTelephonySuggestion =
                 findBestTelephonySuggestion();
 
@@ -468,9 +548,9 @@
         // slotIndex and find the best. Note that we deliberately do not look at age: the caller can
         // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
         // expected to withdraw suggestions they no longer have confidence in.
-        for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+        for (int i = 0; i < mTelephonySuggestionsBySlotIndex.size(); i++) {
             QualifiedTelephonyTimeZoneSuggestion candidateSuggestion =
-                    mSuggestionBySlotIndex.valueAt(i);
+                    mTelephonySuggestionsBySlotIndex.valueAt(i);
             if (candidateSuggestion == null) {
                 // Unexpected
                 continue;
@@ -505,14 +585,10 @@
     @Override
     public synchronized void handleAutoTimeZoneConfigChanged() {
         if (DBG) {
-            Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
+            Slog.d(LOG_TAG, "handleAutoTimeZoneConfigChanged()");
         }
-        if (mCallback.isAutoDetectionEnabled()) {
-            // When the user enabled time zone detection, run the time zone detection and change the
-            // device time zone if possible.
-            String reason = "Auto time zone detection setting enabled.";
-            doAutoTimeZoneDetection(reason);
-        }
+
+        doAutoTimeZoneDetection("handleAutoTimeZoneConfigChanged()");
     }
 
     @Override
@@ -532,15 +608,21 @@
         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
         mTimeZoneChangesLog.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
+        ipw.println("Geolocation suggestion history:");
+        ipw.increaseIndent(); // level 2
+        mLatestGeoLocationSuggestion.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
         ipw.println("Telephony suggestion history:");
         ipw.increaseIndent(); // level 2
-        mSuggestionBySlotIndex.dump(ipw);
+        mTelephonySuggestionsBySlotIndex.dump(ipw);
         ipw.decreaseIndent(); // level 2
         ipw.decreaseIndent(); // level 1
 
@@ -555,7 +637,15 @@
     @VisibleForTesting
     public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion(
             int slotIndex) {
-        return mSuggestionBySlotIndex.get(slotIndex);
+        return mTelephonySuggestionsBySlotIndex.get(slotIndex);
+    }
+
+    /**
+     * A method used to inspect strategy state during tests. Not intended for general use.
+     */
+    @VisibleForTesting
+    public GeolocationTimeZoneSuggestion getLatestGeolocationSuggestion() {
+        return mLatestGeoLocationSuggestion.get();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2f695c6..202a3dc 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3070,7 +3070,7 @@
                     mLockWallpaperMap.put(userId, wallpaper);
                     ensureSaneWallpaperData(wallpaper);
                 } else {
-                    // sanity fallback: we're in bad shape, but establishing a known
+                    // rationality fallback: we're in bad shape, but establishing a known
                     // valid system+lock WallpaperData will keep us from dying.
                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
                     wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e573d36..5b80787 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -107,6 +107,12 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_UNSET;
 
+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_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.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;
@@ -172,12 +178,6 @@
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.Task.ActivityState.DESTROYED;
@@ -294,6 +294,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
@@ -305,7 +306,6 @@
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriPermissionOwner;
 import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 11a468b..f76108f 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -67,10 +67,10 @@
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+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.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
 import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -127,11 +127,11 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.AttributeCache;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.animation.ClipRectLRAnimation;
 import com.android.server.wm.animation.ClipRectTBAnimation;
 import com.android.server.wm.animation.CurvedTranslateAnimation;
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 5720e9b..57d51c5 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -41,14 +41,14 @@
 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
 
+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.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -69,7 +69,7 @@
 import android.view.animation.Animation;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index f563e57..0b2c851 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -16,13 +16,13 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
 
 import android.graphics.Rect;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.function.Supplier;
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 6ffb482..8bd42f0 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -24,11 +24,11 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.util.Preconditions.checkState;
 import static com.android.server.wm.DisplayAreaProto.IS_TASK_DISPLAY_AREA;
 import static com.android.server.wm.DisplayAreaProto.NAME;
 import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
 
 import android.annotation.Nullable;
@@ -38,8 +38,8 @@
 import android.window.DisplayAreaInfo;
 import android.window.IDisplayAreaOrganizer;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.util.Comparator;
 import java.util.function.BiFunction;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fc17053..ba5a382 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -74,6 +74,14 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
+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_IME;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -94,14 +102,6 @@
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_BOOT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -203,6 +203,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.util.function.pooled.PooledConsumer;
@@ -211,7 +212,6 @@
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.utils.DisplayRotationUtil;
 import com.android.server.wm.utils.RotationCache;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -4063,9 +4063,14 @@
         if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
 
         // Send invalid rect and no width and height since it will screenshot the entire display.
-        Rect frame = new Rect(0, 0, -1, -1);
-        final Bitmap bitmap = SurfaceControl.screenshot(frame, 0, 0, inRotation,
-                mDisplay.getRotation());
+        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+        final SurfaceControl.DisplayCaptureArgs captureArgs =
+                new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+                        .setUseIdentityTransform(inRotation)
+                        .build();
+        final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+                SurfaceControl.captureDisplay(captureArgs);
+        final Bitmap bitmap = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
         if (bitmap == null) {
             Slog.w(TAG_WM, "Failed to take screenshot");
             return null;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 4f6f75d..2e03cb8 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -112,6 +112,7 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
 import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
@@ -120,7 +121,6 @@
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -187,6 +187,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.view.AppearanceRegion;
@@ -199,7 +200,6 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
 import com.android.server.policy.WindowOrientationListener;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wallpaper.WallpaperManagerInternal;
 import com.android.server.wm.utils.InsetUtils;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index c63128c..0206787 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -22,8 +22,8 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -58,12 +58,12 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowOrientationListener;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 22dd1d3..133b111 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -18,11 +18,11 @@
 
 import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.DragDropController.MSG_ANIMATION_END;
 import static com.android.server.wm.DragDropController.MSG_DRAG_END_TIMEOUT;
 import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -58,9 +58,9 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.view.IDragAndDropPermissions;
 import com.android.server.LocalServices;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.util.ArrayList;
 
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index e7fbc334..86e2698 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -16,13 +16,13 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
 
 import android.view.InsetsSource;
 import android.view.WindowInsets;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 791f471..0fe9735 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -41,7 +41,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -60,8 +60,8 @@
 import android.view.InputWindowHandle;
 import android.view.SurfaceControl;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.Set;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f64149c..d1eb795 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -24,7 +24,7 @@
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
 import static android.view.ViewRootImpl.sNewInsetsMode;
 
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
 import static com.android.server.wm.WindowManagerService.H.LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED;
 
@@ -40,8 +40,8 @@
 import android.view.SurfaceControl.Transaction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.TriConsumer;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 33dde32..ab1074e 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -30,7 +30,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -45,8 +45,8 @@
 import android.view.InsetsState.InternalInsetsType;
 import android.view.WindowManager;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.inputmethod.InputMethodManagerInternal;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index d7b43bc..6c41683 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -25,8 +25,8 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.TRANSIT_NONE;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
@@ -41,9 +41,9 @@
 import android.util.Slog;
 import android.view.IRecentsAnimationRunner;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6b3a5d6..143fbb0 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -23,10 +23,10 @@
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
@@ -56,12 +56,12 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
+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;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c255a18..e7461e7 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -37,9 +37,9 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
+import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.FastPrintWriter;
-import com.android.server.protolog.ProtoLogImpl;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 1cb483c..b3a3ed7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -41,6 +41,11 @@
 import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -59,11 +64,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
 import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
@@ -146,6 +146,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
+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;
@@ -155,7 +156,6 @@
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.UserState;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index d7b8fb0..3c8036d 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -18,9 +18,9 @@
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.AnimationSpecProto.ROTATE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS;
 import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA;
 import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA;
@@ -50,7 +50,7 @@
 import android.view.animation.Transformation;
 
 import com.android.internal.R;
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 import com.android.server.wm.utils.RotationAnimationUtils;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 86cbf3e..3b32a9d76 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -24,7 +24,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
 
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -58,7 +58,7 @@
 import android.view.WindowManager;
 
 import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 1c97430..d9365c5 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
 
@@ -28,7 +28,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 
 import java.util.function.Supplier;
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2b6e5ef..ec7b1ee 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -78,6 +78,8 @@
 
 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_RECENTS_ANIMATIONS;
 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -116,8 +118,6 @@
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.Task.ActivityState.PAUSED;
 import static com.android.server.wm.Task.ActivityState.PAUSING;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
@@ -210,6 +210,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledFunction;
@@ -218,7 +219,6 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.AppTimeTracker;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.uri.NeededUriGrants;
 
 import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index a847744..3251110 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -32,13 +32,13 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
 import static com.android.server.wm.DisplayContent.alwaysCreateStack;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.RootWindowContainer.TAG_STATES;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
 import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
@@ -58,10 +58,10 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 3fbc037..a66cd84 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -26,8 +26,8 @@
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -59,7 +59,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.TaskResizingAlgorithm;
 import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 
 class TaskPositioner implements IBinder.DeathRecipient {
     private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index 1103bf1..3def091 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -15,14 +15,14 @@
  */
 package com.android.server.wm;
 
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 
 import android.hardware.HardwareBuffer;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 
 import java.util.function.Function;
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 9b18ac8..e9ada6b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -41,7 +41,7 @@
 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getNavigationBarRect;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets;
 import static com.android.server.wm.TaskSnapshotController.mergeInsetsSources;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -83,9 +83,9 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.DecorView;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.view.BaseIWindow;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
-import com.android.server.protolog.common.ProtoLog;
 
 /**
  * This class represents a starting window that shows a snapshot.
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index f467015..38bff9e 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -15,8 +15,8 @@
  */
 package com.android.server.wm;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 
@@ -26,7 +26,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 6377a21..5c6266a 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -36,8 +36,8 @@
 import android.view.Choreographer;
 import android.view.SurfaceControl;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e24d185..8a5e70f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -28,14 +28,14 @@
 import static android.os.UserHandle.USER_NULL;
 import static android.view.SurfaceControl.Transaction;
 
+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_ORIENTATION;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -82,8 +82,8 @@
 import android.window.WindowContainerToken;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index b75f886..b9f67a5 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -19,7 +19,7 @@
 import static android.view.SurfaceControl.METADATA_OWNER_UID;
 import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
 
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowContainerThumbnailProto.HEIGHT;
 import static com.android.server.wm.WindowContainerThumbnailProto.SURFACE_ANIMATOR;
@@ -39,7 +39,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.animation.Animation;
 
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b1d6bc..b33a8e9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -84,22 +84,22 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
+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_IME;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
+import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_BOOT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.ProtoLogGroup.WM_ERROR;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -265,6 +265,8 @@
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.KeyInterceptionInfo;
+import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
@@ -281,8 +283,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
-import com.android.server.protolog.ProtoLogImpl;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.utils.PriorityDump;
 import com.android.server.wm.utils.DeviceConfigInterface;
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bdecb8d..271d2b1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -31,7 +31,7 @@
 import android.view.ViewDebug;
 
 import com.android.internal.os.ByteTransferPipe;
-import com.android.server.protolog.ProtoLogImpl;
+import com.android.internal.protolog.ProtoLogImpl;
 
 import java.io.IOException;
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d9557fa..ebbd74a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -101,6 +101,14 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 
+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_FOCUS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RESIZE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
@@ -116,14 +124,6 @@
 import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS;
 import static com.android.server.wm.MoveAnimationSpecProto.FROM;
 import static com.android.server.wm.MoveAnimationSpecProto.TO;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RESIZE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -236,10 +236,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.KeyInterceptionInfo;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.utils.WmDisplayCutout;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d0101ad..16edb55 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -31,13 +31,13 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_NONE;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DRAW;
+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_SHOW_SURFACE_ALLOC;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 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.ProtoLogGroup.WM_DEBUG_DRAW;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
@@ -76,8 +76,8 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index b89cdd3..9b40822 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -21,8 +21,8 @@
 import static android.view.SurfaceControl.METADATA_OWNER_UID;
 import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
 
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -40,7 +40,7 @@
 import android.view.WindowContentFrameStats;
 import android.view.WindowManager;
 
-import com.android.server.protolog.common.ProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 2c1bb3e..f7cd37f 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -23,10 +23,10 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.ProtoLogGroup.WM_ERROR;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
+import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -59,8 +59,8 @@
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index ba3dc60..e8b8bfc 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -34,7 +34,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 
-import com.android.server.protolog.ProtoLogImpl;
+import com.android.internal.protolog.ProtoLogImpl;
 import com.android.internal.util.TraceBuffer;
 
 import java.io.File;
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index b6633ce..529fb88 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -17,9 +17,7 @@
 #define LOG_TAG "VibratorService"
 
 #include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/BnVibratorCallback.h>
 #include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IServiceManager.h>
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
@@ -28,19 +26,11 @@
 
 #include <utils/misc.h>
 #include <utils/Log.h>
-#include <hardware/vibrator.h>
 
 #include <inttypes.h>
-#include <stdio.h>
 
 #include <vibratorservice/VibratorHalController.h>
 
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::vibrator::V1_0::EffectStrength;
-using android::hardware::vibrator::V1_0::Status;
-using android::hardware::vibrator::V1_1::Effect_1_1;
-
 namespace V1_0 = android::hardware::vibrator::V1_0;
 namespace V1_1 = android::hardware::vibrator::V1_1;
 namespace V1_2 = android::hardware::vibrator::V1_2;
@@ -87,150 +77,7 @@
 static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
                 static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
 
-class VibratorCallback {
-    public:
-        VibratorCallback(JNIEnv *env, jobject vibration) :
-        mVibration(MakeGlobalRefOrDie(env, vibration)) {}
-
-        ~VibratorCallback() {
-            JNIEnv *env = AndroidRuntime::getJNIEnv();
-            env->DeleteGlobalRef(mVibration);
-        }
-
-        void onComplete() {
-            auto env = AndroidRuntime::getJNIEnv();
-            env->CallVoidMethod(mVibration, sMethodIdOnComplete);
-        }
-
-    private:
-        jobject mVibration;
-};
-
-class AidlVibratorCallback : public aidl::BnVibratorCallback {
-  public:
-    AidlVibratorCallback(JNIEnv *env, jobject vibration) :
-    mCb(env, vibration) {}
-
-    binder::Status onComplete() override {
-        mCb.onComplete();
-        return binder::Status::ok(); // oneway, local call
-    }
-
-  private:
-    VibratorCallback mCb;
-};
-
-static constexpr int NUM_TRIES = 2;
-
-template<class R>
-inline R NoneStatus() {
-    using ::android::hardware::Status;
-    return Status::fromExceptionCode(Status::EX_NONE);
-}
-
-template<>
-inline binder::Status NoneStatus() {
-    using binder::Status;
-    return Status::fromExceptionCode(Status::EX_NONE);
-}
-
-// Creates a Return<R> with STATUS::EX_NULL_POINTER.
-template<class R>
-inline R NullptrStatus() {
-    using ::android::hardware::Status;
-    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
-}
-
-template<>
-inline binder::Status NullptrStatus() {
-    using binder::Status;
-    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
-}
-
-template <typename I>
-sp<I> getService() {
-    return I::getService();
-}
-
-template <>
-sp<aidl::IVibrator> getService() {
-    return waitForVintfService<aidl::IVibrator>();
-}
-
-template <typename I>
-sp<I> tryGetService() {
-    return I::tryGetService();
-}
-
-template <>
-sp<aidl::IVibrator> tryGetService() {
-    return checkVintfService<aidl::IVibrator>();
-}
-
-template <typename I>
-class HalWrapper {
-  public:
-    static std::unique_ptr<HalWrapper> Create() {
-        // Assume that if getService returns a nullptr, HAL is not available on the
-        // device.
-        auto hal = getService<I>();
-        return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
-    }
-
-    // Helper used to transparently deal with the vibrator HAL becoming unavailable.
-    template<class R, class... Args0, class... Args1>
-    R call(R (I::* fn)(Args0...), Args1&&... args1) {
-        // Return<R> doesn't have a default constructor, so make a Return<R> with
-        // STATUS::EX_NONE.
-        R ret{NoneStatus<R>()};
-
-        // Note that ret is guaranteed to be changed after this loop.
-        for (int i = 0; i < NUM_TRIES; ++i) {
-            ret = (mHal == nullptr) ? NullptrStatus<R>()
-                    : (*mHal.*fn)(std::forward<Args1>(args1)...);
-
-            if (ret.isOk()) {
-                break;
-            }
-
-            ALOGE("Failed to issue command to vibrator HAL. Retrying.");
-
-            // Restoring connection to the HAL.
-            mHal = tryGetService<I>();
-        }
-        return ret;
-    }
-
-  private:
-    HalWrapper(sp<I> &&hal) : mHal(std::move(hal)) {}
-
-  private:
-    sp<I> mHal;
-};
-
-template <typename I>
-static auto getHal() {
-    static auto sHalWrapper = HalWrapper<I>::Create();
-    return sHalWrapper.get();
-}
-
-template<class R, class I, class... Args0, class... Args1>
-R halCall(R (I::* fn)(Args0...), Args1&&... args1) {
-    auto hal = getHal<I>();
-    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
-}
-
-template<class R>
-bool isValidEffect(jlong effect) {
-    if (effect < 0) {
-        return false;
-    }
-    R val = static_cast<R>(effect);
-    auto iter = hardware::hidl_enum_range<R>();
-    return val >= *iter.begin() && val <= *std::prev(iter.end());
-}
-
-static void callVibrationOnComplete(jobject vibration) {
+static inline void callVibrationOnComplete(jobject vibration) {
     if (vibration == nullptr) {
         return;
     }
@@ -257,14 +104,6 @@
 }
 
 static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        // IBinder::pingBinder isn't accessible as a pointer function
-        // but getCapabilities can serve the same purpose
-        int32_t cap;
-        hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk();
-    } else {
-        halCall(&V1_0::IVibrator::ping).isOk();
-    }
     std::unique_ptr<vibrator::HalController> controller =
             std::make_unique<vibrator::HalController>();
     controller->init();
@@ -342,70 +181,19 @@
     return effects;
 }
 
-static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong effect, jlong strength,
-                                   jobject vibration, jboolean withCallback) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        int32_t lengthMs;
-        sp<AidlVibratorCallback> effectCallback =
-                (withCallback != JNI_FALSE ? new AidlVibratorCallback(env, vibration) : nullptr);
-        aidl::Effect effectType(static_cast<aidl::Effect>(effect));
-        aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength));
-
-        auto status = hal->call(&aidl::IVibrator::perform, effectType, effectStrength, effectCallback, &lengthMs);
-        if (!status.isOk()) {
-            if (status.exceptionCode() != binder::Status::EX_UNSUPPORTED_OPERATION) {
-                ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
-                        ": %s", static_cast<int64_t>(effect), static_cast<int32_t>(strength), status.toString8().string());
-            }
-            return -1;
-        }
-        return lengthMs;
-    } else {
-        Status status;
-        uint32_t lengthMs;
-        auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) {
-            status = retStatus;
-            lengthMs = retLengthMs;
-        };
-        EffectStrength effectStrength(static_cast<EffectStrength>(strength));
-
-        Return<void> ret;
-        if (isValidEffect<V1_0::Effect>(effect)) {
-            ret = halCall(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(effect),
-                    effectStrength, callback);
-        } else if (isValidEffect<Effect_1_1>(effect)) {
-            ret = halCall(&V1_1::IVibrator::perform_1_1, static_cast<Effect_1_1>(effect),
-                            effectStrength, callback);
-        } else if (isValidEffect<V1_2::Effect>(effect)) {
-            ret = halCall(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(effect),
-                            effectStrength, callback);
-        } else if (isValidEffect<V1_3::Effect>(effect)) {
-            ret = halCall(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(effect),
-                            effectStrength, callback);
-        } else {
-            ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")",
-                    static_cast<int32_t>(effect));
-            return -1;
-        }
-
-        if (!ret.isOk()) {
-            ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect));
-            return -1;
-        }
-
-        if (status == Status::OK) {
-            return lengthMs;
-        } else if (status != Status::UNSUPPORTED_OPERATION) {
-            // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor
-            // doesn't have a pre-defined waveform to perform for it, so we should just give the
-            // opportunity to fall back to the framework waveforms.
-            ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
-                    ", error=%" PRIu32 ").", static_cast<int64_t>(effect),
-                    static_cast<int32_t>(strength), static_cast<uint32_t>(status));
-        }
+static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                   jlong effect, jlong strength, jobject vibration) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorPerformEffect failed because controller was not initialized");
+        return -1;
     }
-
-    return -1;
+    aidl::Effect effectType = static_cast<aidl::Effect>(effect);
+    aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength);
+    jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
+    auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
+    auto result = controller->performEffect(effectType, effectStrength, callback);
+    return result.isOk() ? result.value().count() : -1;
 }
 
 static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
@@ -464,7 +252,7 @@
         {"vibratorOn", "(JJLcom/android/server/VibratorService$Vibration;)V", (void*)vibratorOn},
         {"vibratorOff", "(J)V", (void*)vibratorOff},
         {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
-        {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J",
+        {"vibratorPerformEffect", "(JJJLcom/android/server/VibratorService$Vibration;)J",
          (void*)vibratorPerformEffect},
         {"vibratorPerformComposedEffect",
          "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java
index 27a116c..1586a33 100644
--- a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java
+++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java
@@ -34,7 +34,8 @@
                         context,
                         com.android.internal.R.string.config_defaultSystemCaptionsManagerService),
                 /*disallowProperty=*/ null,
-                /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
+                /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER
+                    | PACKAGE_RESTART_POLICY_REFRESH_EAGER);
     }
 
     @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index 1cb004a..fdcadf3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -25,6 +25,7 @@
 import static android.location.Criteria.ACCURACY_FINE;
 import static android.location.Criteria.POWER_HIGH;
 import static android.location.LocationManager.PASSIVE_PROVIDER;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
 
 import static androidx.test.ext.truth.location.LocationSubject.assertThat;
 
@@ -907,6 +908,21 @@
         assertThat(mProvider.getRequest().interval).isEqualTo(5);
     }
 
+    @Test
+    public void testProviderRequest_BatterySaver_ScreenOnOff() {
+        mInjector.getLocationPowerSaveModeHelper().setLocationPowerSaveMode(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+
+        mInjector.getScreenInteractiveHelper().setScreenInteractive(false);
+        assertThat(mProvider.getRequest().reportLocation).isFalse();
+    }
+
     private ILocationListener createMockLocationListener() {
         return spy(new ILocationListener.Stub() {
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 7d6d90c..02d10e3 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -310,7 +309,7 @@
         verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_CLICK),
                 eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
-                any(VibratorService.Vibration.class), eq(false));
+                any(VibratorService.Vibration.class));
     }
 
     @Test
@@ -387,21 +386,26 @@
     }
 
     @Test
-    public void vibrate_withOneShotAndNativeCallbackNotTriggered_finishesVibrationViaFallback() {
+    public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() {
+        when(mNativeWrapperMock.vibratorGetSupportedEffects())
+                .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK});
+        doAnswer(invocation -> {
+            ((VibratorService.Vibration) invocation.getArgument(2)).onComplete();
+            return 10_000L; // 10s
+        }).when(mNativeWrapperMock).vibratorPerformEffect(
+                anyLong(), anyLong(), any(VibratorService.Vibration.class));
         VibratorService service = createService();
         Mockito.clearInvocations(mNativeWrapperMock);
 
-        vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
+        vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
 
-        verify(mNativeWrapperMock).vibratorOff();
-        verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
-        Mockito.clearInvocations(mNativeWrapperMock);
-
-        // Run the scheduled callback to finish one-shot vibration.
-        mTestLooper.moveTimeForward(200);
-        mTestLooper.dispatchAll();
-
-        verify(mNativeWrapperMock).vibratorOff();
+        InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect(
+                eq((long) VibrationEffect.EFFECT_CLICK),
+                eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
+                any(VibratorService.Vibration.class));
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
     }
 
     @Test
@@ -447,30 +451,6 @@
     }
 
     @Test
-    public void vibrate_withComposedAndNativeCallbackNotTriggered_finishesVibrationViaFallback() {
-        mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
-        VibratorService service = createService();
-        Mockito.clearInvocations(mNativeWrapperMock);
-
-        VibrationEffect effect = VibrationEffect.startComposition()
-                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
-                .compose();
-        vibrate(service, effect);
-
-        verify(mNativeWrapperMock).vibratorOff();
-        verify(mNativeWrapperMock).vibratorPerformComposedEffect(
-                any(VibrationEffect.Composition.PrimitiveEffect[].class),
-                any(VibratorService.Vibration.class));
-        Mockito.clearInvocations(mNativeWrapperMock);
-
-        // Run the scheduled callback to finish one-shot vibration.
-        mTestLooper.moveTimeForward(10000); // 10s
-        mTestLooper.dispatchAll();
-
-        verify(mNativeWrapperMock).vibratorOff();
-    }
-
-    @Test
     public void vibrate_whenBinderDies_cancelsVibration() {
         mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
         doAnswer(invocation -> {
@@ -574,15 +554,15 @@
 
         verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_CLICK),
-                eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(), anyBoolean());
+                eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any());
         verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_TICK),
-                eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any(), anyBoolean());
+                eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any());
         verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK),
-                eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any(), anyBoolean());
+                eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any());
         verify(mNativeWrapperMock, never()).vibratorPerformEffect(
-                eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any(), anyBoolean());
+                eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 80f145b..35d6f47 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -48,7 +48,7 @@
             public void sendPackageBroadcast(final String action, final String pkg,
                     final Bundle extras, final int flags, final String targetPkg,
                     final IIntentReceiver finishedReceiver, final int[] userIds,
-                    int[] instantUserIds, SparseArray<int[]> broadcastWhitelist) {
+                    int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
             }
 
             public void sendPackageAddedForNewUsers(String packageName,
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
index 9f9ec31..f96ebda 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
@@ -23,6 +23,8 @@
 
 import org.junit.Rule
 import org.junit.Test
+import org.junit.rules.Timeout
+import java.util.concurrent.TimeUnit
 
 /**
  * Collects APKs from the device and verifies that the new parsing behavior outputs
@@ -32,6 +34,9 @@
 class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
 
     @get:Rule
+    val timeout = Timeout(4, TimeUnit.MINUTES)
+
+    @get:Rule
     val expect = Expect.create()
 
     @Test
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 dcf3190..e5e9311 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -27,6 +27,9 @@
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.util.IndentingPrintWriter;
 
+import java.util.ArrayList;
+import java.util.List;
+
 class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
 
     private StrategyListener mListener;
@@ -41,6 +44,7 @@
     private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
     private boolean mHandleAutoTimeZoneConfigChangedCalled;
     private boolean mDumpCalled;
+    private final List<Dumpable> mDumpables = new ArrayList<>();
 
     @Override
     public void setStrategyListener(@NonNull StrategyListener listener) {
@@ -105,7 +109,7 @@
 
     @Override
     public void addDumpable(Dumpable dumpable) {
-        // Stubbed
+        mDumpables.add(dumpable);
     }
 
     @Override
@@ -149,4 +153,8 @@
     void verifyDumpCalled() {
         assertTrue(mDumpCalled);
     }
+
+    void verifyHasDumpable(Dumpable expected) {
+        assertTrue(mDumpables.contains(expected));
+    }
 }
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 0e2c227..e9d57e5 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -75,6 +75,16 @@
         mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
     }
 
+    @Test
+    public void testAddDumpable() throws Exception {
+        Dumpable stubbedDumpable = mock(Dumpable.class);
+
+        mTimeZoneDetectorInternal.addDumpable(stubbedDumpable);
+        mTestHandler.assertTotalMessagesEnqueued(0);
+
+        mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable);
+    }
+
     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 8034cac..3a1ec4f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -52,6 +52,7 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
 public class TimeZoneDetectorServiceTest {
@@ -253,6 +254,39 @@
     }
 
     @Test(expected = SecurityException.class)
+    public void testSuggestGeolocationTimeZone_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+        GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
+
+        try {
+            mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingOrSelfPermission(
+                    eq(android.Manifest.permission.SET_TIME_ZONE),
+                    anyString());
+        }
+    }
+
+    @Test
+    public void testSuggestGeolocationTimeZone() throws Exception {
+        doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+        GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
+
+        mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingOrSelfPermission(
+                eq(android.Manifest.permission.SET_TIME_ZONE),
+                anyString());
+
+        mTestHandler.waitForMessagesToBeProcessed();
+        mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
+    }
+
+    @Test(expected = SecurityException.class)
     public void testSuggestManualTimeZone_withoutPermission() {
         doThrow(new SecurityException("Mock"))
                 .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
@@ -346,7 +380,7 @@
     }
 
     @Test
-    public void testAutoTimeZoneDetectionChanged() throws Exception {
+    public void testHandleAutoTimeZoneConfigChanged() throws Exception {
         mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
         mTestHandler.assertTotalMessagesEnqueued(1);
         mTestHandler.waitForMessagesToBeProcessed();
@@ -370,10 +404,15 @@
     private static TimeZoneCapabilities createTimeZoneCapabilities() {
         return new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED)
                 .build();
     }
 
+    private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
+        return new GeolocationTimeZoneSuggestion(Arrays.asList("TestZoneId"));
+    }
+
     private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
         return new ManualTimeZoneSuggestion("TestZoneId");
     }
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 6855445..a6caa42 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -41,6 +41,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
@@ -56,10 +57,10 @@
 import org.junit.Test;
 
 import java.io.StringWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -93,16 +94,26 @@
                     TELEPHONY_SCORE_HIGHEST),
     };
 
-    private static final TimeZoneConfiguration CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED =
+    private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED =
             new TimeZoneConfiguration.Builder()
                     .setAutoDetectionEnabled(true)
                     .build();
 
-    private static final TimeZoneConfiguration CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED =
+    private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED =
             new TimeZoneConfiguration.Builder()
                     .setAutoDetectionEnabled(false)
                     .build();
 
+    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED =
+            new TimeZoneConfiguration.Builder()
+                    .setGeoDetectionEnabled(false)
+                    .build();
+
+    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED =
+            new TimeZoneConfiguration.Builder()
+                    .setGeoDetectionEnabled(true)
+                    .build();
+
     private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
     private FakeCallback mFakeCallback;
     private MockStrategyListener mMockStrategyListener;
@@ -120,7 +131,7 @@
     public void testGetCapabilities() {
         new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
         TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID);
         assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID));
     }
@@ -129,7 +140,7 @@
     public void testGetConfiguration() {
         new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
         TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID);
         assertTrue(expectedConfiguration.isComplete());
         assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID));
@@ -140,20 +151,22 @@
         Script script = new Script();
 
         script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                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_TIME_ZONE_DETECTION_DISABLED);
+                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());
         }
     }
@@ -163,20 +176,22 @@
         Script script = new Script();
 
         script.initializeUser(USER_ID, UserCase.RESTRICTED,
-                CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                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_TIME_ZONE_DETECTION_DISABLED);
+                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());
         }
     }
@@ -186,20 +201,22 @@
         Script script = new Script();
 
         script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                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_TIME_ZONE_DETECTION_DISABLED);
+                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());
         }
     }
@@ -208,42 +225,61 @@
     public void testUpdateConfiguration_unrestricted() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
 
         // Set the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+        script.simulateUpdateConfiguration(USER_ID, 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_TIME_ZONE_DETECTION_DISABLED);
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(
-                USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
+        script.verifyConfigurationChangedAndReset(USER_ID,
+                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
 
         // Update the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+        script.verifyConfigurationChangedAndReset(USER_ID,
+                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+
+        // Update the configuration to enable geolocation time zone detection.
+        script.simulateUpdateConfiguration(
+                USER_ID, 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));
     }
 
     @Test
     public void testUpdateConfiguration_restricted() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
 
         // Try to update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
+        script.simulateUpdateConfiguration(
+                USER_ID, 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_TIME_ZONE_DETECTION_ENABLED);
+        script.simulateUpdateConfiguration(
+                USER_ID, 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.
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_GEO_DETECTION_ENABLED,  false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
@@ -253,16 +289,18 @@
     public void testUpdateConfiguration_autoDetectNotSupported() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
 
         // Try to update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
+        script.simulateUpdateConfiguration(
+                USER_ID, 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_TIME_ZONE_DETECTION_ENABLED);
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
@@ -276,7 +314,7 @@
                 createEmptySlotIndex2Suggestion();
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion)
@@ -308,6 +346,10 @@
                 mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
     }
 
+    /**
+     * Telephony suggestions have quality metadata. Ordinarily, low scoring suggestions are not
+     * used, but this is not true if the device's time zone setting is uninitialized.
+     */
     @Test
     public void testTelephonySuggestionsWhenTimeZoneUninitialized() {
         assertTrue(TELEPHONY_SCORE_LOW < TELEPHONY_SCORE_USAGE_THRESHOLD);
@@ -319,7 +361,7 @@
 
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
 
         // A low quality suggestions will not be taken: The device time zone setting is left
         // uninitialized.
@@ -376,16 +418,16 @@
 
     /**
      * Confirms that toggling the auto time zone detection setting has the expected behavior when
-     * the strategy is "opinionated".
+     * the strategy is "opinionated" when using telephony auto detection.
      */
     @Test
-    public void testTogglingAutoTimeZoneDetection() {
+    public void testTogglingAutoDetection_autoTelephony() {
         Script script = new Script();
 
         for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
             // Start with the device in a known state.
             script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                    CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+                    CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
                     .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
             TelephonyTimeZoneSuggestion suggestion =
@@ -405,7 +447,8 @@
                     mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting on should cause the device setting to be set.
-            script.simulateAutoTimeZoneDetectionEnabled(USER_ID, true);
+            script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED,
+                    true /* expectedResult */);
 
             // When time zone detection is already enabled the suggestion (if it scores highly
             // enough) should be set immediately.
@@ -422,7 +465,8 @@
                     mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting should off should do nothing.
-            script.simulateAutoTimeZoneDetectionEnabled(USER_ID, false)
+            script.simulateUpdateConfiguration(
+                    USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
                     .verifyTimeZoneNotChanged();
 
             // Assert internal service state.
@@ -437,7 +481,7 @@
     public void testTelephonySuggestionsSingleSlotId() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
@@ -451,8 +495,7 @@
          */
 
         // Each test case will have the same or lower score than the last.
-        ArrayList<TelephonyTestCase> descendingCasesByScore =
-                new ArrayList<>(Arrays.asList(TELEPHONY_TEST_CASES));
+        List<TelephonyTestCase> descendingCasesByScore = list(TELEPHONY_TEST_CASES);
         Collections.reverse(descendingCasesByScore);
 
         for (TelephonyTestCase testCase : descendingCasesByScore) {
@@ -504,7 +547,7 @@
 
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_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.
@@ -583,15 +626,15 @@
     }
 
     /**
-     * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing
-     * the time zone is actually necessary. This test proves that the service doesn't assume it
-     * knows the current setting.
+     * 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 settings.
      */
     @Test
-    public void testTelephonySuggestionTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
+    public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
 
         TelephonyTestCase testCase = newTelephonyTestCase(
                 MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
@@ -609,26 +652,40 @@
 
         // 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.simulateAutoTimeZoneDetectionEnabled(USER_ID, false)
+        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
                 .verifyTimeZoneNotChanged()
-                .simulateAutoTimeZoneDetectionEnabled(USER_ID, true)
+                .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.simulateAutoTimeZoneDetectionEnabled(USER_ID, false)
+        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
                 .simulateTelephonyTimeZoneSuggestion(newYorkSuggestion)
                 .verifyTimeZoneNotChanged();
         // Latest suggestion should be used.
-        script.simulateAutoTimeZoneDetectionEnabled(USER_ID, true)
+        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(newYorkSuggestion);
     }
 
     @Test
-    public void testManualSuggestion_unrestricted_simulateAutoTimeZoneEnabled() {
+    public void testManualSuggestion_autoDetectionEnabled_autoTelephony() {
+        checkManualSuggestion_autoDetectionEnabled(false /* geoDetectionEnabled */);
+    }
+
+    @Test
+    public void testManualSuggestion_autoDetectionEnabled_autoGeo() {
+        checkManualSuggestion_autoDetectionEnabled(true /* geoDetectionEnabled */);
+    }
+
+    private void checkManualSuggestion_autoDetectionEnabled(boolean geoDetectionEnabled) {
+        TimeZoneConfiguration geoTzEnabledConfig =
+                new TimeZoneConfiguration.Builder()
+                        .setGeoDetectionEnabled(geoDetectionEnabled)
+                        .build();
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+                        CONFIG_AUTO_ENABLED.with(geoTzEnabledConfig))
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Auto time zone detection is enabled so the manual suggestion should be ignored.
@@ -641,7 +698,7 @@
     public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+                        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.
@@ -654,7 +711,7 @@
     public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+                        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.
@@ -668,7 +725,7 @@
     public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Auto time zone detection is disabled so the manual suggestion should be used.
@@ -682,7 +739,7 @@
     public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Restricted users do not have the capability.
@@ -696,7 +753,7 @@
     public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() {
         Script script = new Script()
                 .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Unrestricted users have the capability.
@@ -707,10 +764,261 @@
     }
 
     @Test
+    public void testGeoSuggestion_uncertain() {
+        Script script = new Script()
+                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+        GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeoLocationSuggestion();
+
+        script.simulateGeolocationTimeZoneSuggestion(uncertainSuggestion)
+                .verifyTimeZoneNotChanged();
+
+        // Assert internal service state.
+        assertEquals(uncertainSuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+    }
+
+    @Test
+    public void testGeoSuggestion_noZones() {
+        Script script = new Script()
+                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+        GeolocationTimeZoneSuggestion noZonesSuggestion = createGeoLocationSuggestion(list());
+
+        script.simulateGeolocationTimeZoneSuggestion(noZonesSuggestion)
+                .verifyTimeZoneNotChanged();
+
+        // Assert internal service state.
+        assertEquals(noZonesSuggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+    }
+
+    @Test
+    public void testGeoSuggestion_oneZone() {
+        GeolocationTimeZoneSuggestion suggestion =
+                createGeoLocationSuggestion(list("Europe/London"));
+
+        Script script = new Script()
+                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+        script.simulateGeolocationTimeZoneSuggestion(suggestion)
+                .verifyTimeZoneChangedAndReset("Europe/London");
+
+        // Assert internal service state.
+        assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+    }
+
+    /**
+     * In the current implementation, the first zone ID is always used unless the device is set to
+     * one of the other options. This is "stickiness" - the device favors the zone it is currently
+     * set to until that unambiguously can't be correct.
+     */
+    @Test
+    public void testGeoSuggestion_multiZone() {
+        GeolocationTimeZoneSuggestion londonOnlySuggestion =
+                createGeoLocationSuggestion(list("Europe/London"));
+        GeolocationTimeZoneSuggestion londonOrParisSuggestion =
+                createGeoLocationSuggestion(list("Europe/Paris", "Europe/London"));
+        GeolocationTimeZoneSuggestion parisOnlySuggestion =
+                createGeoLocationSuggestion(list("Europe/Paris"));
+
+        Script script = new Script()
+                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+        script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion)
+                .verifyTimeZoneChangedAndReset("Europe/London");
+        assertEquals(londonOnlySuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+
+        // Confirm bias towards the current device zone when there's multiple zones to choose from.
+        script.simulateGeolocationTimeZoneSuggestion(londonOrParisSuggestion)
+                .verifyTimeZoneNotChanged();
+        assertEquals(londonOrParisSuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+
+        script.simulateGeolocationTimeZoneSuggestion(parisOnlySuggestion)
+                .verifyTimeZoneChangedAndReset("Europe/Paris");
+        assertEquals(parisOnlySuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+
+        // Now the suggestion that previously left the device on Europe/London will leave the device
+        // on Europe/Paris.
+        script.simulateGeolocationTimeZoneSuggestion(londonOrParisSuggestion)
+                .verifyTimeZoneNotChanged();
+        assertEquals(londonOrParisSuggestion,
+                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 =
+                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))
+                .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 */)
+                .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();
+
+        // 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();
+
+        // Assert internal service state.
+        assertEquals(uncertainGeolocationSuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+    }
+
+    /**
+     * Confirms that changing the geolocation time zone detection enabled setting has the expected
+     * behavior, i.e. immediately recompute the detected time zone using different signals.
+     */
+    @Test
+    public void testChangingGeoDetectionEnabled() {
+        GeolocationTimeZoneSuggestion geolocationSuggestion =
+                createGeoLocationSuggestion(list("Europe/London"));
+        TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
+                SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+                "Europe/Paris");
+
+        Script script = new Script()
+                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+        // Add suggestions. Nothing should happen as time zone detection is disabled.
+        script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
+                .verifyTimeZoneNotChanged();
+        script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
+                .verifyTimeZoneNotChanged();
+
+        // Assert internal service state.
+        assertEquals(geolocationSuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+        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 */)
+                .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 */)
+                .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 */)
+                .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");
+    }
+
+    @Test
     public void testAddDumpable() {
         new Script()
                 .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         AtomicBoolean dumpCalled = new AtomicBoolean(false);
@@ -733,6 +1041,15 @@
         return new ManualTimeZoneSuggestion(zoneId);
     }
 
+    private static TelephonyTimeZoneSuggestion createTelephonySuggestion(
+            int slotIndex, @MatchType int matchType, @Quality int quality, String zoneId) {
+        return new TelephonyTimeZoneSuggestion.Builder(slotIndex)
+                .setMatchType(matchType)
+                .setQuality(quality)
+                .setZoneId(zoneId)
+                .build();
+    }
+
     private static TelephonyTimeZoneSuggestion createEmptySlotIndex1Suggestion() {
         return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX1).build();
     }
@@ -741,6 +1058,17 @@
         return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX2).build();
     }
 
+    private static GeolocationTimeZoneSuggestion createUncertainGeoLocationSuggestion() {
+        return createGeoLocationSuggestion(null);
+    }
+
+    private static GeolocationTimeZoneSuggestion createGeoLocationSuggestion(
+            @Nullable List<String> zoneIds) {
+        GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds);
+        suggestion.addDebugInfo("Test suggestion");
+        return suggestion;
+    }
+
     static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback {
 
         private TimeZoneCapabilities mCapabilities;
@@ -757,7 +1085,8 @@
                 TimeZoneConfiguration configuration) {
             assertEquals(userId, capabilities.getUserId());
             mCapabilities = capabilities;
-            assertTrue(configuration.isComplete());
+            assertTrue("Configuration must be complete when initializing, config=" + configuration,
+                    configuration.isComplete());
             mConfiguration.init(new UserConfiguration(userId, configuration));
         }
 
@@ -790,11 +1119,8 @@
             mConfiguration.set(new UserConfiguration(userId, newConfig));
 
             if (!newConfig.equals(oldConfig)) {
-                if (oldConfig.isAutoDetectionEnabled() != newConfig.isAutoDetectionEnabled()) {
-                    // Simulate what happens when the auto detection enabled configuration is
-                    // changed.
-                    mStrategy.handleAutoTimeZoneConfigChanged();
-                }
+                // Simulate what happens when the auto detection configuration is changed.
+                mStrategy.handleAutoTimeZoneConfigChanged();
             }
         }
 
@@ -804,6 +1130,11 @@
         }
 
         @Override
+        public boolean isGeoDetectionEnabled() {
+            return mConfiguration.getLatest().configuration.isGeoDetectionEnabled();
+        }
+
+        @Override
         public boolean isDeviceTimeZoneInitialized() {
             return mTimeZoneId.getLatest() != null;
         }
@@ -942,32 +1273,33 @@
      * supplied configuration.
      */
     private static TimeZoneCapabilities createCapabilities(
-            int userId, UserCase userRole, TimeZoneConfiguration configuration) {
-        switch (userRole) {
+            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(userRole + " not recognized");
+                throw new AssertionError(userCase + " not recognized");
         }
     }
 
@@ -978,8 +1310,8 @@
     private class Script {
 
         Script initializeUser(
-                @UserIdInt int userId, UserCase userRole, TimeZoneConfiguration configuration) {
-            TimeZoneCapabilities capabilities = createCapabilities(userId, userRole, configuration);
+                @UserIdInt int userId, UserCase userCase, TimeZoneConfiguration configuration) {
+            TimeZoneCapabilities capabilities = createCapabilities(userId, userCase, configuration);
             mFakeCallback.initializeUser(userId, capabilities, configuration);
             return this;
         }
@@ -989,27 +1321,24 @@
             return this;
         }
 
-        Script simulateAutoTimeZoneDetectionEnabled(@UserIdInt int userId, boolean enabled) {
-            TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder()
-                    .setAutoDetectionEnabled(enabled)
-                    .build();
-            return simulateUpdateConfiguration(userId, configuration);
-        }
-
         /**
-         * Simulates the time zone detection strategy receiving an updated configuration.
+         * Simulates the time zone detection strategy receiving an updated configuration and checks
+         * the return value.
          */
         Script simulateUpdateConfiguration(
-                @UserIdInt int userId, TimeZoneConfiguration configuration) {
-            mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration);
+                @UserIdInt int userId, TimeZoneConfiguration configuration,
+                boolean expectedResult) {
+            assertEquals(expectedResult,
+                    mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration));
             return this;
         }
 
         /**
-         * Simulates the time zone detection strategy receiving a telephony-originated suggestion.
+         * Simulates the time zone detection strategy receiving a geolocation-originated
+         * suggestion.
          */
-        Script simulateTelephonyTimeZoneSuggestion(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
-            mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion);
+        Script simulateGeolocationTimeZoneSuggestion(GeolocationTimeZoneSuggestion suggestion) {
+            mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(suggestion);
             return this;
         }
 
@@ -1024,13 +1353,26 @@
             return this;
         }
 
+        /**
+         * Simulates the time zone detection strategy receiving a telephony-originated suggestion.
+         */
+        Script simulateTelephonyTimeZoneSuggestion(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+            mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion);
+            return this;
+        }
+
+        /**
+         * Confirms that the device's time zone has not been set by previous actions since the test
+         * state was last reset.
+         */
         Script verifyTimeZoneNotChanged() {
             mFakeCallback.assertTimeZoneNotChanged();
             return this;
         }
 
-        Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
-            mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
+        /** Verifies the device's time zone has been set and clears change tracking history. */
+        Script verifyTimeZoneChangedAndReset(String zoneId) {
+            mFakeCallback.assertTimeZoneChangedTo(zoneId);
             mFakeCallback.commitAllChanges();
             return this;
         }
@@ -1041,6 +1383,12 @@
             return this;
         }
 
+        Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
+            mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
+            mFakeCallback.commitAllChanges();
+            return this;
+        }
+
         /**
          * Verifies that the configuration has been changed to the expected value.
          */
@@ -1120,4 +1468,8 @@
             mOnConfigurationChangedCalled = false;
         }
     }
+
+    private static <T> List<T> list(T... values) {
+        return Arrays.asList(values);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
deleted file mode 100644
index f6c854e..0000000
--- a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2018 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.slice;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.server.UiServiceTestCase;
-import com.android.server.slice.SliceManagerService.PackageMatchingCache;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.function.Supplier;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class PackageMatchingCacheTest extends UiServiceTestCase {
-
-    private final Supplier<String> supplier = mock(Supplier.class);
-    private final PackageMatchingCache cache = new PackageMatchingCache(supplier);
-
-    @Test
-    public void testNulls() {
-        // Doesn't get for a null input
-        cache.matches(null);
-        verify(supplier, never()).get();
-
-        // Gets once valid input in sent.
-        cache.matches("");
-        verify(supplier).get();
-    }
-
-    @Test
-    public void testCaching() {
-        when(supplier.get()).thenReturn("ret.pkg");
-
-        assertTrue(cache.matches("ret.pkg"));
-        assertTrue(cache.matches("ret.pkg"));
-        assertTrue(cache.matches("ret.pkg"));
-
-        verify(supplier, times(1)).get();
-    }
-
-    @Test
-    public void testGetOnFailure() {
-        when(supplier.get()).thenReturn("ret.pkg");
-        assertTrue(cache.matches("ret.pkg"));
-
-        when(supplier.get()).thenReturn("other.pkg");
-        assertTrue(cache.matches("other.pkg"));
-        verify(supplier, times(2)).get();
-    }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index a443695..cf1c36c 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -90,8 +90,6 @@
 
     @Test
     public void testAddPinCreatesPinned() throws RemoteException {
-        doReturn("pkg").when(mService).getDefaultHome(anyInt());
-
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
@@ -99,8 +97,6 @@
 
     @Test
     public void testRemovePinDestroysPinned() throws RemoteException {
-        doReturn("pkg").when(mService).getDefaultHome(anyInt());
-
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
 
         when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false);
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index b3d75d3..4ca5b9e 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -2,15 +2,37 @@
 // Build WmTests package
 //########################################################################
 
+// Include all test java files.
+filegroup {
+    name: "wmtests-sources",
+    srcs: [
+        "src/**/*.java",
+    ],
+}
+
+genrule {
+    name: "wmtests.protologsrc",
+    srcs: [
+        ":protolog-groups",
+        ":wmtests-sources",
+    ],
+    tools: ["protologtool"],
+    cmd: "$(location protologtool) transform-protolog-calls " +
+      "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+      "--protolog-impl-class com.android.internal.protolog.ProtoLogImpl " +
+      "--protolog-cache-class 'com.android.server.wm.ProtoLogCache' " +
+      "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
+      "--loggroups-jar $(location :protolog-groups) " +
+      "--output-srcjar $(out) " +
+      "$(locations :wmtests-sources)",
+    out: ["wmtests.protolog.srcjar"],
+}
+
 android_test {
     name: "WmTests",
 
     // We only want this apk build for tests.
-
-    // Include all test java files.
-    srcs: [
-        "src/**/*.java",
-    ],
+    srcs: [":wmtests.protologsrc"],
 
     static_libs: [
         "frameworks-base-testutils",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
index d1510cf..82230242 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
@@ -25,9 +25,12 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.protolog.ProtoLogImpl;
+import com.android.internal.protolog.ProtoLogGroup;
+import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.common.ProtoLog;
 
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -35,6 +38,7 @@
  */
 @SmallTest
 @Presubmit
+@Ignore("b/163095037")
 public class ProtoLogIntegrationTest {
     @After
     public void tearDown() {
@@ -44,17 +48,21 @@
     @Test
     public void testProtoLogToolIntegration() {
         ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
-        runWith(mockedProtoLog, () -> {
-            ProtoLogGroup.testProtoLog();
-        });
+        runWith(mockedProtoLog, this::testProtoLog);
         verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.ERROR), eq(ProtoLogGroup.TEST_GROUP),
                 anyInt(), eq(0b0010101001010111),
-                eq(ProtoLogGroup.TEST_GROUP.isLogToLogcat()
+                eq(com.android.internal.protolog.ProtoLogGroup.TEST_GROUP.isLogToLogcat()
                         ? "Test completed successfully: %b %d %o %x %e %g %f %% %s"
                         : null),
                 eq(new Object[]{true, 1L, 2L, 3L, 0.4, 0.5, 0.6, "ok"}));
     }
 
+    private void testProtoLog() {
+        ProtoLog.e(ProtoLogGroup.TEST_GROUP,
+                "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
+                true, 1, 2, 3, 0.4, 0.5, 0.6, "ok");
+    }
+
     /**
      * Starts protolog for the duration of {@code runnable}, with a ProtoLogImpl instance installed.
      */
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index e233fed..9da17db 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -11,6 +11,7 @@
         "androidx.test.rules",
         "mockito-target-minus-junit4",
         "truth-prebuilt",
+        "platform-test-annotations",
     ],
     java_resource_dirs: ["res"],
     certificate: "platform",
diff --git a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java
rename to tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
index 3e9f625..3db0116 100644
--- a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog;
+package com.android.internal.protolog;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.server.protolog.ProtoLogImpl.PROTOLOG_VERSION;
+import static com.android.internal.protolog.ProtoLogImpl.PROTOLOG_VERSION;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -42,7 +42,7 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.IProtoLogGroup;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java
rename to tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
index 0254055..ae50216 100644
--- a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog;
+package com.android.internal.protolog;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
diff --git a/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java b/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java
rename to tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java
index 4c7f5fd..e20ca3d 100644
--- a/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.internal.protolog.common;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index ce551bd..0be80d3 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -2,9 +2,9 @@
     name: "protologtool-lib",
     srcs: [
         "src/com/android/protolog/tool/**/*.kt",
+        ":protolog-common-src",
     ],
     static_libs: [
-        "protolog-common",
         "javaparser",
         "platformprotos",
         "jsonlib",
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogParser.kt b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
index a59038f..645c567 100644
--- a/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
@@ -16,16 +16,15 @@
 
 package com.android.protolog.tool
 
+import com.android.internal.protolog.ProtoLogFileProto
+import com.android.internal.protolog.ProtoLogMessage
+import com.android.internal.protolog.common.InvalidFormatStringException
+import com.android.internal.protolog.common.LogDataType
 import com.android.json.stream.JsonReader
-import com.android.server.protolog.common.InvalidFormatStringException
-import com.android.server.protolog.common.LogDataType
-import com.android.server.protolog.ProtoLogMessage
-import com.android.server.protolog.ProtoLogFileProto
 import java.io.BufferedReader
 import java.io.InputStream
 import java.io.InputStreamReader
 import java.io.PrintStream
-import java.lang.Exception
 import java.text.SimpleDateFormat
 import java.util.Date
 import java.util.Locale
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
index 75493b6..42b628b 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
@@ -17,7 +17,7 @@
 package com.android.protolog.tool
 
 import com.android.protolog.tool.Constants.ENUM_VALUES_METHOD
-import com.android.server.protolog.common.IProtoLogGroup
+import com.android.internal.protolog.common.IProtoLogGroup
 import java.io.File
 import java.net.URLClassLoader
 
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index 36ea411..27e61a1 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -16,7 +16,7 @@
 
 package com.android.protolog.tool
 
-import com.android.server.protolog.common.LogDataType
+import com.android.internal.protolog.common.LogDataType
 import com.github.javaparser.StaticJavaParser
 import com.github.javaparser.ast.CompilationUnit
 import com.github.javaparser.ast.NodeList
@@ -89,7 +89,7 @@
             // Out: ProtoLog.e(GROUP, 1234, 0, null, arg)
             newCall.arguments.add(2, IntegerLiteralExpr(typeMask))
             // Replace call to a stub method with an actual implementation.
-            // Out: com.android.server.protolog.ProtoLogImpl.e(GROUP, 1234, null, arg)
+            // Out: ProtoLogImpl.e(GROUP, 1234, null, arg)
             newCall.setScope(protoLogImplClassNode)
             // Create a call to ProtoLog$Cache.GROUP_enabled
             // Out: com.android.server.protolog.ProtoLog$Cache.GROUP_enabled
@@ -119,9 +119,9 @@
             }
             blockStmt.addStatement(ExpressionStmt(newCall))
             // Create an IF-statement with the previously created condition.
-            // Out: if (com.android.server.protolog.ProtoLogImpl.isEnabled(GROUP)) {
+            // Out: if (ProtoLogImpl.isEnabled(GROUP)) {
             //          long protoLogParam0 = arg;
-            //          com.android.server.protolog.ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0);
+            //          ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0);
             //      }
             ifStmt = IfStmt(isLogEnabled, blockStmt, null)
         } else {
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
index cf36651c..3cfbb43 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
@@ -31,7 +31,7 @@
         private const val TEST_PROTOLOG_CLASS = "com.android.server.wm.ProtoLog"
         private const val TEST_PROTOLOGIMPL_CLASS = "com.android.server.wm.ProtoLogImpl"
         private const val TEST_PROTOLOGCACHE_CLASS = "com.android.server.wm.ProtoLog\$Cache"
-        private const val TEST_PROTOLOGGROUP_CLASS = "com.android.server.wm.ProtoLogGroup"
+        private const val TEST_PROTOLOGGROUP_CLASS = "com.android.internal.protolog.ProtoLogGroup"
         private const val TEST_PROTOLOGGROUP_JAR = "out/soong/.intermediates/frameworks/base/" +
                 "services/core/services.core.wm.protologgroups/android_common/javac/" +
                 "services.core.wm.protologgroups.jar"
diff --git a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
index dd8a0b1..0d2b91d 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
@@ -33,8 +33,8 @@
         val output = run(
                 src = "frameworks/base/org/example/Example.java" to """
                     package org.example;
-                    import com.android.server.protolog.common.ProtoLog;
-                    import static com.android.server.wm.ProtoLogGroup.GROUP;
+                    import com.android.internal.protolog.common.ProtoLog;
+                    import static com.android.internal.protolog.ProtoLogGroup.GROUP;
 
                     class Example {
                         void method() {
@@ -46,11 +46,11 @@
                 """.trimIndent(),
                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
                 commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
-                        "--protolog-class", "com.android.server.protolog.common.ProtoLog",
-                        "--protolog-impl-class", "com.android.server.protolog.ProtoLogImpl",
+                        "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
+                        "--protolog-impl-class", "com.android.internal.protolog.ProtoLogImpl",
                         "--protolog-cache-class",
-                        "com.android.server.protolog.ProtoLog${"\$\$"}Cache",
-                        "--loggroups-class", "com.android.server.wm.ProtoLogGroup",
+                        "com.android.server.wm.ProtoLogCache",
+                        "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
                         "--loggroups-jar", "not_required.jar",
                         "--output-srcjar", "out.srcjar",
                         "frameworks/base/org/example/Example.java"))
@@ -64,8 +64,8 @@
         val output = run(
                 src = "frameworks/base/org/example/Example.java" to """
                     package org.example;
-                    import com.android.server.protolog.common.ProtoLog;
-                    import static com.android.server.wm.ProtoLogGroup.GROUP;
+                    import com.android.internal.protolog.common.ProtoLog;
+                    import static com.android.internal.protolog.ProtoLogGroup.GROUP;
 
                     class Example {
                         void method() {
@@ -77,8 +77,8 @@
                 """.trimIndent(),
                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
                 commandOptions = CommandOptions(arrayOf("generate-viewer-config",
-                        "--protolog-class", "com.android.server.protolog.common.ProtoLog",
-                        "--loggroups-class", "com.android.server.wm.ProtoLogGroup",
+                        "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
+                        "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
                         "--loggroups-jar", "not_required.jar",
                         "--viewer-conf", "out.json",
                         "frameworks/base/org/example/Example.java"))
diff --git a/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
index 04a3bfa..67a31da 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
@@ -17,8 +17,8 @@
 package com.android.protolog.tool
 
 import com.android.json.stream.JsonReader
-import com.android.server.protolog.ProtoLogMessage
-import com.android.server.protolog.ProtoLogFileProto
+import com.android.internal.protolog.ProtoLogMessage
+import com.android.internal.protolog.ProtoLogFileProto
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index 53c69c4..eff64a3 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -326,6 +326,8 @@
     field @Deprecated public static final int METERED_OVERRIDE_METERED = 1; // 0x1
     field @Deprecated public static final int METERED_OVERRIDE_NONE = 0; // 0x0
     field @Deprecated public static final int METERED_OVERRIDE_NOT_METERED = 2; // 0x2
+    field @Deprecated public static final int RANDOMIZATION_AUTO = 3; // 0x3
+    field @Deprecated public static final int RANDOMIZATION_ENHANCED = 2; // 0x2
     field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
     field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
     field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 71f0ab8..1588bf7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1130,7 +1130,9 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"RANDOMIZATION_"}, value = {
             RANDOMIZATION_NONE,
-            RANDOMIZATION_PERSISTENT})
+            RANDOMIZATION_PERSISTENT,
+            RANDOMIZATION_ENHANCED,
+            RANDOMIZATION_AUTO})
     public @interface MacRandomizationSetting {}
 
     /**
@@ -1147,14 +1149,30 @@
     public static final int RANDOMIZATION_PERSISTENT = 1;
 
     /**
+     * Use a randomly generated MAC address for connections to this network.
+     * This option does not persist the randomized MAC address.
+     * @hide
+     */
+    @SystemApi
+    public static final int RANDOMIZATION_ENHANCED = 2;
+
+    /**
+     * Let the wifi framework automatically decide the MAC randomization strategy.
+     * @hide
+     */
+    @SystemApi
+    public static final int RANDOMIZATION_AUTO = 3;
+
+    /**
      * Level of MAC randomization for this network.
-     * One of {@link #RANDOMIZATION_NONE} or {@link #RANDOMIZATION_PERSISTENT}.
-     * By default this field is set to {@link #RANDOMIZATION_PERSISTENT}.
+     * One of {@link #RANDOMIZATION_NONE}, {@link #RANDOMIZATION_AUTO},
+     * {@link #RANDOMIZATION_PERSISTENT} or {@link #RANDOMIZATION_ENHANCED}.
+     * By default this field is set to {@link #RANDOMIZATION_AUTO}.
      * @hide
      */
     @SystemApi
     @MacRandomizationSetting
-    public int macRandomizationSetting = RANDOMIZATION_PERSISTENT;
+    public int macRandomizationSetting = RANDOMIZATION_AUTO;
 
     /**
      * @hide