Merge "Fix incorrect use of UserHandle#getUserHandleForUid(int uid)"
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/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp
index 9f795a7..c40e025 100644
--- a/apct-tests/perftests/textclassifier/Android.bp
+++ b/apct-tests/perftests/textclassifier/Android.bp
@@ -19,7 +19,7 @@
         "androidx.test.rules",
         "androidx.annotation_annotation",
         "apct-perftests-utils",
-        "collector-device-lib-platform",
+        "collector-device-lib",
     ],
     data: [":perfetto_artifacts"],
     platform_apis: true,
diff --git a/api/current.txt b/api/current.txt
index 09de005..17fbb55 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11900,6 +11900,7 @@
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
@@ -24064,6 +24065,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 3f3b8ea..b587ea1 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -39,6 +39,14 @@
 
 }
 
+package android.media.session {
+
+  public final class MediaSession {
+    field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000
+  }
+
+}
+
 package android.net {
 
   public final class TetheringConstants {
@@ -86,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 56059dd..b011e9a 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7141,6 +7141,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
@@ -10489,7 +10491,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle);
     method @Deprecated public android.content.ComponentName getDefaultPhoneApp();
-    method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
+    method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
@@ -12732,6 +12734,9 @@
   public interface PacProcessor {
     method @Nullable public String findProxyForUrl(@NonNull String);
     method @NonNull public static android.webkit.PacProcessor getInstance();
+    method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long);
+    method public default long getNetworkHandle();
+    method public default void releasePacProcessor();
     method public boolean setProxyScript(@NonNull String);
   }
 
@@ -12871,6 +12876,7 @@
     method public android.webkit.CookieManager getCookieManager();
     method public android.webkit.GeolocationPermissions getGeolocationPermissions();
     method @NonNull public default android.webkit.PacProcessor getPacProcessor();
+    method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long);
     method public android.webkit.ServiceWorkerController getServiceWorkerController();
     method public android.webkit.WebViewFactoryProvider.Statics getStatics();
     method @Deprecated public android.webkit.TokenBindingService getTokenBindingService();
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index ed717c4..4b7eda0 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
 import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupProgress;
 import android.app.backup.BackupTransport;
@@ -666,7 +667,7 @@
 
         // The rest of the 'list' options work with a restore session on the current transport
         try {
-            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
             if (mRestore == null) {
                 System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
                 return;
@@ -821,7 +822,7 @@
 
         try {
             boolean didRestore = false;
-            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
             if (mRestore == null) {
                 System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
                 return;
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 36ff20f..9c79612 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -417,7 +417,7 @@
     // this guest property specifies multi-display IDs to show the boot animation
     // multiple ids can be set with comma (,) as separator, for example:
     // setprop persist.boot.animation.displays 19260422155234049,19261083906282754
-    Vector<uint64_t> physicalDisplayIds;
+    Vector<PhysicalDisplayId> physicalDisplayIds;
     char displayValue[PROPERTY_VALUE_MAX] = "";
     property_get(DISPLAYS_PROP_NAME, displayValue, "");
     bool isValid = displayValue[0] != '\0';
@@ -435,7 +435,7 @@
     }
     if (isValid) {
         std::istringstream stream(displayValue);
-        for (PhysicalDisplayId id; stream >> id; ) {
+        for (PhysicalDisplayId id; stream >> id.value; ) {
             physicalDisplayIds.add(id);
             if (stream.peek() == ',')
                 stream.ignore();
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index a6c402c..15e22a3 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -166,7 +166,7 @@
 Status Idmap2Service::createIdmap(const std::string& target_apk_path,
                                   const std::string& overlay_apk_path, int32_t fulfilled_policies,
                                   bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
-                                  aidl::nullable<std::string>* _aidl_return) {
+                                  std::optional<std::string>* _aidl_return) {
   assert(_aidl_return);
   SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
   _aidl_return->reset();
@@ -214,7 +214,7 @@
     return error("failed to write to idmap path " + idmap_path);
   }
 
-  *_aidl_return = aidl::make_nullable<std::string>(idmap_path);
+  *_aidl_return = idmap_path;
   return ok();
 }
 
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index ea931c9..1a44519 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -21,7 +21,6 @@
 
 #include <android-base/unique_fd.h>
 #include <binder/BinderService.h>
-#include <binder/Nullable.h>
 
 #include "android/os/BnIdmap2.h"
 
@@ -47,7 +46,7 @@
   binder::Status createIdmap(const std::string& target_apk_path,
                              const std::string& overlay_apk_path, int32_t fulfilled_policies,
                              bool enforce_overlayable, int32_t user_id,
-                             aidl::nullable<std::string>* _aidl_return) override;
+                             std::optional<std::string>* _aidl_return) override;
 
  private:
   // Cache the crc of the android framework package since the crc cannot change without a reboot.
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index a46a54c..dec4a56 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -51,12 +51,11 @@
             "usage: %s [-hp] [-d display-id] [FILENAME]\n"
             "   -h: this message\n"
             "   -p: save the file as a png.\n"
-            "   -d: specify the physical display ID to capture (default: %"
-                    ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ")\n"
+            "   -d: specify the physical display ID to capture (default: %s)\n"
             "       see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
             "If FILENAME ends with .png it will be saved as a png.\n"
             "If FILENAME is not given, the results will be printed to stdout.\n",
-            pname, displayId);
+            pname, to_string(displayId).c_str());
 }
 
 static int32_t flinger2bitmapFormat(PixelFormat f)
@@ -137,7 +136,7 @@
                 png = true;
                 break;
             case 'd':
-                displayId = atoll(optarg);
+                displayId = PhysicalDisplayId(atoll(optarg));
                 break;
             case '?':
             case 'h':
@@ -183,7 +182,7 @@
     ProcessState::self()->startThreadPool();
 
     ScreenCaptureResults captureResults;
-    status_t result = ScreenshotClient::captureDisplay(*displayId, captureResults);
+    status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults);
     if (result != NO_ERROR) {
         close(fd);
         return 1;
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/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blocked.txt
similarity index 100%
rename from config/hiddenapi-force-blacklist.txt
rename to config/hiddenapi-force-blocked.txt
diff --git a/config/hiddenapi-greylist-max-o.txt b/config/hiddenapi-max-target-o.txt
similarity index 100%
rename from config/hiddenapi-greylist-max-o.txt
rename to config/hiddenapi-max-target-o.txt
diff --git a/config/hiddenapi-greylist-max-p.txt b/config/hiddenapi-max-target-p.txt
similarity index 100%
rename from config/hiddenapi-greylist-max-p.txt
rename to config/hiddenapi-max-target-p.txt
diff --git a/config/hiddenapi-greylist-max-q.txt b/config/hiddenapi-max-target-q.txt
similarity index 100%
rename from config/hiddenapi-greylist-max-q.txt
rename to config/hiddenapi-max-target-q.txt
diff --git a/config/hiddenapi-greylist-packages.txt b/config/hiddenapi-unsupported-packages.txt
similarity index 100%
rename from config/hiddenapi-greylist-packages.txt
rename to config/hiddenapi-unsupported-packages.txt
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-unsupported.txt
similarity index 100%
rename from config/hiddenapi-greylist.txt
rename to config/hiddenapi-unsupported.txt
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index ca22bf4..d334de6 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -364,6 +364,18 @@
      */
     public static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x0001000;
 
+    /**
+     * This flag requests that when when {@link #FLAG_REQUEST_MULTI_FINGER_GESTURES} is enabled,
+     * two-finger passthrough gestures are re-enabled. Two-finger swipe gestures are not detected,
+     * but instead passed through as one-finger gestures. In addition, three-finger swipes from the
+     * bottom of the screen are not detected, and instead are passed through unchanged. If {@link
+     * #FLAG_REQUEST_MULTI_FINGER_GESTURES} is disabled this flag has no effect.
+     *
+     * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
+     * @hide
+     */
+    public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000;
+
     /** {@hide} */
     public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
 
@@ -624,6 +636,7 @@
                     0);
             flags = asAttributes.getInt(
                     com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
+            flags |= FLAG_REQUEST_2_FINGER_PASSTHROUGH;
             mSettingsActivityName = asAttributes.getString(
                     com.android.internal.R.styleable.AccessibilityService_settingsActivity);
             if (asAttributes.getBoolean(com.android.internal.R.styleable
@@ -1261,6 +1274,8 @@
                 return "FLAG_SERVICE_HANDLES_DOUBLE_TAP";
             case FLAG_REQUEST_MULTI_FINGER_GESTURES:
                 return "FLAG_REQUEST_MULTI_FINGER_GESTURES";
+            case FLAG_REQUEST_2_FINGER_PASSTHROUGH:
+                return "FLAG_REQUEST_2_FINGER_PASSTHROUGH";
             case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
                 return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
             case FLAG_REPORT_VIEW_IDS:
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2be9282..5c4951e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2880,7 +2880,7 @@
      * Return the number of actions that will be displayed in the picture-in-picture UI when the
      * user interacts with the activity currently in picture-in-picture mode. This number may change
      * if the global configuration changes (ie. if the device is plugged into an external display),
-     * but will always be larger than three.
+     * but will always be at least three.
      */
     public int getMaxNumPictureInPictureActions() {
         try {
@@ -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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9b13d25..7cec717 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -37,6 +37,7 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.backup.BackupManager;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
 import android.app.servertransaction.ActivityRelaunchItem;
@@ -289,6 +290,8 @@
 
     private final Object mNetworkPolicyLock = new Object();
 
+    private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent";
+
     /**
      * Denotes the sequence number of the process state change for which the main thread needs
      * to block until the network rules are updated for it.
@@ -737,6 +740,7 @@
         CompatibilityInfo compatInfo;
         int backupMode;
         int userId;
+        int operationType;
         public String toString() {
             return "CreateBackupAgentData{appInfo=" + appInfo
                     + " backupAgent=" + appInfo.backupAgentName
@@ -957,12 +961,13 @@
         }
 
         public final void scheduleCreateBackupAgent(ApplicationInfo app,
-                CompatibilityInfo compatInfo, int backupMode, int userId) {
+                CompatibilityInfo compatInfo, int backupMode, int userId, int operationType) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
             d.compatInfo = compatInfo;
             d.backupMode = backupMode;
             d.userId = userId;
+            d.operationType = operationType;
 
             sendMessage(H.CREATE_BACKUP_AGENT, d);
         }
@@ -4075,12 +4080,7 @@
             return;
         }
 
-        String classname = data.appInfo.backupAgentName;
-        // full backup operation but no app-supplied agent?  use the default implementation
-        if (classname == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
-                || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
-            classname = "android.app.backup.FullBackupAgent";
-        }
+        String classname = getBackupAgentName(data);
 
         try {
             IBinder binder = null;
@@ -4104,7 +4104,7 @@
                     context.setOuterContext(agent);
                     agent.attach(context);
 
-                    agent.onCreate(UserHandle.of(data.userId));
+                    agent.onCreate(UserHandle.of(data.userId), data.operationType);
                     binder = agent.onBind();
                     backupAgents.put(packageName, agent);
                 } catch (Exception e) {
@@ -4132,6 +4132,23 @@
         }
     }
 
+    private String getBackupAgentName(CreateBackupAgentData data) {
+        String agentName = data.appInfo.backupAgentName;
+        if (!UserHandle.isCore(data.appInfo.uid)
+                && data.operationType == BackupManager.OperationType.MIGRATION) {
+            // If this is a migration, use the default backup agent regardless of the app's
+            // preferences.
+            agentName = DEFAULT_FULL_BACKUP_AGENT;
+        } else {
+            // full backup operation but no app-supplied agent?  use the default implementation
+            if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+                    || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
+                agentName = DEFAULT_FULL_BACKUP_AGENT;
+            }
+        }
+        return agentName;
+    }
+
     // Tear down a BackupAgent
     private void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
@@ -5111,6 +5128,7 @@
                 }
             }
             r.setState(ON_DESTROY);
+            mLastReportedWindowingMode.remove(r.activity.getActivityToken());
         }
         schedulePurgeIdler();
         // updatePendingActivityConfiguration() reads from mActivities to update
@@ -5353,16 +5371,8 @@
             throw e.rethrowFromSystemServer();
         }
 
-        // Save the current windowing mode to be restored and compared to the new configuration's
-        // windowing mode (needed because we update the last reported windowing mode when launching
-        // an activity and we can't tell inside performLaunchActivity whether we are relaunching)
-        final int oldWindowingMode = mLastReportedWindowingMode.getOrDefault(
-                r.activity.getActivityToken(), WINDOWING_MODE_UNDEFINED);
         handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
                 pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
-        mLastReportedWindowingMode.put(r.activity.getActivityToken(), oldWindowingMode);
-        handleWindowingModeChangeIfNeeded(r.activity, r.activity.mCurrentConfig);
-
         if (pendingActions != null) {
             // Only report a successful relaunch to WindowManager.
             pendingActions.setReportRelaunchToWindowManager(true);
@@ -5628,10 +5638,6 @@
             throw new IllegalArgumentException("Activity token not set. Is the activity attached?");
         }
 
-        // multi-window / pip mode changes, if any, should be sent before the configuration change
-        // callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
-        handleWindowingModeChangeIfNeeded(activity, newConfig);
-
         final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
         boolean shouldReportChange = false;
         if (activity.mCurrentConfig == null) {
@@ -5685,6 +5691,11 @@
         }
 
         if (shouldReportChange) {
+            // multi-window / pip mode changes, if any, should be sent before the configuration
+            // change callback, see also
+            // PinnedStackTests#testConfigurationChangeOrderDuringTransition
+            handleWindowingModeChangeIfNeeded(activity, newConfig);
+
             activity.mCalled = false;
             activity.onConfigurationChanged(configToReport);
             if (!activity.mCalled) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b40dd00..95136bb 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2657,8 +2657,10 @@
      * @hide
      */
     // TODO: this should probably be @SystemApi as well
-    public static @NonNull String toReceiverId(@NonNull Object obj) {
-        if (obj instanceof PendingIntent) {
+    public static @NonNull String toReceiverId(@Nullable Object obj) {
+        if (obj == null) {
+            return "null";
+        } else if (obj instanceof PendingIntent) {
             return toReceiverId((PendingIntent) obj);
         } else {
             return obj.getClass().getName() + "@" + System.identityHashCode(obj);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9613e58..f6b5334 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1624,8 +1624,9 @@
         }
         try {
             final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
-                    mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
-                    filter, broadcastPermission, userId, flags);
+                    mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
+                    AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
+                    flags);
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
                 intent.prepareToEnterProcess();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3b6a7b8..93dfc79 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -123,8 +123,8 @@
             in IIntentReceiver receiver, in IntentFilter filter,
             in String requiredPermission, int userId, int flags);
     Intent registerReceiverWithFeature(in IApplicationThread caller, in String callerPackage,
-            in String callingFeatureId, in IIntentReceiver receiver, in IntentFilter filter,
-            in String requiredPermission, int userId, int flags);
+            in String callingFeatureId, in String receiverId, in IIntentReceiver receiver,
+            in IntentFilter filter, in String requiredPermission, int userId, int flags);
     @UnsupportedAppUsage
     void unregisterReceiver(in IIntentReceiver receiver);
     /** @deprecated Use {@link #broadcastIntentWithFeature} instead */
@@ -288,7 +288,8 @@
     void stopAppSwitches();
     @UnsupportedAppUsage
     void resumeAppSwitches();
-    boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId);
+    boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId,
+            int operationType);
     void backupAgentCreated(in String packageName, in IBinder agent, int userId);
     void unbindBackupAgent(in ApplicationInfo appInfo);
     int getUidForIntentSender(in IIntentSender sender);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 6e9157e..24da504 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -95,7 +95,7 @@
     void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
     void setSchedulingGroup(int group);
     void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo,
-            int backupMode, int userId);
+            int backupMode, int userId, int operationType);
     void scheduleDestroyBackupAgent(in ApplicationInfo app,
             in CompatibilityInfo compatInfo, int userId);
     void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
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/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index bca6f39..54f3f10 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -215,7 +215,7 @@
     private long mMisses = 0;
 
     @GuardedBy("mLock")
-    private long mMissDisabled[] = new long[]{ 0, 0, 0 };
+    private long mSkips[] = new long[]{ 0, 0, 0 };
 
     @GuardedBy("mLock")
     private long mMissOverflow = 0;
@@ -223,6 +223,9 @@
     @GuardedBy("mLock")
     private long mHighWaterMark = 0;
 
+    @GuardedBy("mLock")
+    private long mClears = 0;
+
     // Most invalidation is done in a static context, so the counters need to be accessible.
     @GuardedBy("sCorkLock")
     private static final HashMap<String, Long> sInvalidates = new HashMap<>();
@@ -273,6 +276,13 @@
      */
     private volatile SystemProperties.Handle mPropertyHandle;
 
+    /**
+     * The name by which this cache is known.  This should normally be the
+     * binder call that is being cached, but the constructors default it to
+     * the property name.
+     */
+    private final String mCacheName;
+
     @GuardedBy("mLock")
     private final LinkedHashMap<Query, Result> mCache;
 
@@ -297,9 +307,23 @@
      *
      * @param maxEntries Maximum number of entries to cache; LRU discard
      * @param propertyName Name of the system property holding the cache invalidation nonce
+     * Defaults the cache name to the property name.
      */
     public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
+        this(maxEntries, propertyName, propertyName);
+    }
+
+    /**
+     * Make a new property invalidated cache.
+     *
+     * @param maxEntries Maximum number of entries to cache; LRU discard
+     * @param propertyName Name of the system property holding the cache invalidation nonce
+     * @param cacheName Name of this cache in debug and dumpsys
+     */
+    public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
+            @NonNull String cacheName) {
         mPropertyName = propertyName;
+        mCacheName = cacheName;
         mMaxEntries = maxEntries;
         mCache = new LinkedHashMap<Query, Result>(
             2 /* start small */,
@@ -320,7 +344,6 @@
             };
         synchronized (sCorkLock) {
             sCaches.put(this, null);
-            sInvalidates.put(propertyName, (long) 0);
         }
     }
 
@@ -333,6 +356,7 @@
                 Log.d(TAG, "clearing cache for " + mPropertyName);
             }
             mCache.clear();
+            mClears++;
         }
     }
 
@@ -413,7 +437,7 @@
                     // Do not bother collecting statistics if the cache is
                     // locally disabled.
                     synchronized (mLock) {
-                        mMissDisabled[(int) currentNonce]++;
+                        mSkips[(int) currentNonce]++;
                     }
                 }
 
@@ -742,12 +766,16 @@
                 boolean alreadyQueued = mUncorkDeadlineMs >= 0;
                 if (DEBUG) {
                     Log.w(TAG, String.format(
-                                    "autoCork mUncorkDeadlineMs=%s", mUncorkDeadlineMs));
+                            "autoCork %s mUncorkDeadlineMs=%s", mPropertyName,
+                            mUncorkDeadlineMs));
                 }
                 mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs;
                 if (!alreadyQueued) {
                     getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
                     PropertyInvalidatedCache.corkInvalidations(mPropertyName);
+                } else {
+                    final long count = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0);
+                    sCorkedInvalidates.put(mPropertyName, count + 1);
                 }
             }
         }
@@ -756,7 +784,8 @@
             synchronized (mLock) {
                 if (DEBUG) {
                     Log.w(TAG, String.format(
-                                    "handleMsesage mUncorkDeadlineMs=%s", mUncorkDeadlineMs));
+                            "handleMsesage %s mUncorkDeadlineMs=%s",
+                            mPropertyName, mUncorkDeadlineMs));
                 }
 
                 if (mUncorkDeadlineMs < 0) {
@@ -816,7 +845,7 @@
      * method is public so clients can use it.
      */
     public String cacheName() {
-        return mPropertyName;
+        return mCacheName;
     }
 
     /**
@@ -864,16 +893,20 @@
         }
 
         synchronized (mLock) {
-            pw.println(String.format("  Cache Property Name: %s", cacheName()));
-            pw.println(String.format("    Hits: %d, Misses: %d, Invalidates: %d, Overflows: %d",
-                    mHits, mMisses, invalidateCount, mMissOverflow));
-            pw.println(String.format("    Miss-corked: %d, Miss-unset: %d, Miss-other: %d," +
-                    " CorkedInvalidates: %d",
-                    mMissDisabled[NONCE_CORKED], mMissDisabled[NONCE_UNSET],
-                    mMissDisabled[NONCE_DISABLED], corkedInvalidates));
-            pw.println(String.format("    Last Observed Nonce: %d", mLastSeenNonce));
-            pw.println(String.format("    Current Size: %d, Max Size: %d, HW Mark: %d",
-                    mCache.size(), mMaxEntries, mHighWaterMark));
+            pw.println(String.format("  Cache Name: %s", cacheName()));
+            pw.println(String.format("    Property: %s", mPropertyName));
+            final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED];
+            pw.println(String.format("    Hits: %d, Misses: %d, Skips: %d, Clears: %d",
+                    mHits, mMisses, skips, mClears));
+            pw.println(String.format("    Skip-corked: %d, Skip-unset: %d, Skip-other: %d",
+                    mSkips[NONCE_CORKED], mSkips[NONCE_UNSET],
+                    mSkips[NONCE_DISABLED]));
+            pw.println(String.format(
+                    "    Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
+                    mLastSeenNonce, invalidateCount, corkedInvalidates));
+            pw.println(String.format(
+                    "    Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
+                    mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
             pw.println(String.format("    Enabled: %s", mDisabled ? "false" : "true"));
 
             Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 7477899..fb2120e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -39,7 +39,6 @@
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.LruCache;
 import android.util.Pair;
 import android.util.Slog;
 import android.view.Display;
@@ -62,7 +61,6 @@
 import java.util.Objects;
 import java.util.WeakHashMap;
 import java.util.function.Consumer;
-import java.util.function.Predicate;
 
 /** @hide */
 public class ResourcesManager {
@@ -129,17 +127,30 @@
         }
     }
 
-    private static final boolean ENABLE_APK_ASSETS_CACHE = false;
-
     /**
-     * The ApkAssets we are caching and intend to hold strong references to.
+     * Loads {@link ApkAssets} and caches them to prevent their garbage collection while the
+     * instance is alive and reachable.
      */
-    private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets =
-            (ENABLE_APK_ASSETS_CACHE) ? new LruCache<>(3) : null;
+    private class ApkAssetsSupplier {
+        final ArrayMap<ApkKey, ApkAssets> mLocalCache = new ArrayMap<>();
+
+        /**
+         * Retrieves the {@link ApkAssets} corresponding to the specified key, caches the ApkAssets
+         * within this instance, and inserts the loaded ApkAssets into the {@link #mCachedApkAssets}
+         * cache.
+         */
+        ApkAssets load(final ApkKey apkKey) throws IOException {
+            ApkAssets apkAssets = mLocalCache.get(apkKey);
+            if (apkAssets == null) {
+                apkAssets = loadApkAssets(apkKey);
+                mLocalCache.put(apkKey, apkAssets);
+            }
+            return apkAssets;
+        }
+    }
 
     /**
-     * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't
-     * in our LRU cache. Bonus resources :)
+     * The ApkAssets that are being referenced in the wild that we can reuse.
      */
     private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
 
@@ -228,7 +239,8 @@
         }
     }
 
-    DisplayMetrics getDisplayMetrics() {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public DisplayMetrics getDisplayMetrics() {
         return getDisplayMetrics(Display.DEFAULT_DISPLAY,
                 DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
@@ -337,49 +349,78 @@
         return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
     }
 
-    private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay)
-            throws IOException {
-        final ApkKey newKey = new ApkKey(path, sharedLib, overlay);
-        ApkAssets apkAssets = null;
-        if (mLoadedApkAssets != null) {
-            apkAssets = mLoadedApkAssets.get(newKey);
-            if (apkAssets != null && apkAssets.isUpToDate()) {
-                return apkAssets;
-            }
-        }
+    private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
+        ApkAssets apkAssets;
 
         // Optimistically check if this ApkAssets exists somewhere else.
-        final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey);
-        if (apkAssetsRef != null) {
-            apkAssets = apkAssetsRef.get();
-            if (apkAssets != null && apkAssets.isUpToDate()) {
-                if (mLoadedApkAssets != null) {
-                    mLoadedApkAssets.put(newKey, apkAssets);
+        synchronized (this) {
+            final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(key);
+            if (apkAssetsRef != null) {
+                apkAssets = apkAssetsRef.get();
+                if (apkAssets != null && apkAssets.isUpToDate()) {
+                    return apkAssets;
+                } else {
+                    // Clean up the reference.
+                    mCachedApkAssets.remove(key);
                 }
-
-                return apkAssets;
-            } else {
-                // Clean up the reference.
-                mCachedApkAssets.remove(newKey);
             }
         }
 
         // We must load this from disk.
-        if (overlay) {
-            apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), 0 /*flags*/);
+        if (key.overlay) {
+            apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path),
+                    0 /*flags*/);
         } else {
-            apkAssets = ApkAssets.loadFromPath(path, sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
+            apkAssets = ApkAssets.loadFromPath(key.path,
+                    key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
         }
 
-        if (mLoadedApkAssets != null) {
-            mLoadedApkAssets.put(newKey, apkAssets);
+        synchronized (this) {
+            mCachedApkAssets.put(key, new WeakReference<>(apkAssets));
         }
 
-        mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets));
         return apkAssets;
     }
 
     /**
+     * Retrieves a list of apk keys representing the ApkAssets that should be loaded for
+     * AssetManagers mapped to the {@param key}.
+     */
+    private static @NonNull ArrayList<ApkKey> extractApkKeys(@NonNull final ResourcesKey key) {
+        final ArrayList<ApkKey> apkKeys = new ArrayList<>();
+
+        // resDir can be null if the 'android' package is creating a new Resources object.
+        // This is fine, since each AssetManager automatically loads the 'android' package
+        // already.
+        if (key.mResDir != null) {
+            apkKeys.add(new ApkKey(key.mResDir, false /*sharedLib*/, false /*overlay*/));
+        }
+
+        if (key.mSplitResDirs != null) {
+            for (final String splitResDir : key.mSplitResDirs) {
+                apkKeys.add(new ApkKey(splitResDir, false /*sharedLib*/, false /*overlay*/));
+            }
+        }
+
+        if (key.mLibDirs != null) {
+            for (final String libDir : key.mLibDirs) {
+                // Avoid opening files we know do not have resources, like code-only .jar files.
+                if (libDir.endsWith(".apk")) {
+                    apkKeys.add(new ApkKey(libDir, true /*sharedLib*/, false /*overlay*/));
+                }
+            }
+        }
+
+        if (key.mOverlayDirs != null) {
+            for (final String idmapPath : key.mOverlayDirs) {
+                apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/));
+            }
+        }
+
+        return apkKeys;
+    }
+
+    /**
      * Creates an AssetManager from the paths within the ResourcesKey.
      *
      * This can be overridden in tests so as to avoid creating a real AssetManager with
@@ -390,64 +431,38 @@
     @VisibleForTesting
     @UnsupportedAppUsage
     protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
+        return createAssetManager(key, /* apkSupplier */ null);
+    }
+
+    /**
+     * Variant of {@link #createAssetManager(ResourcesKey)} that attempts to load ApkAssets
+     * from an {@link ApkAssetsSupplier} if non-null; otherwise ApkAssets are loaded using
+     * {@link #loadApkAssets(ApkKey)}.
+     */
+    private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
+            @Nullable ApkAssetsSupplier apkSupplier) {
         final AssetManager.Builder builder = new AssetManager.Builder();
 
-        // resDir can be null if the 'android' package is creating a new Resources object.
-        // This is fine, since each AssetManager automatically loads the 'android' package
-        // already.
-        if (key.mResDir != null) {
+        final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
+        for (int i = 0, n = apkKeys.size(); i < n; i++) {
+            final ApkKey apkKey = apkKeys.get(i);
             try {
-                builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
-                        false /*overlay*/));
+                builder.addApkAssets(
+                        (apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey));
             } catch (IOException e) {
-                Log.e(TAG, "failed to add asset path " + key.mResDir);
-                return null;
-            }
-        }
-
-        if (key.mSplitResDirs != null) {
-            for (final String splitResDir : key.mSplitResDirs) {
-                try {
-                    builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/,
-                            false /*overlay*/));
-                } catch (IOException e) {
-                    Log.e(TAG, "failed to add split asset path " + splitResDir);
+                if (apkKey.overlay) {
+                    Log.w(TAG, String.format("failed to add overlay path '%s'", apkKey.path), e);
+                } else if (apkKey.sharedLib) {
+                    Log.w(TAG, String.format(
+                            "asset path '%s' does not exist or contains no resources",
+                            apkKey.path), e);
+                } else {
+                    Log.e(TAG, String.format("failed to add asset path '%s'", apkKey.path), e);
                     return null;
                 }
             }
         }
 
-        if (key.mLibDirs != null) {
-            for (final String libDir : key.mLibDirs) {
-                if (libDir.endsWith(".apk")) {
-                    // Avoid opening files we know do not have resources,
-                    // like code-only .jar files.
-                    try {
-                        builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/,
-                                false /*overlay*/));
-                    } catch (IOException e) {
-                        Log.w(TAG, "Asset path '" + libDir +
-                                "' does not exist or contains no resources.");
-
-                        // continue.
-                    }
-                }
-            }
-        }
-
-        if (key.mOverlayDirs != null) {
-            for (final String idmapPath : key.mOverlayDirs) {
-                try {
-                    builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
-                            true /*overlay*/));
-                } catch (IOException e) {
-                    Log.w(TAG, "failed to add overlay path " + idmapPath);
-
-                    // continue.
-                }
-            }
-        }
-
         if (key.mLoaders != null) {
             for (final ResourcesLoader loader : key.mLoaders) {
                 builder.addLoader(loader);
@@ -480,24 +495,6 @@
 
             pw.println("ResourcesManager:");
             pw.increaseIndent();
-            if (mLoadedApkAssets != null) {
-                pw.print("cached apks: total=");
-                pw.print(mLoadedApkAssets.size());
-                pw.print(" created=");
-                pw.print(mLoadedApkAssets.createCount());
-                pw.print(" evicted=");
-                pw.print(mLoadedApkAssets.evictionCount());
-                pw.print(" hit=");
-                pw.print(mLoadedApkAssets.hitCount());
-                pw.print(" miss=");
-                pw.print(mLoadedApkAssets.missCount());
-                pw.print(" max=");
-                pw.print(mLoadedApkAssets.maxSize());
-            } else {
-                pw.print("cached apks: 0 [cache disabled]");
-            }
-            pw.println();
-
             pw.print("total apks: ");
             pw.println(countLiveReferences(mCachedApkAssets.values()));
 
@@ -533,11 +530,12 @@
         return config;
     }
 
-    private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
+    private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
+            @Nullable ApkAssetsSupplier apkSupplier) {
         final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
         daj.setCompatibilityInfo(key.mCompatInfo);
 
-        final AssetManager assets = createAssetManager(key);
+        final AssetManager assets = createAssetManager(key, apkSupplier);
         if (assets == null) {
             return null;
         }
@@ -575,9 +573,18 @@
      */
     private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
             @NonNull ResourcesKey key) {
+        return findOrCreateResourcesImplForKeyLocked(key, /* apkSupplier */ null);
+    }
+
+    /**
+     * Variant of {@link #findOrCreateResourcesImplForKeyLocked(ResourcesKey)} that attempts to
+     * load ApkAssets from a {@link ApkAssetsSupplier} when creating a new ResourcesImpl.
+     */
+    private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
+            @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
         ResourcesImpl impl = findResourcesImplForKeyLocked(key);
         if (impl == null) {
-            impl = createResourcesImpl(key);
+            impl = createResourcesImpl(key, apkSupplier);
             if (impl != null) {
                 mResourceImpls.put(key, new WeakReference<>(impl));
             }
@@ -766,7 +773,7 @@
             }
 
             // Now request an actual Resources object.
-            return createResources(token, key, classLoader);
+            return createResources(token, key, classLoader, /* apkSupplier */ null);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -810,18 +817,50 @@
     }
 
     /**
+     * Creates an {@link ApkAssetsSupplier} and loads all the ApkAssets required by the {@param key}
+     * into the supplier. This should be done while the lock is not held to prevent performing I/O
+     * while holding the lock.
+     */
+    private @NonNull ApkAssetsSupplier createApkAssetsSupplierNotLocked(@NonNull ResourcesKey key) {
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                "ResourcesManager#createApkAssetsSupplierNotLocked");
+        try {
+            if (Thread.holdsLock(this)) {
+                Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+                    + " is holding mLock", new Throwable());
+            }
+
+            final ApkAssetsSupplier supplier = new ApkAssetsSupplier();
+            final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
+            for (int i = 0, n = apkKeys.size(); i < n; i++) {
+                final ApkKey apkKey = apkKeys.get(i);
+                try {
+                    supplier.load(apkKey);
+                } catch (IOException e) {
+                    Log.w(TAG, String.format("failed to preload asset path '%s'", apkKey.path), e);
+                }
+            }
+            return supplier;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+    }
+
+    /**
      * Creates a Resources object set with a ResourcesImpl object matching the given key.
      *
      * @param activityToken The Activity this Resources object should be associated with.
      * @param key The key describing the parameters of the ResourcesImpl object.
      * @param classLoader The classloader to use for the Resources object.
      *                    If null, {@link ClassLoader#getSystemClassLoader()} is used.
+     * @param apkSupplier The apk assets supplier to use when creating a new ResourcesImpl object.
      * @return A Resources object that gets updated when
      *         {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
      *         is called.
      */
     private @Nullable Resources createResources(@Nullable IBinder activityToken,
-            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
+            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
+            @Nullable ApkAssetsSupplier apkSupplier) {
         synchronized (this) {
             if (DEBUG) {
                 Throwable here = new Throwable();
@@ -829,7 +868,7 @@
                 Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
             }
 
-            ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);
+            ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
             if (resourcesImpl == null) {
                 return null;
             }
@@ -898,7 +937,10 @@
                 rebaseKeyForActivity(activityToken, key);
             }
 
-            return createResources(activityToken, key, classLoader);
+            // Preload the ApkAssets required by the key to prevent performing heavy I/O while the
+            // ResourcesManager lock is held.
+            final ApkAssetsSupplier assetsSupplier = createApkAssetsSupplierNotLocked(key);
+            return createResources(activityToken, key, classLoader, assetsSupplier);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -969,7 +1011,13 @@
                     final ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
                             overrideConfig, displayId);
                     if (newKey != null) {
-                        updateActivityResources(resources, newKey, false);
+                        final ResourcesImpl resourcesImpl =
+                                findOrCreateResourcesImplForKeyLocked(newKey);
+                        if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
+                            // Set the ResourcesImpl, updating it for all users of this Resources
+                            // object.
+                            resources.setImpl(resourcesImpl);
+                        }
                     }
                 }
             }
@@ -1024,24 +1072,6 @@
         return newKey;
     }
 
-    private void updateActivityResources(Resources resources, ResourcesKey newKey,
-            boolean hasLoader) {
-        final ResourcesImpl resourcesImpl;
-
-        if (hasLoader) {
-            // Loaders always get new Impls because they cannot be shared
-            resourcesImpl = createResourcesImpl(newKey);
-        } else {
-            resourcesImpl = findOrCreateResourcesImplForKeyLocked(newKey);
-        }
-
-        if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
-            // Set the ResourcesImpl, updating it for all users of this Resources
-            // object.
-            resources.setImpl(resourcesImpl);
-        }
-    }
-
     public final boolean applyConfigurationToResources(@NonNull Configuration config,
             @Nullable CompatibilityInfo compat) {
         synchronized(this) {
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/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 7c6eff1..06d1b74 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -510,6 +510,9 @@
     }
 
     /**
+     * Activating night mode for the current user
+     *
+     * @return {@code true} if the change is successful
      * @hide
      */
     public boolean setNightModeActivated(boolean active) {
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index a789169..056cfc7 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.IBackupAgent;
 import android.app.QueuedWork;
+import android.app.backup.BackupManager.OperationType;
 import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -38,6 +39,8 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -50,6 +53,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
@@ -129,6 +133,7 @@
 public abstract class BackupAgent extends ContextWrapper {
     private static final String TAG = "BackupAgent";
     private static final boolean DEBUG = false;
+    private static final int DEFAULT_OPERATION_TYPE = OperationType.BACKUP;
 
     /** @hide */
     public static final int RESULT_SUCCESS = 0;
@@ -186,6 +191,9 @@
     Handler mHandler = null;
 
     @Nullable private UserHandle mUser;
+     // This field is written from the main thread (in onCreate), and read in a Binder thread (in
+     // onFullBackup that is called from system_server via Binder).
+    @OperationType private volatile int mOperationType = DEFAULT_OPERATION_TYPE;
 
     Handler getHandler() {
         if (mHandler == null) {
@@ -229,6 +237,13 @@
     }
 
     /**
+     * @hide
+     */
+    public void onCreate(UserHandle user) {
+        onCreate(user, DEFAULT_OPERATION_TYPE);
+    }
+
+    /**
      * Provided as a convenience for agent implementations that need an opportunity
      * to do one-time initialization before the actual backup or restore operation
      * is begun with information about the calling user.
@@ -236,10 +251,11 @@
      *
      * @hide
      */
-    public void onCreate(UserHandle user) {
+    public void onCreate(UserHandle user, @OperationType int operationType) {
         onCreate();
 
         mUser = user;
+        mOperationType = operationType;
     }
 
     /**
@@ -386,16 +402,13 @@
      */
     public void onFullBackup(FullBackupDataOutput data) throws IOException {
         FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this);
-        if (!backupScheme.isFullBackupContentEnabled()) {
+        if (!isDeviceToDeviceMigration() && !backupScheme.isFullBackupContentEnabled()) {
             return;
         }
 
-        Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap;
-        ArraySet<PathWithRequiredFlags> manifestExcludeSet;
+        IncludeExcludeRules includeExcludeRules;
         try {
-            manifestIncludeMap =
-                    backupScheme.maybeParseAndGetCanonicalIncludePaths();
-            manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();
+            includeExcludeRules = getIncludeExcludeRules(backupScheme);
         } catch (IOException | XmlPullParserException e) {
             if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(FullBackup.TAG_XML_PARSER,
@@ -404,6 +417,10 @@
             }
             return;
         }
+        Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap
+                = includeExcludeRules.getIncludeMap();
+        Set<PathWithRequiredFlags> manifestExcludeSet
+                = includeExcludeRules.getExcludeSet();
 
         final String packageName = getPackageName();
         final ApplicationInfo appInfo = getApplicationInfo();
@@ -413,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()
@@ -443,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);
@@ -528,6 +542,41 @@
         }
     }
 
+    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 (isDeviceToDeviceMigration()) {
+            return IncludeExcludeRules.emptyRules();
+        }
+
+        Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap;
+        ArraySet<PathWithRequiredFlags> manifestExcludeSet;
+
+        manifestIncludeMap =
+                backupScheme.maybeParseAndGetCanonicalIncludePaths();
+        manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();
+
+        return new IncludeExcludeRules(manifestIncludeMap, manifestExcludeSet);
+    }
+
     /**
      * Notification that the application's current backup operation causes it to exceed
      * the maximum size permitted by the transport.  The ongoing backup operation is
@@ -570,7 +619,7 @@
      */
     private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken,
             Map<String, Set<PathWithRequiredFlags>> includeMap,
-            ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet,
+            Set<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet,
             FullBackupDataOutput data) throws IOException {
         if (includeMap == null || includeMap.size() == 0) {
             // Do entire sub-tree for the provided token.
@@ -742,7 +791,7 @@
      * @hide
      */
     protected final void fullBackupFileTree(String packageName, String domain, String startingPath,
-                                            ArraySet<PathWithRequiredFlags> manifestExcludes,
+                                            Set<PathWithRequiredFlags> manifestExcludes,
                                             ArraySet<String> systemExcludes,
             FullBackupDataOutput output) {
         // Pull out the domain and set it aside to use when making the tarball.
@@ -811,7 +860,7 @@
     }
 
     private boolean manifestExcludesContainFilePath(
-        ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) {
+        Set<PathWithRequiredFlags> manifestExcludes, String filePath) {
         for (PathWithRequiredFlags exclude : manifestExcludes) {
             String excludePath = exclude.getPath();
             if (excludePath != null && excludePath.equals(filePath)) {
@@ -857,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)) {
@@ -1265,4 +1319,53 @@
             throw new IllegalStateException(mMessage);
         }
     }
+
+    /**  @hide */
+    @VisibleForTesting
+    public static class IncludeExcludeRules {
+        private final Map<String, Set<PathWithRequiredFlags>> mManifestIncludeMap;
+        private final Set<PathWithRequiredFlags> mManifestExcludeSet;
+
+        /** @hide */
+        public IncludeExcludeRules(
+                Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap,
+                Set<PathWithRequiredFlags> manifestExcludeSet) {
+            mManifestIncludeMap = manifestIncludeMap;
+            mManifestExcludeSet = manifestExcludeSet;
+        }
+
+        /**  @hide */
+        @VisibleForTesting
+        public static IncludeExcludeRules emptyRules() {
+            return new IncludeExcludeRules(Collections.emptyMap(), new ArraySet<>());
+        }
+
+        private Map<String, Set<PathWithRequiredFlags>> getIncludeMap() {
+            return mManifestIncludeMap;
+        }
+
+        private Set<PathWithRequiredFlags> getExcludeSet() {
+            return mManifestExcludeSet;
+        }
+
+        /**  @hide */
+        @Override
+        public int hashCode() {
+            return Objects.hash(mManifestIncludeMap, mManifestExcludeSet);
+        }
+
+        /**  @hide */
+        @Override
+        public boolean equals(Object object) {
+            if (this == object) {
+                return true;
+            }
+            if (object == null || getClass() != object.getClass()) {
+                return false;
+            }
+            IncludeExcludeRules that = (IncludeExcludeRules) object;
+            return Objects.equals(mManifestIncludeMap, that.mManifestIncludeMap) &&
+                    Objects.equals(mManifestExcludeSet, that.mManifestExcludeSet);
+        }
+    }
 }
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index b1a62bf..9b67587 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -355,7 +355,36 @@
             try {
                 // All packages, current transport
                 IRestoreSession binder =
-                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
+                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
+                                OperationType.BACKUP);
+                if (binder != null) {
+                    session = new RestoreSession(mContext, binder);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "beginRestoreSession() couldn't connect");
+            }
+        }
+        return session;
+    }
+
+    /**
+     * Begin the process of restoring data from backup.  See the
+     * {@link android.app.backup.RestoreSession} class for documentation on that process.
+     *
+     * @param operationType Type of the operation, see {@link OperationType}
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public RestoreSession beginRestoreSession(@OperationType int operationType) {
+        RestoreSession session = null;
+        checkServiceBinder();
+        if (sService != null) {
+            try {
+                // All packages, current transport
+                IRestoreSession binder =
+                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
+                                operationType);
                 if (binder != null) {
                     session = new RestoreSession(mContext, binder);
                 }
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 96b5dd5..e177a74 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -547,9 +547,11 @@
      *        set can be restored.
      * @param transportID The name of the transport to use for the restore operation.
      *        May be null, in which case the current active transport is used.
+     * @param operationType Type of the operation, see {@link BackupManager#OperationType}
      * @return An interface to the restore session, or null on error.
      */
-    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
+    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID,
+            int operationType);
 
     /**
      * Notify the backup manager that a BackupAgent has completed the operation
diff --git a/core/java/android/app/people/ConversationChannel.java b/core/java/android/app/people/ConversationChannel.java
new file mode 100644
index 0000000..39c5c85
--- /dev/null
+++ b/core/java/android/app/people/ConversationChannel.java
@@ -0,0 +1,97 @@
+/*
+ * 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 android.app.people;
+
+import android.app.NotificationChannel;
+import android.content.pm.ShortcutInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The non-customized notification channel of a conversation. It contains the information to render
+ * the conversation and allows the user to open and customize the conversation setting.
+ *
+ * @hide
+ */
+public final class ConversationChannel implements Parcelable {
+
+    private ShortcutInfo mShortcutInfo;
+    private NotificationChannel mParentNotificationChannel;
+    private long mLastEventTimestamp;
+    private boolean mHasActiveNotifications;
+
+    public static final Creator<ConversationChannel> CREATOR = new Creator<ConversationChannel>() {
+        @Override
+        public ConversationChannel createFromParcel(Parcel in) {
+            return new ConversationChannel(in);
+        }
+
+        @Override
+        public ConversationChannel[] newArray(int size) {
+            return new ConversationChannel[size];
+        }
+    };
+
+    public ConversationChannel(ShortcutInfo shortcutInfo,
+            NotificationChannel parentNotificationChannel, long lastEventTimestamp,
+            boolean hasActiveNotifications) {
+        mShortcutInfo = shortcutInfo;
+        mParentNotificationChannel = parentNotificationChannel;
+        mLastEventTimestamp = lastEventTimestamp;
+        mHasActiveNotifications = hasActiveNotifications;
+    }
+
+    public ConversationChannel(Parcel in) {
+        mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+        mParentNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader());
+        mLastEventTimestamp = in.readLong();
+        mHasActiveNotifications = in.readBoolean();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mShortcutInfo, flags);
+        dest.writeParcelable(mParentNotificationChannel, flags);
+        dest.writeLong(mLastEventTimestamp);
+        dest.writeBoolean(mHasActiveNotifications);
+    }
+
+    public ShortcutInfo getShortcutInfo() {
+        return mShortcutInfo;
+    }
+
+    public NotificationChannel getParentNotificationChannel() {
+        return mParentNotificationChannel;
+    }
+
+    public long getLastEventTimestamp() {
+        return mLastEventTimestamp;
+    }
+
+    /**
+     * Whether this conversation has any active notifications. If it's true, the shortcut for this
+     * conversation can't be uncached until all its active notifications are dismissed.
+     */
+    public boolean hasActiveNotifications() {
+        return mHasActiveNotifications;
+    }
+}
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
new file mode 100644
index 0000000..61dac0d
--- /dev/null
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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 android.app.people;
+
+import android.content.pm.ParceledListSlice;
+import android.net.Uri;
+import android.os.IBinder;
+
+/**
+ * System private API for talking with the people service.
+ * {@hide}
+ */
+interface IPeopleManager {
+    /**
+     * Returns the recent conversations. The conversations that have customized notification
+     * settings are excluded from the returned list.
+     */
+    ParceledListSlice getRecentConversations();
+
+    /**
+     * Removes the specified conversation from the recent conversations list and uncaches the
+     * shortcut associated with the conversation.
+     */
+    void removeRecentConversation(in String packageName, int userId, in String shortcutId);
+
+    /** Removes all the recent conversations and uncaches their cached shortcuts. */
+    void removeAllRecentConversations();
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
index 6bd365f..0770aff 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
@@ -117,7 +117,7 @@
     }
 
     private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) {
-        ArraySet<TimeZoneConfigurationListener> configurationListeners;
+        final ArraySet<TimeZoneConfigurationListener> configurationListeners;
         synchronized (this) {
             configurationListeners = new ArraySet<>(mConfigurationListeners);
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 16cdf23..52b0467 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3496,6 +3496,7 @@
             //@hide: TIME_ZONE_DETECTOR_SERVICE,
             PERMISSION_SERVICE,
             LIGHTS_SERVICE,
+            //@hide: PEOPLE_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -5189,6 +5190,14 @@
     public static final String SMS_SERVICE = "sms";
 
     /**
+     * Use with {@link #getSystemService(String)} to access people service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String PEOPLE_SERVICE = "people";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/LocusId.java b/core/java/android/content/LocusId.java
index 283cea0..98e71f0 100644
--- a/core/java/android/content/LocusId.java
+++ b/core/java/android/content/LocusId.java
@@ -33,7 +33,7 @@
  * by the Android System to correlate state between different subsystems such as content capture,
  * shortcuts, and notifications.
  *
- * <p>For example, if your app provides an activiy representing a chat between 2 users
+ * <p>For example, if your app provides an activity representing a chat between 2 users
  * (say {@code A} and {@code B}, this chat state could be represented by:
  *
  * <pre><code>
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index bd02210..31c77ee 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -963,7 +963,7 @@
     /** @hide */
     public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2;
     /** @hide */
-    public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3;
+    public static final int LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED = 3;
 
     /** @hide */
     public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) {
@@ -974,8 +974,8 @@
                 return "LOCK_TASK_LAUNCH_MODE_NEVER";
             case LOCK_TASK_LAUNCH_MODE_ALWAYS:
                 return "LOCK_TASK_LAUNCH_MODE_ALWAYS";
-            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
-                return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED";
+            case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED:
+                return "LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED";
             default:
                 return "unknown=" + lockTaskLaunchMode;
         }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index df9db27..bed7b26 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2075,7 +2075,8 @@
                 STAGED_SESSION_NO_ERROR,
                 STAGED_SESSION_VERIFICATION_FAILED,
                 STAGED_SESSION_ACTIVATION_FAILED,
-                STAGED_SESSION_UNKNOWN})
+                STAGED_SESSION_UNKNOWN,
+                STAGED_SESSION_OTHER_ERROR})
         @Retention(RetentionPolicy.SOURCE)
         public @interface StagedSessionErrorCode{}
         /**
@@ -2101,6 +2102,12 @@
          */
         public static final int STAGED_SESSION_UNKNOWN = 3;
 
+        /**
+         * Constant indicating that a known error occurred while processing this staged session, but
+         * the error could not be matched to other categories.
+         */
+        public static final int STAGED_SESSION_OTHER_ERROR = 4;
+
         /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public int sessionId;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 42a6107..e08af55 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8089,7 +8089,8 @@
     private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
             sApplicationInfoCache =
             new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
-                    16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+                    16, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+                    "getApplicationInfo") {
                 @Override
                 protected ApplicationInfo recompute(ApplicationInfoQuery query) {
                     return getApplicationInfoAsUserUncached(
@@ -8190,7 +8191,8 @@
     private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
             sPackageInfoCache =
             new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
-                    32, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+                    32, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+                    "getPackageInfo") {
                 @Override
                 protected PackageInfo recompute(PackageInfoQuery query) {
                     return getPackageInfoAsUserUncached(
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index cb93cbf..fcbe362 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -23,6 +23,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.TypedValue;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 
 import dalvik.annotation.optimization.FastNative;
@@ -38,7 +39,8 @@
  * 
  * {@hide}
  */
-final class XmlBlock implements AutoCloseable {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class XmlBlock implements AutoCloseable {
     private static final boolean DEBUG=false;
 
     @UnsupportedAppUsage
@@ -88,7 +90,8 @@
         }
     }
 
-    /*package*/ final class Parser implements XmlResourceParser {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public final class Parser implements XmlResourceParser {
         Parser(long parseState, XmlBlock block) {
             mParseState = parseState;
             mBlock = block;
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index f69bbe5..f0a83f0 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -999,7 +999,7 @@
          * <p>If the camera device configuration fails, then {@link #onConfigureFailed} will
          * be invoked instead of this callback.</p>
          *
-         * @param session the session returned by {@link CameraDevice#createCaptureSession}
+         * @param session the successfully configured session instance
          */
         public abstract void onConfigured(@NonNull CameraCaptureSession session);
 
@@ -1014,7 +1014,7 @@
          * to the session prior to this callback will be discarded and will not produce any
          * callbacks on their listeners.</p>
          *
-         * @param session the session returned by {@link CameraDevice#createCaptureSession}
+         * @param session the session instance that failed during configuration
          */
         public abstract void onConfigureFailed(@NonNull CameraCaptureSession session);
 
@@ -1028,7 +1028,7 @@
          * <p>Otherwise, this callback will be invoked any time the session finishes processing
          * all of its active capture requests, and no repeating request or burst is set up.</p>
          *
-         * @param session the session returned by {@link CameraDevice#createCaptureSession}
+         * @param session the session returned by {@link #onConfigured}
          *
          */
         public void onReady(@NonNull CameraCaptureSession session) {
@@ -1045,7 +1045,7 @@
          * <p>If the session runs out of capture requests to process and calls {@link #onReady},
          * then this callback will be invoked again once new requests are submitted for capture.</p>
          *
-         * @param session the session returned by {@link CameraDevice#createCaptureSession}
+         * @param session the session returned by {@link #onConfigured}
          */
         public void onActive(@NonNull CameraCaptureSession session) {
             // default empty implementation
@@ -1075,7 +1075,7 @@
          * {@link #onReady}, which is fired when all requests in both queues have been processed.</p>
          *
          * @param session
-         *            The session returned by {@link CameraDevice#createCaptureSession}
+         *            The session returned by {@link #onConfigured}
          */
         public void onCaptureQueueEmpty(@NonNull CameraCaptureSession session) {
             // default empty implementation
@@ -1093,7 +1093,7 @@
          * However, any in-progress capture requests submitted to the session will be completed
          * as normal.</p>
          *
-         * @param session the session returned by {@link CameraDevice#createCaptureSession}
+         * @param session the session returned by {@link #onConfigured}
          */
         public void onClosed(@NonNull CameraCaptureSession session) {
             // default empty implementation
@@ -1111,7 +1111,7 @@
          * this callback is still invoked after the error is encountered, though some buffers may
          * not have been successfully pre-allocated.</p>
          *
-         * @param session the session returned by {@link CameraDevice#createCaptureSession}
+         * @param session the session returned by {@link #onConfigured}
          * @param surface the Surface that was used with the {@link #prepare} call.
          */
         public void onSurfacePrepared(@NonNull CameraCaptureSession session,
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 7ac8d05..c7f8915 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -260,6 +260,13 @@
             int displayId, long maxFrames, long timestamp);
 
     /**
+     * Temporarily ignore proximity-sensor-based display behavior until there is a change
+     * to the proximity sensor state. This allows the display to turn back on even if something
+     * is obstructing the proximity sensor.
+     */
+    public abstract void ignoreProximitySensorUntilChanged();
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 885d137..19cb13c 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -1073,12 +1073,12 @@
     /**
      * @hide
      */
-    public abstract static class GenerateChallengeCallback {
-        public abstract void onGenerateChallengeResult(long challenge);
+    public interface GenerateChallengeCallback {
+        void onGenerateChallengeResult(long challenge);
     }
 
     private abstract static class InternalGenerateChallengeCallback
-            extends GenerateChallengeCallback {}
+            implements GenerateChallengeCallback {}
 
     private class OnEnrollCancelListener implements OnCancelListener {
         @Override
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index e384da7..71598eb 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -377,12 +377,12 @@
     /**
      * @hide
      */
-    public abstract static class GenerateChallengeCallback {
-        public abstract void onChallengeGenerated(long challenge);
+    public interface GenerateChallengeCallback {
+        void onChallengeGenerated(long challenge);
     }
 
     private abstract static class InternalGenerateChallengeCallback
-            extends GenerateChallengeCallback {}
+            implements GenerateChallengeCallback {}
 
     /**
      * Request authentication of a crypto object. This call warms up the fingerprint hardware
@@ -581,37 +581,6 @@
     }
 
     /**
-     * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the
-     * TEE/hardware operation is complete.
-     * @return challenge generated in the TEE/hardware
-     * @hide
-     */
-    @RequiresPermission(MANAGE_FINGERPRINT)
-    public long generateChallengeBlocking() {
-        final AtomicReference<Long> result = new AtomicReference<>();
-        final CountDownLatch latch = new CountDownLatch(1);
-        final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() {
-            @Override
-            public void onChallengeGenerated(long challenge) {
-                result.set(challenge);
-                latch.countDown();
-            }
-        };
-
-        generateChallenge(callback);
-
-        try {
-            latch.await(1, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
-            Slog.e(TAG, "Interrupted while generatingChallenge", e);
-            e.printStackTrace();
-        }
-
-        return result.get();
-    }
-
-
-    /**
      * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a
      * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification.
      * The HardwareAuthenticationToken can then be sent to the biometric HAL together with a
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index ba0636f..dc6f579 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -23,6 +23,7 @@
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.TouchCalibration;
 import android.os.IBinder;
+import android.os.VibrationEffect;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputMonitor;
@@ -83,7 +84,7 @@
     int isMicMuted();
 
     // Input device vibrator control.
-    void vibrate(int deviceId, in long[] pattern, in int[] amplitudes, int repeat, IBinder token);
+    void vibrate(int deviceId, in VibrationEffect effect, IBinder token);
     void cancelVibrate(int deviceId, IBinder token);
 
     void setPointerIconType(int typeId);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index f0faeb0..dd820fa 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1297,27 +1297,8 @@
         @Override
         public void vibrate(int uid, String opPkg, VibrationEffect effect,
                 String reason, AudioAttributes attributes) {
-            long[] pattern;
-            int[] amplitudes;
-            int repeat;
-            if (effect instanceof VibrationEffect.OneShot) {
-                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
-                pattern = new long[] { 0, oneShot.getDuration() };
-                amplitudes = new int[] { 0, oneShot.getAmplitude() };
-                repeat = -1;
-            } else if (effect instanceof VibrationEffect.Waveform) {
-                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
-                pattern = waveform.getTimings();
-                amplitudes = waveform.getAmplitudes();
-                repeat = waveform.getRepeatIndex();
-            } else {
-                // TODO: Add support for prebaked effects
-                Log.w(TAG, "Pre-baked effects aren't supported on input devices");
-                return;
-            }
-
             try {
-                mIm.vibrate(mDeviceId, pattern, amplitudes, repeat, mToken);
+                mIm.vibrate(mDeviceId, effect, mToken);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fbe6a50..b0d4497 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -743,6 +743,12 @@
         @UnsupportedAppUsage
         public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
 
+        /**
+         * Returns the proportion of power consumed by the System Service
+         * calls made by this UID.
+         */
+        public abstract double getProportionalSystemServiceUsage();
+
         public abstract ControllerActivityCounter getWifiControllerActivity();
         public abstract ControllerActivityCounter getBluetoothControllerActivity();
         public abstract ControllerActivityCounter getModemControllerActivity();
@@ -2882,6 +2888,17 @@
     public abstract int getDischargeAmountScreenDozeSinceCharge();
 
     /**
+     * Returns the approximate CPU time (in microseconds) spent by the system server handling
+     * incoming service calls from apps.
+     *
+     * @param cluster the index of the CPU cluster.
+     * @param step the index of the CPU speed. This is not the actual speed of the CPU.
+     * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
+     * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
+     */
+    public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step);
+
+    /**
      * Returns the total, last, or current battery uptime in microseconds.
      *
      * @param curTime the elapsed realtime in microseconds.
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 0cce192..8f8d451 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -287,8 +287,8 @@
     /**
      * Control network activity of a UID over interfaces with a quota limit.
      */
-    void setUidMeteredNetworkBlacklist(int uid, boolean enable);
-    void setUidMeteredNetworkWhitelist(int uid, boolean enable);
+    void setUidMeteredNetworkDenylist(int uid, boolean enable);
+    void setUidMeteredNetworkAllowlist(int uid, boolean enable);
     boolean setDataSaverModeEnabled(boolean enable);
 
     void setUidCleartextNetworkPolicy(int uid, int policy);
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/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index e30a409..eb18b96 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.view.Display;
+import android.view.KeyEvent;
 
 import java.util.function.Consumer;
 
@@ -313,4 +314,7 @@
 
     /** Returns information about the last wakeup event. */
     public abstract PowerManager.WakeData getLastWakeup();
+
+    /** Allows power button to intercept a power key button press. */
+    public abstract boolean interceptPowerKeyDown(KeyEvent event);
 }
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/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index bf3d46f..0c19071 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -608,7 +608,7 @@
     /** @hide */
     private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
             new PropertyInvalidatedCache<PermissionQuery, Integer>(
-                    16, CACHE_KEY_PACKAGE_INFO) {
+                    16, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
                 @Override
                 protected Integer recompute(PermissionQuery query) {
                     return checkPermissionUncached(query.permission, query.pid, query.uid);
@@ -689,7 +689,7 @@
     private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>
             sPackageNamePermissionCache =
             new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
-                    16, CACHE_KEY_PACKAGE_INFO) {
+                    16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
                 @Override
                 protected Integer recompute(PackageNamePermissionQuery query) {
                     return checkPackageNamePermissionUncached(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5acc11a8..660455e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7912,6 +7912,13 @@
         public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
 
         /**
+         * Internal use, one handed mode tutorial showed times.
+         * @hide
+         */
+        public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT =
+                "one_handed_tutorial_show_count";
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c383bc7..7f45c04 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -618,16 +618,20 @@
             return false;
         }
         if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
-        updateState(state);
-
-        boolean localStateChanged = !mState.equals(mLastDispatchedState,
-                true /* excludingCaptionInsets */, true /* excludeInvisibleIme */);
         mLastDispatchedState.set(state, true /* copySources */);
 
+        final InsetsState lastState = new InsetsState(mState, true /* copySources */);
+        updateState(state);
         applyLocalVisibilityOverride();
-        if (localStateChanged) {
-            if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState);
+
+        if (!mState.equals(lastState, true /* excludingCaptionInsets */,
+                true /* excludeInvisibleIme */)) {
+            if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
             mHost.notifyInsetsChanged();
+        }
+        if (!mState.equals(state, true /* excludingCaptionInsets */,
+                true /* excludeInvisibleIme */)) {
+            if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState);
             updateRequestedState();
         }
         return true;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 50ed00c..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.
@@ -2390,6 +2281,13 @@
     }
 
     /**
+     * @hide
+     */
+    public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
+        return nativeCaptureLayers(captureArgs);
+    }
+
+    /**
      * Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer
      * handles to exclude.
      * @hide
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0818abe..8917821 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -23358,7 +23358,7 @@
      *            displaying, else return the result of calling through to the
      *            super class.
      *
-     * @return boolean If true than the Drawable is being displayed in the
+     * @return boolean If true then the Drawable is being displayed in the
      *         view; else false and it is not allowed to animate.
      *
      * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 64ddb2f..3f02d70 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1814,19 +1814,13 @@
     /**
      * Called after window layout to update the bounds surface. If the surface insets have changed
      * or the surface has resized, update the bounds surface.
-     *
-     * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl.
      */
-    private void updateBoundsLayer(boolean shouldReparent) {
+    private void updateBoundsLayer() {
         if (mBoundsLayer != null) {
             setBoundsLayerCrop();
-            mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
-                    mSurface.getNextFrameNumber());
-
-            if (shouldReparent) {
-                mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl());
-            }
-            mTransaction.apply();
+            mTransaction.deferTransactionUntil(mBoundsLayer,
+                    getRenderSurfaceControl(), mSurface.getNextFrameNumber())
+                    .apply();
         }
     }
 
@@ -2905,16 +2899,7 @@
         }
 
         if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
-            // If the surface has been replaced, there's a chance the bounds layer is not parented
-            // to the new layer. When updating bounds layer, also reparent to the main VRI
-            // SurfaceControl to ensure it's correctly placed in the hierarchy.
-            //
-            // This needs to be done on the client side since WMS won't reparent the children to the
-            // new surface if it thinks the app is closing. WMS gets the signal that the app is
-            // stopping, but on the client side it doesn't get stopped since it's restarted quick
-            // enough. WMS doesn't want to keep around old children since they will leak when the
-            // client creates new children.
-            updateBoundsLayer(surfaceReplaced);
+            updateBoundsLayer();
         }
 
         final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
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/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index fed3dbf..0008658 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -20,9 +20,11 @@
 import android.annotation.WorkerThread;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
 import java.util.Objects;
+import java.util.function.Supplier;
 
 import sun.misc.Cleaner;
 
@@ -40,6 +42,9 @@
     private final TextClassificationContext mClassificationContext;
     private final Cleaner mCleaner;
 
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
     private boolean mDestroyed;
 
     TextClassificationSession(TextClassificationContext context, TextClassifier delegate) {
@@ -54,8 +59,7 @@
 
     @Override
     public TextSelection suggestSelection(TextSelection.Request request) {
-        checkDestroyed();
-        return mDelegate.suggestSelection(request);
+        return checkDestroyedAndRun(() -> mDelegate.suggestSelection(request));
     }
 
     private void initializeRemoteSession() {
@@ -67,77 +71,97 @@
 
     @Override
     public TextClassification classifyText(TextClassification.Request request) {
-        checkDestroyed();
-        return mDelegate.classifyText(request);
+        return checkDestroyedAndRun(() -> mDelegate.classifyText(request));
     }
 
     @Override
     public TextLinks generateLinks(TextLinks.Request request) {
-        checkDestroyed();
-        return mDelegate.generateLinks(request);
+        return checkDestroyedAndRun(() -> mDelegate.generateLinks(request));
     }
 
     @Override
     public ConversationActions suggestConversationActions(ConversationActions.Request request) {
-        checkDestroyed();
-        return mDelegate.suggestConversationActions(request);
+        return checkDestroyedAndRun(() -> mDelegate.suggestConversationActions(request));
     }
 
     @Override
     public TextLanguage detectLanguage(TextLanguage.Request request) {
-        checkDestroyed();
-        return mDelegate.detectLanguage(request);
+        return checkDestroyedAndRun(() -> mDelegate.detectLanguage(request));
     }
 
     @Override
     public int getMaxGenerateLinksTextLength() {
-        checkDestroyed();
-        return mDelegate.getMaxGenerateLinksTextLength();
+        return checkDestroyedAndRun(mDelegate::getMaxGenerateLinksTextLength);
     }
 
     @Override
     public void onSelectionEvent(SelectionEvent event) {
-        try {
-            if (mEventHelper.sanitizeEvent(event)) {
-                mDelegate.onSelectionEvent(event);
+        checkDestroyedAndRun(() -> {
+            try {
+                if (mEventHelper.sanitizeEvent(event)) {
+                    mDelegate.onSelectionEvent(event);
+                }
+            } catch (Exception e) {
+                // Avoid crashing for event reporting.
+                Log.e(LOG_TAG, "Error reporting text classifier selection event", e);
             }
-        } catch (Exception e) {
-            // Avoid crashing for event reporting.
-            Log.e(LOG_TAG, "Error reporting text classifier selection event", e);
-        }
+            return null;
+        });
     }
 
     @Override
     public void onTextClassifierEvent(TextClassifierEvent event) {
-        try {
-            event.mHiddenTempSessionId = mSessionId;
-            mDelegate.onTextClassifierEvent(event);
-        } catch (Exception e) {
-            // Avoid crashing for event reporting.
-            Log.e(LOG_TAG, "Error reporting text classifier event", e);
-        }
+        checkDestroyedAndRun(() -> {
+            try {
+                event.mHiddenTempSessionId = mSessionId;
+                mDelegate.onTextClassifierEvent(event);
+            } catch (Exception e) {
+                // Avoid crashing for event reporting.
+                Log.e(LOG_TAG, "Error reporting text classifier event", e);
+            }
+            return null;
+        });
     }
 
     @Override
     public void destroy() {
-        mCleaner.clean();
-        mDestroyed = true;
+        synchronized (mLock) {
+            if (!mDestroyed) {
+                mCleaner.clean();
+                mDestroyed = true;
+            }
+        }
     }
 
     @Override
     public boolean isDestroyed() {
-        return mDestroyed;
+        synchronized (mLock) {
+            return mDestroyed;
+        }
     }
 
     /**
-     * @throws IllegalStateException if this TextClassification session has been destroyed.
+     * Check whether the TextClassification Session was destroyed before and after the actual API
+     * invocation, and return response if not.
+     *
+     * @param responseSupplier a Supplier that represents a TextClassifier call
+     * @return the response of the TextClassifier call
+     * @throws IllegalStateException if this TextClassification session was destroyed before the
+     *                               call returned
      * @see #isDestroyed()
      * @see #destroy()
      */
-    private void checkDestroyed() {
-        if (mDestroyed) {
-            throw new IllegalStateException("This TextClassification session has been destroyed");
+    private <T> T checkDestroyedAndRun(Supplier<T> responseSupplier) {
+        if (!isDestroyed()) {
+            T response = responseSupplier.get();
+            synchronized (mLock) {
+                if (!mDestroyed) {
+                    return response;
+                }
+            }
         }
+        throw new IllegalStateException(
+                "This TextClassification session has been destroyed");
     }
 
     /**
diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java
index 5ef450f..7e7b987 100644
--- a/core/java/android/webkit/PacProcessor.java
+++ b/core/java/android/webkit/PacProcessor.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-
+import android.net.Network;
 
 /**
  * Class to evaluate PAC scripts.
@@ -40,6 +40,20 @@
     }
 
     /**
+     * Returns PacProcessor instance associated with the {@link Network}.
+     * The host resolution is done on this {@link Network}.
+     *
+     * @param networkHandle a handle representing {@link Network} handle.
+     * @return PacProcessor instance for the specified network.
+     * @see Network#getNetworkHandle
+     * @see Network#fromNetworkHandle
+     */
+    @NonNull
+    static PacProcessor getInstanceForNetwork(long networkHandle) {
+        return WebViewFactory.getProvider().getPacProcessorForNetwork(networkHandle);
+    }
+
+    /**
      * Set PAC script to use.
      *
      * @param script PAC script.
@@ -55,4 +69,23 @@
      */
     @Nullable
     String findProxyForUrl(@NonNull String url);
+
+    /**
+     * Stops support for this {@link PacProcessor} and release its resources.
+     * No methods of this class must be called after calling this method.
+     */
+    default void releasePacProcessor() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Returns a network handle associated with this {@link PacProcessor}.
+     *
+     * @return a network handle or 0 if a network is unspecified.
+     * @see Network#getNetworkHandle
+     * @see Network#fromNetworkHandle
+     */
+    default long getNetworkHandle() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
 }
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index f7c3ec0..f1863e3 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Network;
 import android.net.Uri;
 
 import java.util.List;
@@ -175,7 +176,7 @@
     WebViewDatabase getWebViewDatabase(Context context);
 
     /**
-     * Gets the singleton PacProcessor instance.
+     * Gets the default PacProcessor instance.
      * @return the PacProcessor instance
      */
     @NonNull
@@ -184,6 +185,20 @@
     }
 
     /**
+     * Returns PacProcessor instance associated with the {@link Network}.
+     * The host resolution is done on this {@link Network}.
+     *
+     * @param networkHandle a network handle representing the {@link Network}.
+     * @return the {@link PacProcessor} instance associated with {@link Network}.
+     * @see Network#getNetworkHandle
+     * @see Network#fromNetworkHandle
+     */
+    @NonNull
+    default PacProcessor getPacProcessorForNetwork(long networkHandle) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
      * Gets the classloader used to load internal WebView implementation classes. This interface
      * should only be used by the WebView Support Library.
      */
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c4eb396..60f8bb7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -154,6 +154,10 @@
     // Specifies whether to use the magnifier when pressing the insertion or selection handles.
     private static final boolean FLAG_USE_MAGNIFIER = true;
 
+    // Specifies how far to make the cursor start float when drag the cursor away from the
+    // beginning or end of the line.
+    private static final int CURSOR_START_FLOAT_DISTANCE_PX = 20;
+
     private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
     private static final int RECENT_CUT_COPY_DURATION_MS = 15 * 1000; // 15 seconds in millis
 
@@ -289,6 +293,9 @@
     private boolean mRenderCursorRegardlessTiming;
     private Blink mBlink;
 
+    // Whether to let magnifier draw cursor on its surface. This is for floating cursor effect.
+    // And it can only be true when |mNewMagnifierEnabled| is true.
+    private boolean mDrawCursorOnMagnifier;
     boolean mCursorVisible = true;
     boolean mSelectAllOnFocus;
     boolean mTextIsSelectable;
@@ -385,6 +392,7 @@
     private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
 
     private boolean mFlagCursorDragFromAnywhereEnabled;
+    private float mCursorDragDirectionMinXYRatio;
     private boolean mFlagInsertionHandleGesturesEnabled;
 
     // Specifies whether the new magnifier (with fish-eye effect) is enabled.
@@ -425,6 +433,11 @@
         mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting(
                 WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
                 WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0;
+        final int cursorDragMinAngleFromVertical = AppGlobals.getIntCoreSetting(
+                WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL,
+                WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT);
+        mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(
+                cursorDragMinAngleFromVertical);
         mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting(
                 WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES,
                 WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0;
@@ -437,6 +450,8 @@
         if (TextView.DEBUG_CURSOR) {
             logCursor("Editor", "Cursor drag from anywhere is %s.",
                     mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled");
+            logCursor("Editor", "Cursor drag min angle from vertical is %d (= %f x/y ratio)",
+                    cursorDragMinAngleFromVertical, mCursorDragDirectionMinXYRatio);
             logCursor("Editor", "Insertion handle gestures is %s.",
                     mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled");
             logCursor("Editor", "New magnifier is %s.",
@@ -463,6 +478,11 @@
     }
 
     @VisibleForTesting
+    public void setCursorDragMinAngleFromVertical(int degreesFromVertical) {
+        mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(degreesFromVertical);
+    }
+
+    @VisibleForTesting
     public boolean getFlagInsertionHandleGesturesEnabled() {
         return mFlagInsertionHandleGesturesEnabled;
     }
@@ -877,7 +897,7 @@
         }
 
         boolean enabled = windowSupportsHandles && mTextView.getLayout() != null;
-        mInsertionControllerEnabled = enabled && isCursorVisible();
+        mInsertionControllerEnabled = enabled && (mDrawCursorOnMagnifier || isCursorVisible());
         mSelectionControllerEnabled = enabled && mTextView.textCanBeSelected();
 
         if (!mInsertionControllerEnabled) {
@@ -5088,26 +5108,38 @@
             final int[] textViewLocationOnScreen = new int[2];
             mTextView.getLocationOnScreen(textViewLocationOnScreen);
             final float touchXInView = event.getRawX() - textViewLocationOnScreen[0];
-            float leftBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
-            float rightBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
-            if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_END) ^ rtl)) {
-                leftBound += getHorizontal(mTextView.getLayout(), otherHandleOffset);
+            float leftBound, rightBound;
+            if (mNewMagnifierEnabled) {
+                leftBound = 0;
+                rightBound = mTextView.getWidth();
+                if (touchXInView < leftBound || touchXInView > rightBound) {
+                    // The touch is too far from the current line / selection, so hide the magnifier.
+                    return false;
+                }
             } else {
-                leftBound += mTextView.getLayout().getLineLeft(lineNumber);
-            }
-            if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_START) ^ rtl)) {
-                rightBound += getHorizontal(mTextView.getLayout(), otherHandleOffset);
-            } else {
-                rightBound += mTextView.getLayout().getLineRight(lineNumber);
-            }
-            leftBound *= mTextViewScaleX;
-            rightBound *= mTextViewScaleX;
-            final float contentWidth = Math.round(mMagnifierAnimator.mMagnifier.getWidth()
-                    / mMagnifierAnimator.mMagnifier.getZoom());
-            if (touchXInView < leftBound - contentWidth / 2
-                    || touchXInView > rightBound + contentWidth / 2) {
-                // The touch is too far from the current line / selection, so hide the magnifier.
-                return false;
+                leftBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
+                rightBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
+                if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_END)
+                        ^ rtl)) {
+                    leftBound += getHorizontal(mTextView.getLayout(), otherHandleOffset);
+                } else {
+                    leftBound += mTextView.getLayout().getLineLeft(lineNumber);
+                }
+                if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_START)
+                        ^ rtl)) {
+                    rightBound += getHorizontal(mTextView.getLayout(), otherHandleOffset);
+                } else {
+                    rightBound += mTextView.getLayout().getLineRight(lineNumber);
+                }
+                leftBound *= mTextViewScaleX;
+                rightBound *= mTextViewScaleX;
+                final float contentWidth = Math.round(mMagnifierAnimator.mMagnifier.getWidth()
+                        / mMagnifierAnimator.mMagnifier.getZoom());
+                if (touchXInView < leftBound - contentWidth / 2
+                        || touchXInView > rightBound + contentWidth / 2) {
+                    // The touch is too far from the current line / selection, so hide the magnifier.
+                    return false;
+                }
             }
 
             final float scaledTouchXInView;
@@ -5165,7 +5197,8 @@
             final Rect magnifierRect = new Rect(magnifierTopLeft.x, magnifierTopLeft.y,
                     magnifierTopLeft.x + mMagnifierAnimator.mMagnifier.getWidth(),
                     magnifierTopLeft.y + mMagnifierAnimator.mMagnifier.getHeight());
-            setVisible(!handleOverlapsMagnifier(HandleView.this, magnifierRect));
+            setVisible(!handleOverlapsMagnifier(HandleView.this, magnifierRect)
+                    && !mDrawCursorOnMagnifier);
             final HandleView otherHandle = getOtherSelectionHandle();
             if (otherHandle != null) {
                 otherHandle.setVisible(!handleOverlapsMagnifier(otherHandle, magnifierRect));
@@ -5195,7 +5228,20 @@
                     lineLeft += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
                     int lineRight = (int) layout.getLineRight(line);
                     lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
-                    mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight);
+                    mDrawCursorOnMagnifier =
+                            showPosInView.x < lineLeft - CURSOR_START_FLOAT_DISTANCE_PX
+                            || showPosInView.x > lineRight + CURSOR_START_FLOAT_DISTANCE_PX;
+                    mMagnifierAnimator.mMagnifier.setDrawCursor(
+                            mDrawCursorOnMagnifier, mDrawableForCursor);
+                    boolean cursorVisible = mCursorVisible;
+                    // Updates cursor visibility, so that the real cursor and the float cursor on
+                    // magnifier surface won't appear at the same time.
+                    mCursorVisible = !mDrawCursorOnMagnifier;
+                    if (mCursorVisible && !cursorVisible) {
+                        // When the real cursor is a drawable, hiding/showing it would change its
+                        // bounds. So, call updateCursorPosition() to correct its position.
+                        updateCursorPosition();
+                    }
                     final int lineHeight =
                             layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
                     float zoom = mInitialZoom;
@@ -5217,6 +5263,11 @@
             if (mMagnifierAnimator != null) {
                 mMagnifierAnimator.dismiss();
                 mRenderCursorRegardlessTiming = false;
+                mDrawCursorOnMagnifier = false;
+                if (!mCursorVisible) {
+                    mCursorVisible = true;
+                    mTextView.invalidate();
+                }
                 resumeBlink();
                 setVisible(true);
                 final HandleView otherHandle = getOtherSelectionHandle();
@@ -6127,10 +6178,11 @@
                     if (mIsDraggingCursor) {
                         performCursorDrag(event);
                     } else if (mFlagCursorDragFromAnywhereEnabled
-                                && mTextView.getLayout() != null
-                                && mTextView.isFocused()
-                                && mTouchState.isMovedEnoughForDrag()
-                                && !mTouchState.isDragCloseToVertical()) {
+                            && mTextView.getLayout() != null
+                            && mTextView.isFocused()
+                            && mTouchState.isMovedEnoughForDrag()
+                            && (mTouchState.getInitialDragDirectionXYRatio()
+                            > mCursorDragDirectionMinXYRatio || mTouchState.isOnHandle())) {
                         startCursorDrag(event);
                     }
                     break;
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index 9eb63087..7514368 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -59,7 +59,7 @@
     private boolean mMultiTapInSameArea;
 
     private boolean mMovedEnoughForDrag;
-    private boolean mIsDragCloseToVertical;
+    private float mInitialDragDirectionXYRatio;
 
     public float getLastDownX() {
         return mLastDownX;
@@ -98,8 +98,23 @@
         return mMovedEnoughForDrag;
     }
 
-    public boolean isDragCloseToVertical() {
-        return mIsDragCloseToVertical && !mIsOnHandle;
+    /**
+     * When {@link #isMovedEnoughForDrag()} is {@code true}, this function returns the x/y ratio for
+     * the initial drag direction. Smaller values indicate that the direction is closer to vertical,
+     * while larger values indicate that the direction is closer to horizontal. For example:
+     * <ul>
+     *     <li>if the drag direction is exactly vertical, this returns 0
+     *     <li>if the drag direction is exactly horizontal, this returns {@link Float#MAX_VALUE}
+     *     <li>if the drag direction is 45 deg from vertical, this returns 1
+     *     <li>if the drag direction is 30 deg from vertical, this returns 0.58 (x delta is smaller
+     *     than y delta)
+     *     <li>if the drag direction is 60 deg from vertical, this returns 1.73 (x delta is bigger
+     *     than y delta)
+     * </ul>
+     * This function never returns negative values, regardless of the direction of the drag.
+     */
+    public float getInitialDragDirectionXYRatio() {
+        return mInitialDragDirectionXYRatio;
     }
 
     public void setIsOnHandle(boolean onHandle) {
@@ -155,7 +170,7 @@
             mLastDownY = event.getY();
             mLastDownMillis = event.getEventTime();
             mMovedEnoughForDrag = false;
-            mIsDragCloseToVertical = false;
+            mInitialDragDirectionXYRatio = 0.0f;
         } else if (action == MotionEvent.ACTION_UP) {
             if (TextView.DEBUG_CURSOR) {
                 logCursor("EditorTouchState", "ACTION_UP");
@@ -164,7 +179,7 @@
             mLastUpY = event.getY();
             mLastUpMillis = event.getEventTime();
             mMovedEnoughForDrag = false;
-            mIsDragCloseToVertical = false;
+            mInitialDragDirectionXYRatio = 0.0f;
         } else if (action == MotionEvent.ACTION_MOVE) {
             if (!mMovedEnoughForDrag) {
                 float deltaX = event.getX() - mLastDownX;
@@ -174,9 +189,8 @@
                 int touchSlop = config.getScaledTouchSlop();
                 mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
                 if (mMovedEnoughForDrag) {
-                    // If the direction of the swipe motion is within 45 degrees of vertical, it is
-                    // considered a vertical drag.
-                    mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
+                    mInitialDragDirectionXYRatio = (deltaY == 0) ? Float.MAX_VALUE :
+                            Math.abs(deltaX / deltaY);
                 }
             }
         } else if (action == MotionEvent.ACTION_CANCEL) {
@@ -185,7 +199,7 @@
             mMultiTapStatus = MultiTapStatus.NONE;
             mMultiTapInSameArea = false;
             mMovedEnoughForDrag = false;
-            mIsDragCloseToVertical = false;
+            mInitialDragDirectionXYRatio = 0.0f;
         }
     }
 
@@ -201,4 +215,27 @@
         float distanceSquared = (deltaX * deltaX) + (deltaY * deltaY);
         return distanceSquared <= maxDistance * maxDistance;
     }
+
+    /**
+     * Returns the x/y ratio corresponding to the given angle relative to vertical. Smaller angle
+     * values (ie, closer to vertical) will result in a smaller x/y ratio. For example:
+     * <ul>
+     *     <li>if the angle is 45 deg, the ratio is 1
+     *     <li>if the angle is 30 deg, the ratio is 0.58 (x delta is smaller than y delta)
+     *     <li>if the angle is 60 deg, the ratio is 1.73 (x delta is bigger than y delta)
+     * </ul>
+     * If the passed-in value is <= 0, this function returns 0. If the passed-in value is >= 90,
+     * this function returns {@link Float#MAX_VALUE}.
+     *
+     * @see #getInitialDragDirectionXYRatio()
+     */
+    public static float getXYRatio(int angleFromVerticalInDegrees) {
+        if (angleFromVerticalInDegrees <= 0) {
+            return 0.0f;
+        }
+        if (angleFromVerticalInDegrees >= 90) {
+            return Float.MAX_VALUE;
+        }
+        return (float) Math.tan(Math.toRadians(angleFromVerticalInDegrees));
+    }
 }
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 89206fda..c72eed4 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -149,9 +149,6 @@
     private int mLeftCutWidth = 0;
     // The width of the cut region on the right edge of the pixel copy source rect.
     private int mRightCutWidth = 0;
-    // The horizontal bounds of the content source in pixels, relative to the view.
-    private int mLeftBound = Integer.MIN_VALUE;
-    private int mRightBound = Integer.MAX_VALUE;
     // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
     private final int mRamp;
 
@@ -244,18 +241,6 @@
     }
 
     /**
-     * Sets the horizontal bounds of the source when showing the magnifier.
-     * This is used for new style magnifier. e.g. limit the source bounds by the text line bounds.
-     *
-     * @param left the left of the bounds, relative to the view.
-     * @param right the right of the bounds, relative to the view.
-     */
-    void setSourceHorizontalBounds(int left, int right) {
-        mLeftBound = left;
-        mRightBound = right;
-    }
-
-    /**
      * Shows the magnifier on the screen. The method takes the coordinates of the center
      * of the content source going to be magnified and copied to the magnifier. The coordinates
      * are relative to the top left corner of the magnified view. The magnifier will be
@@ -280,6 +265,14 @@
                 sourceCenterY + mDefaultVerticalSourceToMagnifierOffset);
     }
 
+    private Drawable mCursorDrawable;
+    private boolean mDrawCursorEnabled;
+
+    void setDrawCursor(boolean enabled, Drawable cursorDrawable) {
+        mDrawCursorEnabled = enabled;
+        mCursorDrawable = cursorDrawable;
+    }
+
     /**
      * Shows the magnifier on the screen at a position that is independent from its content
      * position. The first two arguments represent the coordinates of the center of the
@@ -309,8 +302,7 @@
             magnifierCenterX = mClampedCenterZoomCoords.x - mViewCoordinatesInSurface[0];
             magnifierCenterY = mClampedCenterZoomCoords.y - mViewCoordinatesInSurface[1];
 
-            // mLeftBound & mRightBound (typically the text line left/right) is for magnified
-            // content. However the PixelCopy requires the pre-magnified bounds.
+            // PixelCopy requires the pre-magnified bounds.
             // The below logic calculates the leftBound & rightBound for the pre-magnified bounds.
             final float rampPre =
                     (mSourceWidth - (mSourceWidth - 2 * mRamp) / mZoom) / 2;
@@ -318,7 +310,7 @@
             // Calculates the pre-zoomed left edge.
             // The leftEdge moves from the left of view towards to sourceCenterX, considering the
             // fisheye-like zooming.
-            final float x0 = sourceCenterX - mSourceWidth / 2;
+            final float x0 = sourceCenterX - mSourceWidth / 2f;
             final float rampX0 = x0 + mRamp;
             float leftEdge = 0;
             if (leftEdge > rampX0) {
@@ -330,12 +322,12 @@
                 // increase per ramp zoom (ramp / rampPre).
                 leftEdge = x0 + rampPre - (rampX0 - leftEdge) * rampPre / mRamp;
             }
-            int leftBound = Math.min(Math.max((int) leftEdge, mLeftBound), mRightBound);
+            int leftBound = Math.min((int) leftEdge, mView.getWidth());
 
             // Calculates the pre-zoomed right edge.
             // The rightEdge moves from the right of view towards to sourceCenterX, considering the
             // fisheye-like zooming.
-            final float x1 = sourceCenterX + mSourceWidth / 2;
+            final float x1 = sourceCenterX + mSourceWidth / 2f;
             final float rampX1 = x1 - mRamp;
             float rightEdge = mView.getWidth();
             if (rightEdge < rampX1) {
@@ -347,7 +339,7 @@
                 // increase per ramp zoom (ramp / rampPre).
                 rightEdge = x1 - rampPre + (rightEdge - rampX1) * rampPre / mRamp;
             }
-            int rightBound = Math.max(leftBound, Math.min((int) rightEdge, mRightBound));
+            int rightBound = Math.max(leftBound, (int) rightEdge);
 
             // Gets the startX for new style, which should be bounded by the horizontal bounds.
             // Also calculates the left/right cut width for pixel copy.
@@ -772,6 +764,23 @@
         }
     }
 
+    private void maybeDrawCursor(Canvas canvas) {
+        if (mDrawCursorEnabled) {
+            if (mCursorDrawable != null) {
+                mCursorDrawable.setBounds(
+                        mSourceWidth / 2, 0,
+                        mSourceWidth / 2 + mCursorDrawable.getIntrinsicWidth(), mSourceHeight);
+                mCursorDrawable.draw(canvas);
+            } else {
+                Paint paint = new Paint();
+                paint.setColor(Color.BLACK);  // The cursor on magnifier is by default in black.
+                canvas.drawRect(
+                        new Rect(mSourceWidth / 2 - 1, 0, mSourceWidth / 2 + 1, mSourceHeight),
+                        paint);
+            }
+        }
+    }
+
     private void performPixelCopy(final int startXInSurface, final int startYInSurface,
             final boolean updateWindowPosition) {
         if (mContentCopySurface.mSurface == null || !mContentCopySurface.mSurface.isValid()) {
@@ -827,8 +836,10 @@
                             final Rect dstRect = new Rect(mLeftCutWidth, 0,
                                     mSourceWidth - mRightCutWidth, bitmap.getHeight());
                             can.drawBitmap(bitmap, null, dstRect, null);
+                            maybeDrawCursor(can);
                             mWindow.updateContent(newBitmap);
                         } else {
+                            maybeDrawCursor(new Canvas(bitmap));
                             mWindow.updateContent(bitmap);
                         }
                     }
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index 832dd51..1a49365 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -41,6 +41,28 @@
     public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true;
 
     /**
+     * Threshold for the direction of a swipe gesture in order for it to be handled as a cursor drag
+     * rather than a scroll. The direction angle of the swipe gesture must exceed this value in
+     * order to trigger cursor drag; otherwise, the swipe will be assumed to be a scroll gesture.
+     * The value units for this flag is degrees and the valid range is [0,90] inclusive. If a value
+     * < 0 is set, 0 will be used instead; if a value > 90 is set, 90 will be used instead.
+     */
+    public static final String CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL =
+            "CursorControlFeature__min_angle_from_vertical_to_start_cursor_drag";
+
+    /**
+     * The key used in app core settings for the flag
+     * {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}.
+     */
+    public static final String KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL =
+            "widget__min_angle_from_vertical_to_start_cursor_drag";
+
+    /**
+     * Default value for the flag {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}.
+     */
+    public static final int CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT = 45;
+
+    /**
      * The flag of finger-to-cursor distance in DP for cursor dragging.
      * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy
      * value, which is based on handle size, will be used.
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index 42724be..f08d0ef8 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -83,63 +83,25 @@
     /**
      * Converts between the int brightness system and the float brightness system.
      */
-    public static float brightnessIntToFloat(Context context, int brightnessInt) {
-        final PowerManager pm = context.getSystemService(PowerManager.class);
-        final float pmMinBrightness = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-        final float pmMaxBrightness = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-        final int minBrightnessInt = Math.round(brightnessFloatToIntRange(pmMinBrightness,
-                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
-                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON));
-        final int maxBrightnessInt = Math.round(brightnessFloatToIntRange(pmMaxBrightness,
-                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
-                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON));
-
-        return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt,
-                pmMinBrightness, pmMaxBrightness);
-    }
-
-    /**
-     * Converts between the int brightness system and the float brightness system.
-     */
-    public static float brightnessIntToFloat(int brightnessInt, int minInt, int maxInt,
-            float minFloat, float maxFloat) {
+    public static float brightnessIntToFloat(int brightnessInt) {
         if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
             return PowerManager.BRIGHTNESS_OFF_FLOAT;
         } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
         } else {
-            return MathUtils.constrainedMap(minFloat, maxFloat, (float) minInt, (float) maxInt,
-                    brightnessInt);
+            final float minFloat = PowerManager.BRIGHTNESS_MIN;
+            final float maxFloat = PowerManager.BRIGHTNESS_MAX;
+            final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
+            final float maxInt = PowerManager.BRIGHTNESS_ON;
+            return MathUtils.constrainedMap(minFloat, maxFloat, minInt, maxInt, brightnessInt);
         }
     }
 
     /**
      * Converts between the float brightness system and the int brightness system.
      */
-    public static int brightnessFloatToInt(Context context, float brightnessFloat) {
-        return Math.round(brightnessFloatToIntRange(context, brightnessFloat));
-    }
-
-    /**
-     * Converts between the float brightness system and the int brightness system, but returns
-     * the converted value as a float within the int-system's range. This method helps with
-     * conversions from one system to the other without losing the floating-point precision.
-     */
-    public static float brightnessFloatToIntRange(Context context, float brightnessFloat) {
-        final PowerManager pm = context.getSystemService(PowerManager.class);
-        final float minFloat = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-        final float maxFloat = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-        final float minInt = brightnessFloatToIntRange(minFloat,
-                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
-                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON);
-        final float maxInt = brightnessFloatToIntRange(maxFloat,
-                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
-                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON);
-        return brightnessFloatToIntRange(brightnessFloat, minFloat, maxFloat, minInt, maxInt);
+    public static int brightnessFloatToInt(float brightnessFloat) {
+        return Math.round(brightnessFloatToIntRange(brightnessFloat));
     }
 
     /**
@@ -148,20 +110,24 @@
      * Value returned as a float privimite (to preserve precision), but is a value within the
      * int-system range.
      */
-    private static float brightnessFloatToIntRange(float brightnessFloat, float minFloat,
-            float maxFloat, float minInt, float maxInt) {
+    public static float brightnessFloatToIntRange(float brightnessFloat) {
         if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
             return PowerManager.BRIGHTNESS_OFF;
         } else if (Float.isNaN(brightnessFloat)) {
             return PowerManager.BRIGHTNESS_INVALID;
         } else {
+            final float minFloat = PowerManager.BRIGHTNESS_MIN;
+            final float maxFloat = PowerManager.BRIGHTNESS_MAX;
+            final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
+            final float maxInt = PowerManager.BRIGHTNESS_ON;
             return MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat, brightnessFloat);
         }
     }
 
     private static float getScreenBrightnessFloat(Context context) {
         return Settings.System.getFloatForUser(context.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_FLOAT, Float.NaN, UserHandle.USER_CURRENT);
+                Settings.System.SCREEN_BRIGHTNESS_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                UserHandle.USER_CURRENT);
     }
 
     private static int getScreenBrightnessInt(Context context) {
@@ -185,10 +151,10 @@
         if (topOfQueue != null && topOfQueue.equals(value)) {
             mWriteHistory.poll();
         } else {
-            if (brightnessFloatToInt(mContext, mPreferredSettingValue) == value) {
+            if (brightnessFloatToInt(mPreferredSettingValue) == value) {
                 return;
             }
-            float newBrightnessFloat = brightnessIntToFloat(mContext, value);
+            float newBrightnessFloat = brightnessIntToFloat(value);
             mWriteHistory.offer(newBrightnessFloat);
             mPreferredSettingValue = newBrightnessFloat;
             Settings.System.putFloatForUser(mContext.getContentResolver(),
@@ -207,7 +173,7 @@
      * @param value Brightness setting as float to store in int setting.
      */
     private void updateBrightnessIntFromFloat(float value) {
-        int newBrightnessInt = brightnessFloatToInt(mContext, value);
+        int newBrightnessInt = brightnessFloatToInt(value);
         Object topOfQueue = mWriteHistory.peek();
         if (topOfQueue != null && topOfQueue.equals(value)) {
             mWriteHistory.poll();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3a89dcd..fd90b56 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -206,6 +206,7 @@
     public static final int SELECTION_TYPE_APP = 2;
     public static final int SELECTION_TYPE_STANDARD = 3;
     public static final int SELECTION_TYPE_COPY = 4;
+    public static final int SELECTION_TYPE_NEARBY = 5;
 
     private static final int SCROLL_STATUS_IDLE = 0;
     private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1;
@@ -784,8 +785,8 @@
                 FrameworkStatsLog.SHARESHEET_STARTED,
                 getReferrerPackageName(),
                 target.getType(),
-                initialIntents == null ? 0 : initialIntents.length,
                 mCallerChooserTargets == null ? 0 : mCallerChooserTargets.length,
+                initialIntents == null ? 0 : initialIntents.length,
                 isWorkProfile(),
                 findPreferredContentPreview(getTargetIntent(), getContentResolver()),
                 target.getAction()
@@ -1135,7 +1136,8 @@
         return displayContentPreview(previewType, targetIntent, getLayoutInflater(), parent);
     }
 
-    private ComponentName getNearbySharingComponent() {
+    @VisibleForTesting
+    protected ComponentName getNearbySharingComponent() {
         String nearbyComponent = Settings.Secure.getString(
                 getContentResolver(),
                 Settings.Secure.NEARBY_SHARING_COMPONENT);
@@ -1148,7 +1150,8 @@
         return ComponentName.unflattenFromString(nearbyComponent);
     }
 
-    private TargetInfo getNearbySharingTarget(Intent originalIntent) {
+    @VisibleForTesting
+    protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
         final ComponentName cn = getNearbySharingComponent();
         if (cn == null) return null;
 
@@ -1216,14 +1219,21 @@
         final TargetInfo ti = getNearbySharingTarget(originalIntent);
         if (ti == null) return null;
 
-        return createActionButton(
+        final Button b = createActionButton(
                 ti.getDisplayIcon(this),
                 ti.getDisplayLabel(),
                 (View unused) -> {
+                    // Log share completion via nearby
+                    getChooserActivityLogger().logShareTargetSelected(
+                            SELECTION_TYPE_NEARBY,
+                            "",
+                            -1);
                     safelyStartActivity(ti);
                     finish();
                 }
         );
+        b.setId(R.id.chooser_nearby_button);
+        return b;
     }
 
     private void addActionButton(ViewGroup parent, Button b) {
@@ -2616,7 +2626,9 @@
         }
     }
 
-    static final class EmptyTargetInfo extends NotSelectableTargetInfo {
+    protected static final class EmptyTargetInfo extends NotSelectableTargetInfo {
+        public EmptyTargetInfo() {}
+
         public Drawable getDisplayIcon(Context context) {
             return null;
         }
@@ -3134,7 +3146,9 @@
         // ends up disabled. That's because at some point the old tab's vertical scrolling is
         // disabled and the new tab's is enabled. For context, see b/159997845
         setVerticalScrollEnabled(true);
-        mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
+        if (mResolverDrawerLayout != null) {
+            mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java
index c26bac4..426859e 100644
--- a/core/java/com/android/internal/app/ChooserActivityLogger.java
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -116,7 +116,9 @@
         @UiEvent(doc = "User selected a standard target.")
         SHARESHEET_STANDARD_TARGET_SELECTED(234),
         @UiEvent(doc = "User selected the copy target.")
-        SHARESHEET_COPY_TARGET_SELECTED(235);
+        SHARESHEET_COPY_TARGET_SELECTED(235),
+        @UiEvent(doc = "User selected the nearby target.")
+        SHARESHEET_NEARBY_TARGET_SELECTED(626);
 
         private final int mId;
         SharesheetTargetSelectedEvent(int id) {
@@ -136,6 +138,8 @@
                     return SHARESHEET_STANDARD_TARGET_SELECTED;
                 case ChooserActivity.SELECTION_TYPE_COPY:
                     return SHARESHEET_COPY_TARGET_SELECTED;
+                case ChooserActivity.SELECTION_TYPE_NEARBY:
+                    return SHARESHEET_NEARBY_TARGET_SELECTED;
                 default:
                     return INVALID;
             }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index ea3d2de..eb59f0f 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -123,10 +123,15 @@
     // Flag related to Privacy Indicators
 
     /**
-     * Whether the Permissions Hub is showing.
+     * Whether to show the complete ongoing app ops chip.
      */
     public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";
 
+    /**
+     * Whether to show app ops chip for just microphone + camera.
+     */
+    public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled";
+
     // Flags related to Assistant
 
     /**
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index b3ea118..2620ba0 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -129,6 +129,7 @@
     public double videoPowerMah;
     public double wakeLockPowerMah;
     public double wifiPowerMah;
+    public double systemServiceCpuPowerMah;
 
     //                           ****************
     // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
@@ -242,6 +243,7 @@
         videoPowerMah += other.videoPowerMah;
         proportionalSmearMah += other.proportionalSmearMah;
         totalSmearedPowerMah += other.totalSmearedPowerMah;
+        systemServiceCpuPowerMah += other.systemServiceCpuPowerMah;
     }
 
     /**
@@ -253,7 +255,8 @@
     public double sumPower() {
         totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
                 sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
-                flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah;
+                flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah
+                + systemServiceCpuPowerMah;
         totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
 
         return totalPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index b131ab8..3dfa3c3 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -133,6 +133,7 @@
     private double mMaxDrainedPower;
 
     PowerCalculator mCpuPowerCalculator;
+    SystemServicePowerCalculator mSystemServicePowerCalculator;
     PowerCalculator mWakelockPowerCalculator;
     MobileRadioPowerCalculator mMobileRadioPowerCalculator;
     PowerCalculator mWifiPowerCalculator;
@@ -396,6 +397,11 @@
         }
         mCpuPowerCalculator.reset();
 
+        if (mSystemServicePowerCalculator == null) {
+            mSystemServicePowerCalculator = new SystemServicePowerCalculator(mPowerProfile, mStats);
+        }
+        mSystemServicePowerCalculator.reset();
+
         if (mMemoryPowerCalculator == null) {
             mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile);
         }
@@ -588,6 +594,8 @@
             mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
                     mStatsType);
             mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
+            mSystemServicePowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
+                    mStatsType);
 
             final double totalPower = app.sumPower();
             if (DEBUG && totalPower != 0) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 58ba16b..8498151 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -144,6 +144,7 @@
     private static final boolean DEBUG = false;
     public static final boolean DEBUG_ENERGY = false;
     private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
+    private static final boolean DEBUG_BINDER_STATS = true;
     private static final boolean DEBUG_MEMORY = false;
     private static final boolean DEBUG_HISTORY = false;
     private static final boolean USE_OLD_HISTORY = false;   // for debugging.
@@ -154,7 +155,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0);
+    static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -218,10 +219,13 @@
             new KernelCpuUidClusterTimeReader(true);
     @VisibleForTesting
     protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
+    @VisibleForTesting
+    protected SystemServerCpuThreadReader mSystemServerCpuThreadReader;
 
     private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
             = new KernelMemoryBandwidthStats();
     private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
+
     public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
         return mKernelMemoryStats;
     }
@@ -267,6 +271,7 @@
 
     /** Container for Rail Energy Data stats. */
     private final RailStats mTmpRailStats = new RailStats();
+
     /**
      * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
      * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
@@ -1007,6 +1012,16 @@
 
     private long[] mCpuFreqs;
 
+    /**
+     * Times spent by the system server threads grouped by cluster and CPU speed.
+     */
+    private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs;
+
+    /**
+     * Times spent by the system server threads handling incoming binder requests.
+     */
+    private LongSamplingCounter[][] mBinderThreadCpuTimesUs;
+
     @VisibleForTesting
     protected PowerProfile mPowerProfile;
 
@@ -6131,10 +6146,77 @@
      * the power consumption to the calling app.
      */
     public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
-            Collection<BinderCallsStats.CallStat> callStats) {
+            Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
         synchronized (this) {
             getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount,
                     callStats);
+            mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
+        }
+    }
+
+    /**
+     * Estimates the proportion of system server CPU activity handling incoming binder calls
+     * that can be attributed to each app
+     */
+    @VisibleForTesting
+    public void updateSystemServiceCallStats() {
+        // Start off by computing the average duration of recorded binder calls,
+        // regardless of which binder or transaction. We will use this as a fallback
+        // for calls that were not sampled at all.
+        int totalRecordedCallCount = 0;
+        long totalRecordedCallTimeMicros = 0;
+        for (int i = 0; i < mUidStats.size(); i++) {
+            Uid uid = mUidStats.valueAt(i);
+            ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
+            for (int j = binderCallStats.size() - 1; j >= 0; j--) {
+                BinderCallStats stats = binderCallStats.valueAt(j);
+                totalRecordedCallCount += stats.recordedCallCount;
+                totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros;
+            }
+        }
+
+        long totalSystemServiceTimeMicros = 0;
+
+        // For every UID, use recorded durations of sampled binder calls to estimate
+        // the total time the system server spent handling requests from this UID.
+        for (int i = 0; i < mUidStats.size(); i++) {
+            Uid uid = mUidStats.valueAt(i);
+
+            long totalTimeForUid = 0;
+            int totalCallCountForUid = 0;
+            ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
+            for (int j = binderCallStats.size() - 1; j >= 0; j--) {
+                BinderCallStats stats = binderCallStats.valueAt(j);
+                totalCallCountForUid += stats.callCount;
+                if (stats.recordedCallCount > 0) {
+                    totalTimeForUid +=
+                            stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount;
+                } else if (totalRecordedCallCount > 0) {
+                    totalTimeForUid +=
+                            stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount;
+                }
+            }
+
+            if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) {
+                // Estimate remaining calls, which were not tracked because of binder call
+                // stats sampling
+                totalTimeForUid +=
+                        (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros
+                                / totalRecordedCallCount;
+            }
+
+            uid.mSystemServiceTimeUs = totalTimeForUid;
+            totalSystemServiceTimeMicros += totalTimeForUid;
+        }
+
+        for (int i = 0; i < mUidStats.size(); i++) {
+            Uid uid = mUidStats.valueAt(i);
+            if (totalSystemServiceTimeMicros > 0) {
+                uid.mProportionalSystemServiceUsage =
+                        (double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros;
+            } else {
+                uid.mProportionalSystemServiceUsage = 0;
+            }
         }
     }
 
@@ -6583,7 +6665,7 @@
     /**
      * Accumulates stats for a specific binder transaction.
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @VisibleForTesting
     protected static class BinderCallStats {
         static final Comparator<BinderCallStats> COMPARATOR =
                 Comparator.comparing(BinderCallStats::getClassName)
@@ -6822,6 +6904,16 @@
          */
         private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
 
+        /**
+         * Estimated total time spent by the system server handling requests from this uid.
+         */
+        private long mSystemServiceTimeUs;
+
+        /**
+         * Estimated proportion of system server binder call CPU cost for this uid.
+         */
+        private double mProportionalSystemServiceUsage;
+
         public Uid(BatteryStatsImpl bsi, int uid) {
             mBsi = bsi;
             mUid = uid;
@@ -6899,7 +6991,6 @@
             return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
         }
 
-
         @Override
         public long[] getCpuFreqTimes(int which, int procState) {
             if (which < 0 || which >= NUM_PROCESS_STATE) {
@@ -6934,10 +7025,16 @@
             return mBinderCallCount;
         }
 
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
         public ArraySet<BinderCallStats> getBinderCallStats() {
             return mBinderCallStats;
         }
 
+        @Override
+        public  double getProportionalSystemServiceUsage() {
+            return mProportionalSystemServiceUsage;
+        }
+
         public void addIsolatedUid(int isolatedUid) {
             if (mChildUids == null) {
                 mChildUids = new IntArray();
@@ -8029,9 +8126,12 @@
             mBinderCallCount = 0;
             mBinderCallStats.clear();
 
+            mProportionalSystemServiceUsage = 0;
+
             mLastStepUserTime = mLastStepSystemTime = 0;
             mCurStepUserTime = mCurStepSystemTime = 0;
 
+
             return !active;
         }
 
@@ -8373,28 +8473,7 @@
             mUserCpuTime.writeToParcel(out);
             mSystemCpuTime.writeToParcel(out);
 
-            if (mCpuClusterSpeedTimesUs != null) {
-                out.writeInt(1);
-                out.writeInt(mCpuClusterSpeedTimesUs.length);
-                for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
-                    if (cpuSpeeds != null) {
-                        out.writeInt(1);
-                        out.writeInt(cpuSpeeds.length);
-                        for (LongSamplingCounter c : cpuSpeeds) {
-                            if (c != null) {
-                                out.writeInt(1);
-                                c.writeToParcel(out);
-                            } else {
-                                out.writeInt(0);
-                            }
-                        }
-                    } else {
-                        out.writeInt(0);
-                    }
-                }
-            } else {
-                out.writeInt(0);
-            }
+            mBsi.writeCpuSpeedCountersToParcel(out, mCpuClusterSpeedTimesUs);
 
             LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
             LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
@@ -8432,6 +8511,7 @@
             } else {
                 out.writeInt(0);
             }
+            out.writeDouble(mProportionalSystemServiceUsage);
         }
 
         void readJobCompletionsFromParcelLocked(Parcel in) {
@@ -8692,36 +8772,7 @@
             mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
             mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
 
-            if (in.readInt() != 0) {
-                int numCpuClusters = in.readInt();
-                if (mBsi.mPowerProfile != null && mBsi.mPowerProfile.getNumCpuClusters() != numCpuClusters) {
-                    throw new ParcelFormatException("Incompatible number of cpu clusters");
-                }
-
-                mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][];
-                for (int cluster = 0; cluster < numCpuClusters; cluster++) {
-                    if (in.readInt() != 0) {
-                        int numSpeeds = in.readInt();
-                        if (mBsi.mPowerProfile != null &&
-                                mBsi.mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
-                            throw new ParcelFormatException("Incompatible number of cpu speeds");
-                        }
-
-                        final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
-                        mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds;
-                        for (int speed = 0; speed < numSpeeds; speed++) {
-                            if (in.readInt() != 0) {
-                                cpuSpeeds[speed] = new LongSamplingCounter(
-                                        mBsi.mOnBatteryTimeBase, in);
-                            }
-                        }
-                    } else {
-                        mCpuClusterSpeedTimesUs[cluster] = null;
-                    }
-                }
-            } else {
-                mCpuClusterSpeedTimesUs = null;
-            }
+            mCpuClusterSpeedTimesUs = mBsi.readCpuSpeedCountersFromParcel(in);
 
             mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase);
             mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
@@ -8762,6 +8813,8 @@
             } else {
                 mWifiRadioApWakeupCount = null;
             }
+
+            mProportionalSystemServiceUsage = in.readDouble();
         }
 
         public void noteJobsDeferredLocked(int numDeferred, long sinceLast) {
@@ -9904,7 +9957,6 @@
             UserInfoProvider userInfoProvider) {
         init(clocks);
 
-
         if (systemDir == null) {
             mStatsFile = null;
             mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
@@ -10046,6 +10098,8 @@
             firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
         }
 
+        mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
+
         if (mEstimatedBatteryCapacity == -1) {
             // Initialize the estimated battery capacity to a known preset one.
             mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
@@ -10726,6 +10780,9 @@
 
         mTmpRailStats.reset();
 
+        resetIfNotNull(mSystemServerThreadCpuTimesUs, false);
+        resetIfNotNull(mBinderThreadCpuTimesUs, false);
+
         mLastHistoryStepDetails = null;
         mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
         mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
@@ -10853,7 +10910,7 @@
         return null;
     }
 
-   /**
+    /**
      * Distribute WiFi energy info and network traffic to apps.
      * @param info The energy information from the WiFi controller.
      */
@@ -11772,6 +11829,7 @@
             for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
                 mKernelCpuSpeedReaders[cluster].readDelta();
             }
+            mSystemServerCpuThreadReader.readDelta();
             return;
         }
 
@@ -11791,6 +11849,87 @@
             readKernelUidCpuClusterTimesLocked(onBattery);
             mNumAllUidCpuTimeReads += 2;
         }
+
+        updateSystemServerThreadStats();
+    }
+
+    /**
+     * Estimates the proportion of the System Server CPU activity (per cluster per speed)
+     * spent on handling incoming binder calls.
+     */
+    @VisibleForTesting
+    public void updateSystemServerThreadStats() {
+        // There are some simplifying assumptions made in this algorithm
+        // 1) We assume that if a thread handles incoming binder calls, all of its activity
+        //    is spent doing that.  Most incoming calls are handled by threads allocated
+        //    by the native layer in the binder thread pool, so this assumption is reasonable.
+        // 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU
+        //    cost. In reality, in multi-core CPUs, the CPU cost may not be linearly
+        //    affected by additional threads.
+
+        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+                    mSystemServerCpuThreadReader.readDelta();
+
+        int index = 0;
+        int numCpuClusters = mPowerProfile.getNumCpuClusters();
+        if (mSystemServerThreadCpuTimesUs == null) {
+            mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
+            mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
+        }
+        for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+            int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+            if (mSystemServerThreadCpuTimesUs[cluster] == null) {
+                mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
+                mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
+                for (int speed = 0; speed < numSpeeds; speed++) {
+                    mSystemServerThreadCpuTimesUs[cluster][speed] =
+                            new LongSamplingCounter(mOnBatteryTimeBase);
+                    mBinderThreadCpuTimesUs[cluster][speed] =
+                            new LongSamplingCounter(mOnBatteryTimeBase);
+                }
+            }
+            for (int speed = 0; speed < numSpeeds; speed++) {
+                mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked(
+                        systemServiceCpuThreadTimes.threadCpuTimesUs[index]);
+                mBinderThreadCpuTimesUs[cluster][speed].addCountLocked(
+                        systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]);
+                index++;
+            }
+        }
+        if (DEBUG_BINDER_STATS) {
+            Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
+            long binderThreadTime = 0;
+            long totalThreadTime = 0;
+            int cpuIndex = 0;
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("cpu").append(cpuIndex).append(": [");
+                int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+                for (int speed = 0; speed < numSpeeds; speed++) {
+                    if (speed != 0) {
+                        sb.append(", ");
+                    }
+                    long totalCount = mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked(
+                            0) / 1000;
+                    long binderCount = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0)
+                            / 1000;
+                    sb.append(String.format("%d/%d(%.1f%%)",
+                            binderCount,
+                            totalCount,
+                            totalCount != 0 ? (double) binderCount * 100 / totalCount : 0));
+
+                    totalThreadTime += totalCount;
+                    binderThreadTime += binderCount;
+                    index++;
+                }
+                cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
+                Slog.d(TAG, sb.toString());
+            }
+            Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTime);
+            Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
+                    binderThreadTime,
+                    binderThreadTime != 0 ? (double) binderThreadTime * 100 / totalThreadTime : 0));
+        }
     }
 
     /**
@@ -12998,6 +13137,75 @@
         }
     }
 
+
+    @Override
+    public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) {
+        // Estimates the time spent by the system server handling incoming binder requests.
+        //
+        // The data that we can get from the kernel is this:
+        //   - CPU duration for a (thread - cluster - CPU speed) combination
+        //   - CPU duration for a (UID - cluster - CPU speed) combination
+        //
+        // The configuration we have in the Power Profile is this:
+        //   - Average CPU power for a (cluster - CPU speed) combination.
+        //
+        // The model used by BatteryStats can be illustrated with this example:
+        //
+        // - Let's say the system server has 10 threads.
+        // - These 10 threads spent 1000 ms of CPU time in aggregate
+        // - Of the 10 threads 4 were execute exclusively incoming binder calls.
+        // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
+        // - The real time spent by the system server UID doing all of this is, say, 200 ms.
+        //
+        // We will assume that power consumption is proportional to the time spent by the CPU
+        // across all threads.  This is a crude assumption, but we don't have more detailed data.
+        // Thus,
+        //   binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime
+        //
+        // In our example,
+        //   binderRealTime = 200 * 600 / 1000 = 120ms
+        //
+        // We can then multiply this estimated time by the average power to obtain an estimate
+        // of the total power consumed by incoming binder calls for the given cluster/speed
+        // combination.
+
+        if (mSystemServerThreadCpuTimesUs == null) {
+            return 0;
+        }
+
+        if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) {
+            return 0;
+        }
+
+        final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster];
+
+        if (step < 0 || step >= threadTimesForCluster.length) {
+            return 0;
+        }
+
+        Uid systemUid = mUidStats.get(Process.SYSTEM_UID);
+        if (systemUid == null) {
+            return 0;
+        }
+
+        final long uidTimeAtCpuSpeed = systemUid.getTimeAtCpuSpeed(cluster, step,
+                BatteryStats.STATS_SINCE_CHARGED);
+        if (uidTimeAtCpuSpeed == 0) {
+            return 0;
+        }
+
+        final long uidThreadTime =
+                threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+
+        if (uidThreadTime == 0) {
+            return 0;
+        }
+
+        final long binderThreadTime = mBinderThreadCpuTimesUs[cluster][step].getCountLocked(
+                BatteryStats.STATS_SINCE_CHARGED);
+        return uidTimeAtCpuSpeed * binderThreadTime / uidThreadTime;
+    }
+
     /**
      * Retrieve the statistics object for a particular uid, creating if needed.
      */
@@ -13327,45 +13535,7 @@
             pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
             pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
         }
-        pw.println("Per UID system service calls:");
-        BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver();
-        for (int i = 0; i < size; i++) {
-            int u = mUidStats.keyAt(i);
-            Uid uid = mUidStats.get(u);
-            long binderCallCount = uid.getBinderCallCount();
-            if (binderCallCount != 0) {
-                pw.print(" ");
-                pw.print(u);
-                pw.print(" system service calls: ");
-                pw.print(binderCallCount);
-                ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats();
-                if (!binderCallStats.isEmpty()) {
-                    pw.println(", including");
-                    BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()];
-                    binderCallStats.toArray(bcss);
-                    for (BinderCallStats bcs : bcss) {
-                        bcs.ensureMethodName(nameResolver);
-                    }
-                    Arrays.sort(bcss, BinderCallStats.COMPARATOR);
-                    for (BinderCallStats callStats : bcss) {
-                        pw.print("    ");
-                        pw.print(callStats.getClassName());
-                        pw.print('#');
-                        pw.print(callStats.getMethodName());
-                        pw.print(" calls: ");
-                        pw.print(callStats.callCount);
-                        if (callStats.recordedCallCount != 0) {
-                            pw.print(" time: ");
-                            pw.print(callStats.callCount * callStats.recordedCpuTimeMicros
-                                    / callStats.recordedCallCount / 1000);
-                        }
-                        pw.println();
-                    }
-                } else {
-                    pw.println();
-                }
-            }
-        }
+
         pw.println("Per UID CPU active time in ms:");
         for (int i = 0; i < size; i++) {
             int u = mUidStats.keyAt(i);
@@ -13390,6 +13560,30 @@
                 pw.print("  "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
             }
         }
+
+        updateSystemServiceCallStats();
+        if (mSystemServerThreadCpuTimesUs != null) {
+            pw.println("Per UID System server binder time in ms:");
+            for (int i = 0; i < size; i++) {
+                int u = mUidStats.keyAt(i);
+                Uid uid = mUidStats.get(u);
+                double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
+
+                long time = 0;
+                for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) {
+                    int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length;
+                    for (int speed = 0; speed < numSpeeds; speed++) {
+                        time += getSystemServiceTimeAtCpuSpeed(cluster, speed)
+                                * proportionalSystemServiceUsage;
+                    }
+                }
+
+                pw.print("  ");
+                pw.print(u);
+                pw.print(": ");
+                pw.println(time);
+            }
+        }
     }
 
     final ReentrantLock mWriteLock = new ReentrantLock();
@@ -14908,6 +15102,9 @@
             u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in);
             mUidStats.append(uid, u);
         }
+
+        mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
+        mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -14923,6 +15120,8 @@
         // Need to update with current kernel wake lock counts.
         pullPendingStateUpdatesLocked();
 
+        updateSystemServiceCallStats();
+
         // Pull the clock time.  This may update the time and make a new history entry
         // if we had originally pulled a time before the RTC was set.
         getStartClockTime();
@@ -15105,6 +15304,73 @@
         } else {
             out.writeInt(0);
         }
+        writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs);
+        writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs);
+    }
+
+    private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) {
+        if (counters == null) {
+            out.writeInt(0);
+            return;
+        }
+
+        out.writeInt(1);
+        out.writeInt(counters.length);
+        for (int i = 0; i < counters.length; i++) {
+            LongSamplingCounter[] counterArray = counters[i];
+            if (counterArray == null) {
+                out.writeInt(0);
+                continue;
+            }
+
+            out.writeInt(1);
+            out.writeInt(counterArray.length);
+            for (int j = 0; j < counterArray.length; j++) {
+                LongSamplingCounter c = counterArray[j];
+                if (c != null) {
+                    out.writeInt(1);
+                    c.writeToParcel(out);
+                } else {
+                    out.writeInt(0);
+                }
+            }
+        }
+    }
+
+    private LongSamplingCounter[][] readCpuSpeedCountersFromParcel(Parcel in) {
+        LongSamplingCounter[][] counters;
+        if (in.readInt() != 0) {
+            int numCpuClusters = in.readInt();
+            if (mPowerProfile != null
+                    && mPowerProfile.getNumCpuClusters() != numCpuClusters) {
+                throw new ParcelFormatException("Incompatible number of cpu clusters");
+            }
+
+            counters = new LongSamplingCounter[numCpuClusters][];
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                if (in.readInt() != 0) {
+                    int numSpeeds = in.readInt();
+                    if (mPowerProfile != null
+                            && mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
+                        throw new ParcelFormatException("Incompatible number of cpu speeds");
+                    }
+
+                    final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
+                    counters[cluster] = cpuSpeeds;
+                    for (int speed = 0; speed < numSpeeds; speed++) {
+                        if (in.readInt() != 0) {
+                            cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+                        }
+                    }
+                } else {
+                    counters[cluster] = null;
+                }
+            }
+        } else {
+            counters = null;
+        }
+
+        return counters;
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index e09ef49..f5bef0b 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -74,6 +74,10 @@
 
     // Whether to collect all the data: cpu + exceptions + reply/request sizes.
     private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
+    // If set to true, indicates that all transactions for specific UIDs are being
+    // recorded, ignoring sampling. The UidEntry.recordAllTransactions flag is also set
+    // for the UIDs being tracked.
+    private boolean mRecordingAllTransactionsForUid;
     // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
     // of 100 requests.
     private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
@@ -115,7 +119,8 @@
                     if (uidEntry != null) {
                         ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
                         mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
-                                uidEntry.incrementalCallCount, callStats.values());
+                                uidEntry.incrementalCallCount, callStats.values(),
+                                mNativeTids.toArray());
                         uidEntry.incrementalCallCount = 0;
                         for (int j = callStats.size() - 1; j >= 0; j--) {
                             callStats.valueAt(j).incrementalCallCount = 0;
@@ -177,7 +182,8 @@
     @Override
     @Nullable
     public CallSession callStarted(Binder binder, int code, int workSourceUid) {
-        if (mDeviceState == null || mDeviceState.isCharging()) {
+        if (!mRecordingAllTransactionsForUid
+                && (mDeviceState == null || mDeviceState.isCharging())) {
             return null;
         }
 
@@ -189,7 +195,9 @@
         s.exceptionThrown = false;
         s.cpuTimeStarted = -1;
         s.timeStarted = -1;
-        if (shouldRecordDetailedData()) {
+        s.recordedCall = shouldRecordDetailedData();
+
+        if (mRecordingAllTransactionsForUid || s.recordedCall) {
             s.cpuTimeStarted = getThreadTimeMicro();
             s.timeStarted = getElapsedRealtimeMicro();
         }
@@ -217,8 +225,17 @@
 
     private void processCallEnded(CallSession s,
             int parcelRequestSize, int parcelReplySize, int workSourceUid) {
-        // Non-negative time signals we need to record data for this call.
-        final boolean recordCall = s.cpuTimeStarted >= 0;
+        UidEntry uidEntry = null;
+        final boolean recordCall;
+        if (s.recordedCall) {
+            recordCall = true;
+        } else if (mRecordingAllTransactionsForUid) {
+            uidEntry = getUidEntry(workSourceUid);
+            recordCall = uidEntry.recordAllTransactions;
+        } else {
+            recordCall = false;
+        }
+
         final long duration;
         final long latencyDuration;
         if (recordCall) {
@@ -237,14 +254,17 @@
 
         synchronized (mLock) {
             // This was already checked in #callStart but check again while synchronized.
-            if (mDeviceState == null || mDeviceState.isCharging()) {
+            if (!mRecordingAllTransactionsForUid
+                    && (mDeviceState == null || mDeviceState.isCharging())) {
                 return;
             }
 
-            final UidEntry uidEntry = getUidEntry(workSourceUid);
+            if (uidEntry == null) {
+                uidEntry = getUidEntry(workSourceUid);
+            }
+
             uidEntry.callCount++;
             uidEntry.incrementalCallCount++;
-
             if (recordCall) {
                 uidEntry.cpuTimeMicros += duration;
                 uidEntry.recordedCallCount++;
@@ -356,28 +376,67 @@
             for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) {
                 final UidEntry entry = mUidEntries.valueAt(entryIdx);
                 for (CallStat stat : entry.getCallStatsList()) {
-                    ExportedCallStat exported = new ExportedCallStat();
-                    exported.workSourceUid = entry.workSourceUid;
-                    exported.callingUid = stat.callingUid;
-                    exported.className = stat.binderClass.getName();
-                    exported.binderClass = stat.binderClass;
-                    exported.transactionCode = stat.transactionCode;
-                    exported.screenInteractive = stat.screenInteractive;
-                    exported.cpuTimeMicros = stat.cpuTimeMicros;
-                    exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
-                    exported.latencyMicros = stat.latencyMicros;
-                    exported.maxLatencyMicros = stat.maxLatencyMicros;
-                    exported.recordedCallCount = stat.recordedCallCount;
-                    exported.callCount = stat.callCount;
-                    exported.maxRequestSizeBytes = stat.maxRequestSizeBytes;
-                    exported.maxReplySizeBytes = stat.maxReplySizeBytes;
-                    exported.exceptionCount = stat.exceptionCount;
-                    resultCallStats.add(exported);
+                    resultCallStats.add(getExportedCallStat(entry.workSourceUid, stat));
                 }
             }
         }
 
         // Resolve codes outside of the lock since it can be slow.
+        resolveBinderMethodNames(resultCallStats);
+
+        // Debug entries added to help validate the data.
+        if (mAddDebugEntries && mBatteryStopwatch != null) {
+            resultCallStats.add(createDebugEntry("start_time_millis", mStartElapsedTime));
+            resultCallStats.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime()));
+            resultCallStats.add(
+                    createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
+            resultCallStats.add(createDebugEntry("sampling_interval", mPeriodicSamplingInterval));
+        }
+
+        return resultCallStats;
+    }
+
+    /**
+     * This method is expensive to call.
+     */
+    public ArrayList<ExportedCallStat> getExportedCallStats(int workSourceUid) {
+        ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
+        synchronized (mLock) {
+            final UidEntry entry = getUidEntry(workSourceUid);
+            for (CallStat stat : entry.getCallStatsList()) {
+                resultCallStats.add(getExportedCallStat(workSourceUid, stat));
+            }
+        }
+
+        // Resolve codes outside of the lock since it can be slow.
+        resolveBinderMethodNames(resultCallStats);
+
+        return resultCallStats;
+    }
+
+    private ExportedCallStat getExportedCallStat(int workSourceUid, CallStat stat) {
+        ExportedCallStat exported = new ExportedCallStat();
+        exported.workSourceUid = workSourceUid;
+        exported.callingUid = stat.callingUid;
+        exported.className = stat.binderClass.getName();
+        exported.binderClass = stat.binderClass;
+        exported.transactionCode = stat.transactionCode;
+        exported.screenInteractive = stat.screenInteractive;
+        exported.cpuTimeMicros = stat.cpuTimeMicros;
+        exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
+        exported.latencyMicros = stat.latencyMicros;
+        exported.maxLatencyMicros = stat.maxLatencyMicros;
+        exported.recordedCallCount = stat.recordedCallCount;
+        exported.callCount = stat.callCount;
+        exported.maxRequestSizeBytes = stat.maxRequestSizeBytes;
+        exported.maxReplySizeBytes = stat.maxReplySizeBytes;
+        exported.exceptionCount = stat.exceptionCount;
+        return exported;
+    }
+
+    private void resolveBinderMethodNames(
+            ArrayList<ExportedCallStat> resultCallStats) {
+        // Resolve codes outside of the lock since it can be slow.
         ExportedCallStat previous = null;
         String previousMethodName = null;
         resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode);
@@ -397,17 +456,6 @@
             exported.methodName = methodName;
             previous = exported;
         }
-
-        // Debug entries added to help validate the data.
-        if (mAddDebugEntries && mBatteryStopwatch != null) {
-            resultCallStats.add(createDebugEntry("start_time_millis", mStartElapsedTime));
-            resultCallStats.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime()));
-            resultCallStats.add(
-                    createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
-            resultCallStats.add(createDebugEntry("sampling_interval", mPeriodicSamplingInterval));
-        }
-
-        return resultCallStats;
     }
 
     private ExportedCallStat createDebugEntry(String variableName, long value) {
@@ -431,33 +479,24 @@
     }
 
     /** Writes the collected statistics to the supplied {@link PrintWriter}.*/
-    public void dump(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
+    public void dump(PrintWriter pw, AppIdToPackageMap packageMap, int workSourceUid,
+            boolean verbose) {
         synchronized (mLock) {
-            dumpLocked(pw, packageMap, verbose);
+            dumpLocked(pw, packageMap, workSourceUid, verbose);
         }
     }
 
-    private void dumpLocked(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
-        long totalCallsCount = 0;
-        long totalRecordedCallsCount = 0;
-        long totalCpuTime = 0;
+    private void dumpLocked(PrintWriter pw, AppIdToPackageMap packageMap, int workSourceUid,
+            boolean verbose) {
+        if (workSourceUid != Process.INVALID_UID) {
+            verbose = true;
+        }
         pw.print("Start time: ");
         pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartCurrentTime));
         pw.print("On battery time (ms): ");
         pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
         pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
-        final List<UidEntry> entries = new ArrayList<>();
 
-        final int uidEntriesSize = mUidEntries.size();
-        for (int i = 0; i < uidEntriesSize; i++) {
-            UidEntry e = mUidEntries.valueAt(i);
-            entries.add(e);
-            totalCpuTime += e.cpuTimeMicros;
-            totalRecordedCallsCount += e.recordedCallCount;
-            totalCallsCount += e.callCount;
-        }
-
-        entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
         final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
         final StringBuilder sb = new StringBuilder();
         pw.println("Per-UID raw data " + datasetSizeDesc
@@ -466,10 +505,15 @@
                 + "latency_time_micros, max_latency_time_micros, exception_count, "
                 + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
                 + "call_count):");
-        final List<ExportedCallStat> exportedCallStats = getExportedCallStats();
+        final List<ExportedCallStat> exportedCallStats;
+        if (workSourceUid != Process.INVALID_UID) {
+            exportedCallStats = getExportedCallStats(workSourceUid);
+        } else {
+            exportedCallStats = getExportedCallStats();
+        }
         exportedCallStats.sort(BinderCallsStats::compareByCpuDesc);
         for (ExportedCallStat e : exportedCallStats) {
-            if (e.methodName.startsWith(DEBUG_ENTRY_PREFIX)) {
+            if (e.methodName != null && e.methodName.startsWith(DEBUG_ENTRY_PREFIX)) {
                 // Do not dump debug entries.
                 continue;
             }
@@ -493,6 +537,30 @@
             pw.println(sb);
         }
         pw.println();
+        final List<UidEntry> entries = new ArrayList<>();
+        long totalCallsCount = 0;
+        long totalRecordedCallsCount = 0;
+        long totalCpuTime = 0;
+
+        if (workSourceUid != Process.INVALID_UID) {
+            UidEntry e = getUidEntry(workSourceUid);
+            entries.add(e);
+            totalCpuTime += e.cpuTimeMicros;
+            totalRecordedCallsCount += e.recordedCallCount;
+            totalCallsCount += e.callCount;
+        } else {
+            final int uidEntriesSize = mUidEntries.size();
+            for (int i = 0; i < uidEntriesSize; i++) {
+                UidEntry e = mUidEntries.valueAt(i);
+                entries.add(e);
+                totalCpuTime += e.cpuTimeMicros;
+                totalRecordedCallsCount += e.recordedCallCount;
+                totalCallsCount += e.callCount;
+            }
+            entries.sort(
+                    Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
+        }
+
         pw.println("Per-UID Summary " + datasetSizeDesc
                 + "(cpu_time, % of total cpu_time, recorded_call_count, call_count, package/uid):");
         final List<UidEntry> summaryEntries = verbose ? entries
@@ -504,10 +572,13 @@
                     entry.recordedCallCount, entry.callCount, uidStr));
         }
         pw.println();
-        pw.println(String.format("  Summary: total_cpu_time=%d, "
-                        + "calls_count=%d, avg_call_cpu_time=%.0f",
-                totalCpuTime, totalCallsCount, (double) totalCpuTime / totalRecordedCallsCount));
-        pw.println();
+        if (workSourceUid == Process.INVALID_UID) {
+            pw.println(String.format("  Summary: total_cpu_time=%d, "
+                            + "calls_count=%d, avg_call_cpu_time=%.0f",
+                    totalCpuTime, totalCallsCount,
+                    (double) totalCpuTime / totalRecordedCallsCount));
+            pw.println();
+        }
 
         pw.println("Exceptions thrown (exception_count, class_name):");
         final List<Pair<String, Integer>> exceptionEntries = new ArrayList<>();
@@ -589,6 +660,22 @@
         }
     }
 
+    /**
+     * Marks the specified work source UID for total binder call tracking: detailed information
+     * will be recorded for all calls from this source ID.
+     *
+     * This is expensive and can cause memory pressure, therefore this mode should only be used
+     * for debugging.
+     */
+    public void recordAllCallsForWorkSourceUid(int workSourceUid) {
+        setDetailedTracking(true);
+
+        Slog.i(TAG, "Recording all Binder calls for UID: "  + workSourceUid);
+        UidEntry uidEntry = getUidEntry(workSourceUid);
+        uidEntry.recordAllTransactions = true;
+        mRecordingAllTransactionsForUid = true;
+    }
+
     public void setAddDebugEntries(boolean addDebugEntries) {
         mAddDebugEntries = addDebugEntries;
     }
@@ -636,6 +723,7 @@
             if (mBatteryStopwatch != null) {
                 mBatteryStopwatch.reset();
             }
+            mRecordingAllTransactionsForUid = false;
         }
     }
 
@@ -766,6 +854,8 @@
         public long cpuTimeMicros;
         // Call count that gets reset after delivery to BatteryStats
         public long incrementalCallCount;
+        // Indicates that all transactions for the UID must be tracked
+        public boolean recordAllTransactions;
 
         UidEntry(int uid) {
             this.workSourceUid = uid;
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index feb5aab..2645b8e 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -85,9 +85,10 @@
         long timeStarted;
         // Should be set to one when an exception is thrown.
         boolean exceptionThrown;
+        // Detailed information should be recorded for this call when it ends.
+        public boolean recordedCall;
     }
 
-
     /**
      * Responsible for resolving a work source.
      */
@@ -142,7 +143,8 @@
          * Notes incoming binder call stats associated with this work source UID.
          */
         void noteCallStats(int workSourceUid, long incrementalCallCount,
-                Collection<BinderCallsStats.CallStat> callStats);
+                Collection<BinderCallsStats.CallStat> callStats,
+                int[] binderThreadNativeTids);
     }
 
     /**
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3407670..2ba372a 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -225,19 +225,22 @@
 
     /** Set the number of frequency buckets to use */
     void setNumBuckets(int numBuckets) {
-        if (numBuckets < 1) {
-            Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
-            return;
-        }
         // If `numBuckets` hasn't changed since the last set, do nothing
         if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
             return;
         }
-        mFrequencyBucketCreator =
-                new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
-        mFrequenciesKhz =
-                mFrequencyBucketCreator.bucketFrequencies(
-                        mProcTimeInStateReader.getFrequenciesKhz());
+
+        final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
+        if (numBuckets != 0) {
+            mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, numBuckets);
+            mFrequenciesKhz = mFrequencyBucketCreator.bucketFrequencies(frequenciesKhz);
+        } else {
+            mFrequencyBucketCreator = null;
+            mFrequenciesKhz = new int[frequenciesKhz.length];
+            for (int i = 0; i < frequenciesKhz.length; i++) {
+                mFrequenciesKhz[i] = (int) frequenciesKhz[i];
+            }
+        }
     }
 
     /** Set the UID predicate for {@link #getProcessCpuUsage} */
@@ -320,8 +323,15 @@
         if (cpuUsagesLong == null) {
             return null;
         }
-        int[] cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong);
-
+        final int[] cpuUsages;
+        if (mFrequencyBucketCreator != null) {
+            cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong);
+        } else {
+            cpuUsages = new int[cpuUsagesLong.length];
+            for (int i = 0; i < cpuUsagesLong.length; i++) {
+                cpuUsages[i] = (int) cpuUsagesLong[i];
+            }
+        }
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
new file mode 100644
index 0000000..1cdd42c
--- /dev/null
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -0,0 +1,151 @@
+/*
+ * 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.internal.os;
+
+import android.os.Process;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
+ * by various threads of the System Server.
+ */
+public class SystemServerCpuThreadReader {
+    private KernelCpuThreadReader mKernelCpuThreadReader;
+    private int[] mBinderThreadNativeTids;
+
+    private int[] mThreadCpuTimesUs;
+    private int[] mBinderThreadCpuTimesUs;
+    private long[] mLastThreadCpuTimesUs;
+    private long[] mLastBinderThreadCpuTimesUs;
+
+    /**
+     * Times (in microseconds) spent by the system server UID.
+     */
+    public static class SystemServiceCpuThreadTimes {
+        // All threads
+        public long[] threadCpuTimesUs;
+        // Just the threads handling incoming binder calls
+        public long[] binderThreadCpuTimesUs;
+    }
+
+    private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes();
+
+    /**
+     * Creates a configured instance of SystemServerCpuThreadReader.
+     */
+    public static SystemServerCpuThreadReader create() {
+        return new SystemServerCpuThreadReader(
+                KernelCpuThreadReader.create(0, uid -> uid == Process.myUid()));
+    }
+
+    @VisibleForTesting
+    public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException {
+        this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null,
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return systemServerUid;
+                    }
+                }));
+    }
+
+    @VisibleForTesting
+    public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) {
+        mKernelCpuThreadReader = kernelCpuThreadReader;
+    }
+
+    public void setBinderThreadNativeTids(int[] nativeTids) {
+        mBinderThreadNativeTids = nativeTids;
+    }
+
+    /**
+     * Returns delta of CPU times, per thread, since the previous call to this method.
+     */
+    public SystemServiceCpuThreadTimes readDelta() {
+        if (mBinderThreadCpuTimesUs == null) {
+            int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length;
+            mThreadCpuTimesUs = new int[numCpuFrequencies];
+            mBinderThreadCpuTimesUs = new int[numCpuFrequencies];
+
+            mLastThreadCpuTimesUs = new long[numCpuFrequencies];
+            mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
+
+            mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
+            mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
+        }
+
+        Arrays.fill(mThreadCpuTimesUs, 0);
+        Arrays.fill(mBinderThreadCpuTimesUs, 0);
+
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage =
+                mKernelCpuThreadReader.getProcessCpuUsage();
+        int processCpuUsageSize = processCpuUsage.size();
+        for (int i = 0; i < processCpuUsageSize; i++) {
+            KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i);
+            ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages;
+            if (threadCpuUsages != null) {
+                int threadCpuUsagesSize = threadCpuUsages.size();
+                for (int j = 0; j < threadCpuUsagesSize; j++) {
+                    KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j);
+                    boolean isBinderThread = isBinderThread(tcu.threadId);
+
+                    final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length);
+                    for (int k = 0; k < len; k++) {
+                        int usageTimeUs = tcu.usageTimesMillis[k] * 1000;
+                        mThreadCpuTimesUs[k] += usageTimeUs;
+                        if (isBinderThread) {
+                            mBinderThreadCpuTimesUs[k] += usageTimeUs;
+                        }
+                    }
+                }
+            }
+        }
+
+        for (int i = 0; i < mThreadCpuTimesUs.length; i++) {
+            if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) {
+                mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i];
+                mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
+            } else {
+                mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
+                        mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i];
+                mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
+                        mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i];
+            }
+            mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i];
+            mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
+        }
+
+        return mDeltaCpuThreadTimes;
+    }
+
+    private boolean isBinderThread(int threadId) {
+        if (mBinderThreadNativeTids != null) {
+            for (int i = 0; i < mBinderThreadNativeTids.length; i++) {
+                if (threadId == mBinderThreadNativeTids[i]) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
new file mode 100644
index 0000000..481b901
--- /dev/null
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -0,0 +1,91 @@
+/*
+ * 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.internal.os;
+
+import android.os.BatteryStats;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Estimates the amount of power consumed by the System Server handling requests from
+ * a given app.
+ */
+public class SystemServicePowerCalculator extends PowerCalculator {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "SystemServicePowerCalc";
+
+    private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
+
+    private final PowerProfile mPowerProfile;
+    private final BatteryStats mBatteryStats;
+    // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds
+    private double[][] mSystemServicePowerMaUs;
+
+    public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) {
+        mPowerProfile = powerProfile;
+        mBatteryStats = batteryStats;
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+            long rawUptimeUs, int statsType) {
+        final double proportionalUsage = u.getProportionalSystemServiceUsage();
+        if (proportionalUsage > 0) {
+            if (mSystemServicePowerMaUs == null) {
+                updateSystemServicePower();
+            }
+
+            double cpuPowerMaUs = 0;
+            int numCpuClusters = mPowerProfile.getNumCpuClusters();
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+                for (int speed = 0; speed < numSpeeds; speed++) {
+                    cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage;
+                }
+            }
+
+            app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
+        }
+    }
+
+    private void updateSystemServicePower() {
+        final int numCpuClusters = mPowerProfile.getNumCpuClusters();
+        mSystemServicePowerMaUs = new double[numCpuClusters][];
+        for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+            final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+            mSystemServicePowerMaUs[cluster] = new double[numSpeeds];
+            for (int speed = 0; speed < numSpeeds; speed++) {
+                mSystemServicePowerMaUs[cluster][speed] =
+                        mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed)
+                                * mPowerProfile.getAveragePowerForCpuCore(cluster, speed);
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "System service power per CPU cluster and frequency");
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                Log.d(TAG, "Cluster[" + cluster  + "]: "
+                        + Arrays.toString(mSystemServicePowerMaUs[cluster]));
+            }
+        }
+    }
+
+    @Override
+    public void reset() {
+        mSystemServicePowerMaUs = null;
+    }
+}
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index ba60fa5..b42ea7d 100644
--- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -16,14 +16,8 @@
 
 package com.android.internal.os.logging;
 
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.util.Pair;
 import android.view.WindowManager.LayoutParams;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.FrameworkStatsLog;
 
 /**
@@ -32,81 +26,6 @@
  */
 public class MetricsLoggerWrapper {
 
-    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
-    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
-
-    public static void logPictureInPictureDismissByTap(Context context,
-            Pair<ComponentName, Integer> topActivityInfo) {
-        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                METRIC_VALUE_DISMISSED_BY_TAP);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                getUid(context, topActivityInfo.first, topActivityInfo.second),
-                topActivityInfo.first.flattenToString(),
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
-    }
-
-    public static void logPictureInPictureDismissByDrag(Context context,
-            Pair<ComponentName, Integer> topActivityInfo) {
-        MetricsLogger.action(context,
-                MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                METRIC_VALUE_DISMISSED_BY_DRAG);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                getUid(context, topActivityInfo.first, topActivityInfo.second),
-                topActivityInfo.first.flattenToString(),
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
-    }
-
-    public static void logPictureInPictureMinimize(Context context, boolean isMinimized,
-            Pair<ComponentName, Integer> topActivityInfo) {
-        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
-                isMinimized);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                getUid(context, topActivityInfo.first, topActivityInfo.second),
-                topActivityInfo.first.flattenToString(),
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
-    }
-
-    /**
-     * Get uid from component name and user Id
-     * @return uid. -1 if not found.
-     */
-    private static int getUid(Context context, ComponentName componentName, int userId) {
-        int uid = -1;
-        if (componentName == null) {
-            return uid;
-        }
-        try {
-            uid = context.getPackageManager().getApplicationInfoAsUser(
-                    componentName.getPackageName(), 0, userId).uid;
-        } catch (NameNotFoundException e) {
-        }
-        return uid;
-    }
-
-    public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) {
-        MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
-                menuStateFull);
-    }
-
-    public static void logPictureInPictureEnter(Context context,
-            int uid, String shortComponentName, boolean supportsEnterPipOnTaskSwitch) {
-        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
-                supportsEnterPipOnTaskSwitch);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid,
-                shortComponentName,
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
-    }
-
-    public static void logPictureInPictureFullScreen(Context context, int uid,
-            String shortComponentName) {
-        MetricsLogger.action(context,
-                MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                uid,
-                shortComponentName,
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
-    }
-
     public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) {
         if (changed) {
             if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) {
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/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/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index e35fda1..d5d635d 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -47,8 +47,9 @@
     void resetKeyStore(int userId);
     VerifyCredentialResponse checkCredential(in LockscreenCredential credential, int userId,
             in ICheckCredentialProgressCallback progressCallback);
-    VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, long challenge, int userId);
-    VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, long challenge, int userId);
+    VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, int userId, int flags);
+    VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, int userId, int flags);
+    VerifyCredentialResponse verifyGatekeeperPassword(in byte[] gatekeeperPassword, long challenge, int userId);
     boolean checkVoldPassword(int userId);
     int getCredentialType(int userId);
     byte[] getHashFactor(in LockscreenCredential currentCredential, int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 85a45fd..5adbc58 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -1,5 +1,6 @@
 package com.android.internal.widget;
 
+import android.annotation.NonNull;
 import android.os.AsyncTask;
 
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
@@ -41,11 +42,11 @@
         /**
          * Invoked when a security verification is finished.
          *
-         * @param attestation The attestation that the challenge was verified, or null.
+         * @param response The response, optionally containing Gatekeeper HAT or Gatekeeper Password
          * @param throttleTimeoutMs The amount of time in ms to wait before reattempting
-         * the call. Only non-0 if attestation is null.
+         * the call. Only non-0 if the response is {@link VerifyCredentialResponse#RESPONSE_RETRY}.
          */
-        void onVerified(byte[] attestation, int throttleTimeoutMs);
+        void onVerified(@NonNull VerifyCredentialResponse response, int throttleTimeoutMs);
     }
 
     /**
@@ -53,33 +54,27 @@
      *
      * @param utils The LockPatternUtils instance to use.
      * @param credential The credential to check.
-     * @param challenge The challenge to verify against the credential.
      * @param userId The user to check against the credential.
+     * @param flags See {@link LockPatternUtils.VerifyFlag}
      * @param callback The callback to be invoked with the verification result.
      */
     public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils,
             final LockscreenCredential credential,
-            final long challenge,
             final int userId,
+            final @LockPatternUtils.VerifyFlag int flags,
             final OnVerifyCallback callback) {
         // Create a copy of the credential since checking credential is asynchrounous.
         final LockscreenCredential credentialCopy = credential.duplicate();
-        AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
-            private int mThrottleTimeout;
-
+        AsyncTask<Void, Void, VerifyCredentialResponse> task =
+                new AsyncTask<Void, Void, VerifyCredentialResponse>() {
             @Override
-            protected byte[] doInBackground(Void... args) {
-                try {
-                    return utils.verifyCredential(credentialCopy, challenge, userId);
-                } catch (RequestThrottledException ex) {
-                    mThrottleTimeout = ex.getTimeoutMs();
-                    return null;
-                }
+            protected VerifyCredentialResponse doInBackground(Void... args) {
+                return utils.verifyCredential(credentialCopy, userId, flags);
             }
 
             @Override
-            protected void onPostExecute(byte[] result) {
-                callback.onVerified(result, mThrottleTimeout);
+            protected void onPostExecute(@NonNull VerifyCredentialResponse result) {
+                callback.onVerified(result, result.getTimeout());
                 credentialCopy.zeroize();
             }
 
@@ -141,33 +136,27 @@
      *
      * @param utils The LockPatternUtils instance to use.
      * @param credential The credential to check.
-     * @param challenge The challenge to verify against the credential.
      * @param userId The user to check against the credential.
+     * @param flags See {@link LockPatternUtils.VerifyFlag}
      * @param callback The callback to be invoked with the verification result.
      */
     public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
             final LockscreenCredential credential,
-            final long challenge,
             final int userId,
+            final @LockPatternUtils.VerifyFlag int flags,
             final OnVerifyCallback callback) {
-        // Create a copy of the credential since checking credential is asynchrounous.
+        // Create a copy of the credential since checking credential is asynchronous.
         final LockscreenCredential credentialCopy = credential.duplicate();
-        AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
-            private int mThrottleTimeout;
-
+        AsyncTask<Void, Void, VerifyCredentialResponse> task =
+                new AsyncTask<Void, Void, VerifyCredentialResponse>() {
             @Override
-            protected byte[] doInBackground(Void... args) {
-                try {
-                    return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId);
-                } catch (RequestThrottledException ex) {
-                    mThrottleTimeout = ex.getTimeoutMs();
-                    return null;
-                }
+            protected VerifyCredentialResponse doInBackground(Void... args) {
+                return utils.verifyTiedProfileChallenge(credentialCopy, userId, flags);
             }
 
             @Override
-            protected void onPostExecute(byte[] result) {
-                callback.onVerified(result, mThrottleTimeout);
+            protected void onPostExecute(@NonNull VerifyCredentialResponse response) {
+                callback.onVerified(response, response.getTimeout());
                 credentialCopy.zeroize();
             }
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 93690cd..f7370d6 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -130,6 +130,18 @@
     public @interface CredentialType {}
 
     /**
+     * Flag provided to {@link #verifyCredential(LockscreenCredential, long, int, int)} . If set,
+     * the method will return the Gatekeeper Password in the {@link VerifyCredentialResponse}.
+     */
+    public static final int VERIFY_FLAG_RETURN_GK_PW = 1 << 0;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+            VERIFY_FLAG_RETURN_GK_PW
+    })
+    public @interface VerifyFlag {}
+
+    /**
      * Special user id for triggering the FRP verification flow.
      */
     public static final int USER_FRP = UserHandle.USER_NULL + 1;
@@ -374,29 +386,46 @@
      * If credential matches, return an opaque attestation that the challenge was verified.
      *
      * @param credential The credential to check.
-     * @param challenge The challenge to verify against the credential
      * @param userId The user whose credential is being verified
-     * @return the attestation that the challenge was verified, or null
-     * @throws RequestThrottledException if credential verification is being throttled due to
-     *         to many incorrect attempts.
+     * @param flags See {@link VerifyFlag}
      * @throws IllegalStateException if called on the main thread.
      */
-    public byte[] verifyCredential(@NonNull LockscreenCredential credential, long challenge,
-            int userId) throws RequestThrottledException {
+    @NonNull
+    public VerifyCredentialResponse verifyCredential(@NonNull LockscreenCredential credential,
+            int userId, @VerifyFlag int flags) {
         throwIfCalledOnMainThread();
         try {
-            VerifyCredentialResponse response = getLockSettings().verifyCredential(
-                    credential, challenge, userId);
-            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
-                return response.getPayload();
-            } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
-                throw new RequestThrottledException(response.getTimeout());
+            final VerifyCredentialResponse response = getLockSettings().verifyCredential(
+                    credential, userId, flags);
+            if (response == null) {
+                return VerifyCredentialResponse.ERROR;
             } else {
-                return null;
+                return response;
             }
         } catch (RemoteException re) {
             Log.e(TAG, "failed to verify credential", re);
-            return null;
+            return VerifyCredentialResponse.ERROR;
+        }
+    }
+
+    /**
+     * With the Gatekeeper Password returned via {@link #verifyCredential(LockscreenCredential,
+     * int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping the given
+     * challenge.
+     */
+    @NonNull
+    public VerifyCredentialResponse verifyGatekeeperPassword(@NonNull byte[] gatekeeperPassword,
+            long challenge, int userId) {
+        try {
+            final VerifyCredentialResponse response = getLockSettings().verifyGatekeeperPassword(
+                    gatekeeperPassword, challenge, userId);
+            if (response == null) {
+                return VerifyCredentialResponse.ERROR;
+            }
+            return response;
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to verify gatekeeper password", e);
+            return VerifyCredentialResponse.ERROR;
         }
     }
 
@@ -418,8 +447,9 @@
         try {
             VerifyCredentialResponse response = getLockSettings().checkCredential(
                     credential, userId, wrapCallback(progressCallback));
-
-            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+            if (response == null) {
+                return false;
+            } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                 return true;
             } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
                 throw new RequestThrottledException(response.getTimeout());
@@ -439,30 +469,26 @@
      * verified.
      *
      * @param credential The parent user's credential to check.
-     * @param challenge The challenge to verify against the credential
      * @return the attestation that the challenge was verified, or null
      * @param userId The managed profile user id
-     * @throws RequestThrottledException if credential verification is being throttled due to
-     *         to many incorrect attempts.
+     * @param flags See {@link VerifyFlag}
      * @throws IllegalStateException if called on the main thread.
      */
-    public byte[] verifyTiedProfileChallenge(@NonNull LockscreenCredential credential,
-            long challenge, int userId) throws RequestThrottledException {
+    @NonNull
+    public VerifyCredentialResponse verifyTiedProfileChallenge(
+            @NonNull LockscreenCredential credential, int userId, @VerifyFlag int flags) {
         throwIfCalledOnMainThread();
         try {
-            VerifyCredentialResponse response =
-                    getLockSettings().verifyTiedProfileChallenge(credential, challenge, userId);
-
-            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
-                return response.getPayload();
-            } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
-                throw new RequestThrottledException(response.getTimeout());
+            final VerifyCredentialResponse response = getLockSettings()
+                    .verifyTiedProfileChallenge(credential, userId, flags);
+            if (response == null) {
+                return VerifyCredentialResponse.ERROR;
             } else {
-                return null;
+                return response;
             }
         } catch (RemoteException re) {
             Log.e(TAG, "failed to verify tied profile credential", re);
-            return null;
+            return VerifyCredentialResponse.ERROR;
         }
     }
 
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 55f30fb..a488449 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -48,7 +48,7 @@
  *     // Process the credential in some way
  * }
  * </pre>
- * With this construct, we can garantee that there will be no copies of the password left in
+ * With this construct, we can guarantee that there will be no copies of the password left in
  * memory when the credential goes out of scope. This should help mitigate certain class of
  * attacks where the attcker gains read-only access to full device memory (cold boot attack,
  * unsecured software/hardware memory dumping interfaces such as JTAG).
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index 7d1c706..e09eb42 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -16,11 +16,16 @@
 
 package com.android.internal.widget;
 
+import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.util.Slog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Response object for a ILockSettings credential verification request.
  * @hide
@@ -30,78 +35,114 @@
     public static final int RESPONSE_ERROR = -1;
     public static final int RESPONSE_OK = 0;
     public static final int RESPONSE_RETRY = 1;
+    @IntDef({RESPONSE_ERROR,
+            RESPONSE_OK,
+            RESPONSE_RETRY})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ResponseCode {}
 
-    public static final VerifyCredentialResponse OK = new VerifyCredentialResponse();
-    public static final VerifyCredentialResponse ERROR
-            = new VerifyCredentialResponse(RESPONSE_ERROR, 0, null);
+    public static final VerifyCredentialResponse OK = new VerifyCredentialResponse.Builder()
+            .build();
+    public static final VerifyCredentialResponse ERROR = fromError();
     private static final String TAG = "VerifyCredentialResponse";
 
-    private int mResponseCode;
-    private byte[] mPayload;
-    private int mTimeout;
+    private final @ResponseCode int mResponseCode;
+    private final int mTimeout;
+    @Nullable private final byte[] mGatekeeperHAT;
+    @Nullable private final byte[] mGatekeeperPw;
 
     public static final Parcelable.Creator<VerifyCredentialResponse> CREATOR
             = new Parcelable.Creator<VerifyCredentialResponse>() {
         @Override
         public VerifyCredentialResponse createFromParcel(Parcel source) {
-            int responseCode = source.readInt();
-            VerifyCredentialResponse response = new VerifyCredentialResponse(responseCode, 0, null);
-            if (responseCode == RESPONSE_RETRY) {
-                response.setTimeout(source.readInt());
-            } else if (responseCode == RESPONSE_OK) {
-                int size = source.readInt();
-                if (size > 0) {
-                    byte[] payload = new byte[size];
-                    source.readByteArray(payload);
-                    response.setPayload(payload);
-                }
-            }
-            return response;
+            final @ResponseCode int responseCode = source.readInt();
+            final int timeout = source.readInt();
+            final byte[] gatekeeperHAT = source.createByteArray();
+            final byte[] gatekeeperPassword = source.createByteArray();
+
+            return new VerifyCredentialResponse(responseCode, timeout, gatekeeperHAT,
+                    gatekeeperPassword);
         }
 
         @Override
         public VerifyCredentialResponse[] newArray(int size) {
             return new VerifyCredentialResponse[size];
         }
-
     };
 
-    public VerifyCredentialResponse() {
-        mResponseCode = RESPONSE_OK;
-        mPayload = null;
+    public static class Builder {
+        @Nullable private byte[] mGatekeeperHAT;
+        @Nullable private byte[] mGatekeeperPassword;
+
+        /**
+         * @param gatekeeperHAT Gatekeeper HardwareAuthToken, minted upon successful authentication.
+         */
+        public Builder setGatekeeperHAT(byte[] gatekeeperHAT) {
+            mGatekeeperHAT = gatekeeperHAT;
+            return this;
+        }
+
+        public Builder setGatekeeperPassword(byte[] gatekeeperPassword) {
+            mGatekeeperPassword = gatekeeperPassword;
+            return this;
+        }
+
+        /**
+         * Builds a VerifyCredentialResponse with {@link #RESPONSE_OK} and any other parameters
+         * that were preveiously set.
+         * @return
+         */
+        public VerifyCredentialResponse build() {
+            return new VerifyCredentialResponse(RESPONSE_OK,
+                    0 /* timeout */,
+                    mGatekeeperHAT,
+                    mGatekeeperPassword);
+        }
     }
 
-
-    public VerifyCredentialResponse(byte[] payload) {
-        mPayload = payload;
-        mResponseCode = RESPONSE_OK;
+    /**
+     * Since timeouts are always an error, provide a way to create the VerifyCredentialResponse
+     * object directly. None of the other fields (Gatekeeper HAT, Gatekeeper Password, etc)
+     * are valid in this case. Similarly, the response code will always be
+     * {@link #RESPONSE_RETRY}.
+     */
+    public static VerifyCredentialResponse fromTimeout(int timeout) {
+        return new VerifyCredentialResponse(RESPONSE_RETRY,
+                timeout,
+                null /* gatekeeperHAT */,
+                null /* gatekeeperPassword */);
     }
 
-    public VerifyCredentialResponse(int timeout) {
-        mTimeout = timeout;
-        mResponseCode = RESPONSE_RETRY;
-        mPayload = null;
+    /**
+     * Since error (incorrect password) should never result in any of the other fields from
+     * being populated, provide a default method to return a VerifyCredentialResponse.
+     */
+    public static VerifyCredentialResponse fromError() {
+        return new VerifyCredentialResponse(RESPONSE_ERROR,
+                0 /* timeout */,
+                null /* gatekeeperHAT */,
+                null /* gatekeeperPassword */);
     }
 
-    private VerifyCredentialResponse(int responseCode, int timeout, byte[] payload) {
+    private VerifyCredentialResponse(@ResponseCode int responseCode, int timeout,
+            @Nullable byte[] gatekeeperHAT, @Nullable byte[] gatekeeperPassword) {
         mResponseCode = responseCode;
         mTimeout = timeout;
-        mPayload = payload;
+        mGatekeeperHAT = gatekeeperHAT;
+        mGatekeeperPw = gatekeeperPassword;
+    }
+
+    public VerifyCredentialResponse stripPayload() {
+        return new VerifyCredentialResponse(mResponseCode, mTimeout,
+                null /* gatekeeperHAT */, null /* gatekeeperPassword */);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mResponseCode);
-        if (mResponseCode == RESPONSE_RETRY) {
-            dest.writeInt(mTimeout);
-        } else if (mResponseCode == RESPONSE_OK) {
-            if (mPayload != null) {
-                dest.writeInt(mPayload.length);
-                dest.writeByteArray(mPayload);
-            } else {
-                dest.writeInt(0);
-            }
-        }
+        dest.writeInt(mTimeout);
+        dest.writeByteArray(mGatekeeperHAT);
+        dest.writeByteArray(mGatekeeperPw);
     }
 
     @Override
@@ -109,48 +150,51 @@
         return 0;
     }
 
-    public byte[] getPayload() {
-        return mPayload;
+    @Nullable
+    public byte[] getGatekeeperHAT() {
+        return mGatekeeperHAT;
+    }
+
+    @Nullable
+    public byte[] getGatekeeperPw() {
+        return mGatekeeperPw;
     }
 
     public int getTimeout() {
         return mTimeout;
     }
 
-    public int getResponseCode() {
+    public @ResponseCode int getResponseCode() {
         return mResponseCode;
     }
 
-    private void setTimeout(int timeout) {
-        mTimeout = timeout;
+    public boolean isMatched() {
+        return mResponseCode == RESPONSE_OK;
     }
 
-    private void setPayload(byte[] payload) {
-        mPayload = payload;
-    }
-
-    public VerifyCredentialResponse stripPayload() {
-        return new VerifyCredentialResponse(mResponseCode, mTimeout, new byte[0]);
+    @Override
+    public String toString() {
+        return "Response: " + mResponseCode
+                + ", GK HAT: " + (mGatekeeperHAT != null)
+                + ", GK PW: " + (mGatekeeperPw != null);
     }
 
     public static VerifyCredentialResponse fromGateKeeperResponse(
             GateKeeperResponse gateKeeperResponse) {
-        VerifyCredentialResponse response;
         int responseCode = gateKeeperResponse.getResponseCode();
         if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
-            response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
+            return fromTimeout(gateKeeperResponse.getTimeout());
         } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
             byte[] token = gateKeeperResponse.getPayload();
             if (token == null) {
                 // something's wrong if there's no payload with a challenge
                 Slog.e(TAG, "verifyChallenge response had no associated payload");
-                response = VerifyCredentialResponse.ERROR;
+                return fromError();
             } else {
-                response = new VerifyCredentialResponse(token);
+                return new VerifyCredentialResponse.Builder().setGatekeeperHAT(token).build();
             }
         } else {
-            response = VerifyCredentialResponse.ERROR;
+            return fromError();
         }
-        return response;
     }
 }
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_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 7daefd3..50a557bb 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -63,6 +63,7 @@
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
     void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
                                int32_t configId, nsecs_t vsyncPeriod) override;
+    void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
 };
 
 
@@ -95,8 +96,8 @@
     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
     if (receiverObj.get()) {
         ALOGV("receiver %p ~ Invoking vsync handler.", this);
-        env->CallVoidMethod(receiverObj.get(),
-                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
+        env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
+                            timestamp, displayId.value, count);
         ALOGV("receiver %p ~ Returned from vsync handler.", this);
     }
 
@@ -110,8 +111,8 @@
     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
     if (receiverObj.get()) {
         ALOGV("receiver %p ~ Invoking hotplug handler.", this);
-        env->CallVoidMethod(receiverObj.get(),
-                gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, displayId, connected);
+        env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchHotplug,
+                            timestamp, displayId.value, connected);
         ALOGV("receiver %p ~ Returned from hotplug handler.", this);
     }
 
@@ -126,9 +127,8 @@
                                       jniGetReferent(env, mReceiverWeakGlobal));
   if (receiverObj.get()) {
     ALOGV("receiver %p ~ Invoking config changed handler.", this);
-    env->CallVoidMethod(receiverObj.get(),
-                        gDisplayEventReceiverClassInfo.dispatchConfigChanged,
-                        timestamp, displayId, configId);
+    env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged,
+                        timestamp, displayId.value, configId);
     ALOGV("receiver %p ~ Returned from config changed handler.", this);
   }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 814a07e..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;
 }
 
@@ -703,7 +700,7 @@
 
     jlong* values = env->GetLongArrayElements(array, 0);
     for (size_t i = 0; i < displayIds.size(); ++i) {
-        values[i] = static_cast<jlong>(displayIds[i]);
+        values[i] = static_cast<jlong>(displayIds[i].value);
     }
 
     env->ReleaseLongArrayElements(array, values, 0);
@@ -711,7 +708,8 @@
 }
 
 static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
-    sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(physicalDisplayId);
+    sp<IBinder> token =
+            SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
     return javaObjectForIBinder(env, token);
 }
 
@@ -1848,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/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index eeda275..d629e0d 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -104,6 +104,31 @@
     return std::string(defaultValue);
 }
 
+static inline JNIEnv* GetJNIEnvironment(JavaVM* vm, jint version = JNI_VERSION_1_4) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), version) != JNI_OK) {
+        return nullptr;
+    }
+    return env;
+}
+
+static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm, jint version = JNI_VERSION_1_4) {
+    JNIEnv* env = GetJNIEnvironment(jvm, version);
+    if (!env) {
+        int result = jvm->AttachCurrentThread(&env, nullptr);
+        LOG_ALWAYS_FATAL_IF(result != JNI_OK, "JVM thread attach failed.");
+        struct VmDetacher {
+            VmDetacher(JavaVM* vm) : mVm(vm) {}
+            ~VmDetacher() { mVm->DetachCurrentThread(); }
+
+        private:
+            JavaVM* const mVm;
+        };
+        static thread_local VmDetacher detacher(jvm);
+    }
+    return env;
+}
+
 }  // namespace android
 
 #endif  // CORE_JNI_HELPERS
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 5b22e31..1014fbb 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2698,4 +2698,9 @@
     // CATEGORY: SETTINGS
     // OS: S
     EMERGENCY_SOS_GESTURE_SETTINGS = 1847;
+
+    // OPEN: Settings > System > Gestures > Double tap
+    // CATEGORY: SETTINGS
+    // OS: S
+    SETTINGS_COLUMBUS = 1848;
 }
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/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index cff1218..56736c0 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -976,7 +976,7 @@
     <string name="save_password_remember" msgid="6490888932657708341">"Запомніць"</string>
     <string name="save_password_never" msgid="6776808375903410659">"Ніколі"</string>
     <string name="open_permission_deny" msgid="5136793905306987251">"У вас няма дазволу на адкрыццё гэтай старонкі."</string>
-    <string name="text_copied" msgid="2531420577879738860">"Тэкст скапіяваны ў буфер абмену."</string>
+    <string name="text_copied" msgid="2531420577879738860">"Тэкст скапіраваны ў буфер абмену."</string>
     <string name="copied" msgid="4675902854553014676">"Скапіравана"</string>
     <string name="more_item_label" msgid="7419249600215749115">"Больш"</string>
     <string name="prepend_shortcut_label" msgid="1743716737502867951">"Меню+"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 2e4169e..4f55bf5 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -470,10 +470,10 @@
     <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Permet que l\'aplicació impedeixi que la tauleta entri en repòs."</string>
     <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Permet que l\'aplicació impedeixi que el dispositiu Android TV entri en repòs."</string>
     <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Permet que l\'aplicació impedeixi que el telèfon entri en repòs."</string>
-    <string name="permlab_transmitIr" msgid="8077196086358004010">"transmissió d\'infraroigs"</string>
-    <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Permet que l\'aplicació utilitzi el transmissor d\'infraroigs de la tauleta."</string>
-    <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Permet que l\'aplicació faci servir el transmissor d\'infraroigs del dispositiu Android TV."</string>
-    <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Permet que l\'aplicació utilitzi el transmissor d\'infraroigs del telèfon."</string>
+    <string name="permlab_transmitIr" msgid="8077196086358004010">"transmissió d\'infrarojos"</string>
+    <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Permet que l\'aplicació utilitzi el transmissor d\'infrarojos de la tauleta."</string>
+    <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Permet que l\'aplicació faci servir el transmissor d\'infrarojos del dispositiu Android TV."</string>
+    <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Permet que l\'aplicació utilitzi el transmissor d\'infrarojos del telèfon."</string>
     <string name="permlab_setWallpaper" msgid="6959514622698794511">"establir fons de pantalla"</string>
     <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Permet que l\'aplicació estableixi el fons de pantalla del sistema."</string>
     <string name="permlab_setWallpaperHints" msgid="1153485176642032714">"ajustament de la mida del fons de pantalla"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 27ff3f0..84f3243 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1937,7 +1937,7 @@
       <item quantity="one">Una sugerencia de Autocompletar</item>
     </plurals>
     <string name="autofill_save_title" msgid="7719802414283739775">"¿Quieres guardar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
-    <string name="autofill_save_title_with_type" msgid="3002460014579799605">"¿Quieres guardar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="3002460014579799605">"¿Quieres guardar la <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"¿Quieres guardar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"¿Quieres guardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g> en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
     <string name="autofill_update_title" msgid="3630695947047069136">"¿Quieres actualizar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index b115025..264a8fc 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1894,7 +1894,7 @@
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Այս հավելվածը ստեղծվել է Android-ի ավելի հին տարբերակի համար և կարող է պատշաճ չաշխատել: Ստուգեք թարմացումների առկայությունը կամ դիմեք մշակողին:"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Ստուգել նոր տարբերակի առկայությունը"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Դուք ունեք նոր հաղորդագրություններ"</string>
-    <string name="new_sms_notification_content" msgid="3197949934153460639">"Դիտելու համար բացել SMS հավելվածը"</string>
+    <string name="new_sms_notification_content" msgid="3197949934153460639">"Դիտելու համար բացել SMS-ների փոխանակման հավելվածը"</string>
     <string name="profile_encrypted_title" msgid="9001208667521266472">"Որոշ գործառույթներ կարող են չաշխատել"</string>
     <string name="profile_encrypted_detail" msgid="5279730442756849055">"Աշխատանքային պրոֆիլը կողպված է"</string>
     <string name="profile_encrypted_message" msgid="1128512616293157802">"Հպեք՝ այն ապակողպելու համար"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 688996d..085df65 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -866,12 +866,12 @@
     <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Sudah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali Anda salah menggambar pola pembuka kunci. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, Anda akan diminta membuka kunci perangkat Android TV menggunakan login Google.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
-    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string>
+    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, tablet akan direset ke setelan default pabrik dan semua data pengguna hilang."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal membuka kunci perangkat Android TV. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, perangkat Android TV akan direset ke default pabrik dan semua data pengguna akan hilang."</string>
-    <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini tablet akan disetel ulang ke setelan default pabrik."</string>
+    <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, ponsel akan direset ke setelan default pabrik dan semua data pengguna hilang."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini tablet akan direset ke setelan default pabrik."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal membuka kunci perangkat Android TV. Perangkat Android TV sekarang akan direset ke default pabrik."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini ponsel akan disetel ulang ke setelan default pabrik."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini ponsel akan direset ke setelan default pabrik."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Coba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> detik."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Lupa pola?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Pembuka kunci akun"</string>
@@ -1438,7 +1438,7 @@
     <string name="vpn_lockdown_config" msgid="8331697329868252169">"Ubah setelan jaringan atau VPN"</string>
     <string name="upload_file" msgid="8651942222301634271">"Pilih file"</string>
     <string name="no_file_chosen" msgid="4146295695162318057">"Tidak ada file yang dipilih"</string>
-    <string name="reset" msgid="3865826612628171429">"Setel ulang"</string>
+    <string name="reset" msgid="3865826612628171429">"Reset"</string>
     <string name="submit" msgid="862795280643405865">"Kirim"</string>
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikasi mengemudi sedang berjalan"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ketuk untuk keluar dari aplikasi mengemudi."</string>
@@ -1608,12 +1608,12 @@
     <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik PIN. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik sandi. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, tablet akan direset ke setelan default pabrik dan semua data pengguna akan hilang."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal membuka kunci perangkat Android TV. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, perangkat Android TV akan direset ke default pabrik dan semua data pengguna akan hilang."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
-    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Kini tablet akan disetel ulang ke setelan default pabrik."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, ponsel akan direset ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Kini tablet akan direset ke setelan default pabrik."</string>
     <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal membuka kunci perangkat Android TV. Perangkat Android TV sekarang akan direset ke default pabrik."</string>
-    <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha untuk membuka kunci ponsel. Kini ponsel akan disetel ulang ke setelan default pabrik."</string>
+    <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha untuk membuka kunci ponsel. Kini ponsel akan direset ke setelan default pabrik."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, Anda akan diminta membuka kunci perangkat Android TV menggunakan akun email.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
@@ -1907,7 +1907,7 @@
     <string name="app_info" msgid="6113278084877079851">"Info aplikasi"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"Memulai demo..."</string>
-    <string name="demo_restarting_message" msgid="1160053183701746766">"Menyetel ulang perangkat..."</string>
+    <string name="demo_restarting_message" msgid="1160053183701746766">"Mereset perangkat..."</string>
     <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> dinonaktifkan"</string>
     <string name="conference_call" msgid="5731633152336490471">"Konferensi Telepon"</string>
     <string name="tooltip_popup_title" msgid="7863719020269945722">"Keterangan alat"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 400c44a..14bfa03 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1937,7 +1937,7 @@
       <item quantity="one">Un suggerimento di Compilazione automatica</item>
     </plurals>
     <string name="autofill_save_title" msgid="7719802414283739775">"Vuoi salvare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
-    <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Vuoi salvare <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Vuoi salvare la <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Vuoi salvare <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> su "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Vuoi salvare <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> su "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
     <string name="autofill_update_title" msgid="3630695947047069136">"Vuoi aggiornare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 2c42745..2b6d226 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1202,7 +1202,7 @@
     <string name="aerr_application_repeated" msgid="7804378743218496566">"האפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> נעצרת שוב ושוב"</string>
     <string name="aerr_process_repeated" msgid="1153152413537954974">"האפליקציה <xliff:g id="PROCESS">%1$s</xliff:g> נעצרת שוב ושוב"</string>
     <string name="aerr_restart" msgid="2789618625210505419">"פתח שוב את האפליקציה"</string>
-    <string name="aerr_report" msgid="3095644466849299308">"משוב"</string>
+    <string name="aerr_report" msgid="3095644466849299308">"שליחת משוב"</string>
     <string name="aerr_close" msgid="3398336821267021852">"סגירה"</string>
     <string name="aerr_mute" msgid="2304972923480211376">"השתק עד הפעלה מחדש של המכשיר"</string>
     <string name="aerr_wait" msgid="3198677780474548217">"המתן"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index fd7b1b2..bb79c21 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1633,7 +1633,7 @@
     <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"निष्क्रिय"</string>
     <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> लाई तपाईंको यन्त्र पूर्ण रूपमा नियन्त्रण गर्न दिने हो?"</string>
     <string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"तपाईंले <xliff:g id="SERVICE">%1$s</xliff:g> सक्रिय गर्नुभयो भने तपाईंको यन्त्रले डेटा इन्क्रिप्ट गर्ने सुविधाको स्तरोन्नति गर्न तपाईंको स्क्रिन लक सुविधाको प्रयोग गर्ने छैन।"</string>
-    <string name="accessibility_service_warning_description" msgid="291674995220940133">"तपाईंलाई पहुँच राख्न आवश्यक पर्ने कुरामा सहयोग गर्ने अनुप्रयोगहरूमाथि पूर्ण नियन्त्रण गर्नु उपयुक्त हुन्छ तर अधिकांश अनुप्रयोगहरूका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।"</string>
+    <string name="accessibility_service_warning_description" msgid="291674995220940133">"तपाईंलाई पहुँच राख्न आवश्यक पर्ने कुरामा सहयोग गर्ने एपमाथि पूर्ण नियन्त्रण गर्नु उपयुक्त हुन्छ तर अधिकांश अनुप्रयोगहरूका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।"</string>
     <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रिन हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string>
     <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"यसले स्क्रिनमा देखिने सबै सामग्री पढ्न सक्छ र अन्य एपहरूमा उक्त सामग्री देखाउन सक्छ।"</string>
     <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"कारबाहीहरू हेर्नुहोस् र तिनमा कार्य गर्नुहोस्"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9f23a76..3ef51a7 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1165,7 +1165,7 @@
     <string name="capital_off" msgid="7443704171014626777">"IZKLOPLJENO"</string>
     <string name="checked" msgid="9179896827054513119">"potrjeno"</string>
     <string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Izvedba dejanja"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Odpiranje z aplikacijo"</string>
diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml
index 3a1679c..f46f70c 100644
--- a/core/res/res/values/donottranslate.xml
+++ b/core/res/res/values/donottranslate.xml
@@ -23,7 +23,7 @@
     <!-- @hide DO NOT TRANSLATE. Control aspect ratio of lock pattern -->
     <string name="lock_pattern_view_aspect">square</string>
     <!-- @hide DO NOT TRANSLATE. ICU pattern for "Mon, 14 January" -->
-    <string name="icu_abbrev_wday_month_day_no_year">eeeMMMMd</string>
+    <string name="icu_abbrev_wday_month_day_no_year">EEEMMMMd</string>
     <!-- @hide DO NOT TRANSLATE. date formatting pattern for system ui.-->
     <string name="system_ui_date_pattern">@string/icu_abbrev_wday_month_day_no_year</string>
     <!-- @hide DO NOT TRANSLATE Spans within this text are applied to style composing regions
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index bddda1b..f77c6f9 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -197,6 +197,9 @@
   <!-- Marks the "copy to clipboard" button in the ChooserActivity -->
   <item type="id" name="chooser_copy_button" />
 
+  <!-- Marks the "nearby" button in the ChooserActivity -->
+  <item type="id" name="chooser_nearby_button" />
+
   <!-- Accessibility action identifier for {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK}. -->
   <item type="id" name="accessibilitySystemActionBack" />
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d5c72da..040fad5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3841,6 +3841,7 @@
   <java-symbol type="layout" name="chooser_dialog_item" />
   <java-symbol type="drawable" name="chooser_dialog_background" />
   <java-symbol type="id" name="chooser_copy_button" />
+  <java-symbol type="id" name="chooser_nearby_button" />
   <java-symbol type="layout" name="chooser_action_button" />
   <java-symbol type="dimen" name="chooser_action_button_icon_size" />
   <java-symbol type="string" name="config_defaultNearbySharingComponent" />
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/backup/BackupAgentTest.java b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
new file mode 100644
index 0000000..ea903f2
--- /dev/null
+++ b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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 android.app.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.app.backup.BackupAgent.IncludeExcludeRules;
+import android.app.backup.BackupManager.OperationType;
+import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupAgentTest {
+    // An arbitrary user.
+    private static final UserHandle USER_HANDLE = new UserHandle(15);
+
+    @Mock FullBackup.BackupScheme mBackupScheme;
+
+    private BackupAgent mBackupAgent;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testGetIncludeExcludeRules_isMigration_returnsEmptyRules()  throws Exception {
+        mBackupAgent = getAgentForOperationType(OperationType.MIGRATION);
+
+        IncludeExcludeRules rules = mBackupAgent.getIncludeExcludeRules(mBackupScheme);
+        assertThat(rules).isEqualTo(IncludeExcludeRules.emptyRules());
+    }
+
+    @Test
+    public void testGetIncludeExcludeRules_isNotMigration_returnsRules() throws Exception {
+        PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags */ 0);
+        Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test",
+                Collections.singleton(path));
+        ArraySet<PathWithRequiredFlags> excludePaths = new ArraySet<>();
+        excludePaths.add(path);
+        IncludeExcludeRules expectedRules = new IncludeExcludeRules(includePaths, excludePaths);
+
+        mBackupAgent = getAgentForOperationType(OperationType.BACKUP);
+        when(mBackupScheme.maybeParseAndGetCanonicalExcludePaths()).thenReturn(excludePaths);
+        when(mBackupScheme.maybeParseAndGetCanonicalIncludePaths()).thenReturn(includePaths);
+
+        IncludeExcludeRules rules = mBackupAgent.getIncludeExcludeRules(mBackupScheme);
+        assertThat(rules).isEqualTo(expectedRules);
+    }
+
+    private BackupAgent getAgentForOperationType(@OperationType int operationType) {
+        BackupAgent agent = new TestFullBackupAgent();
+        agent.onCreate(USER_HANDLE, operationType);
+        return agent;
+    }
+
+    private static class TestFullBackupAgent extends BackupAgent {
+
+        @Override
+        public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+                ParcelFileDescriptor newState) throws IOException {
+            // Left empty as this is a full backup agent.
+        }
+
+        @Override
+        public void onRestore(BackupDataInput data, int appVersionCode,
+                ParcelFileDescriptor newState) throws IOException {
+            // Left empty as this is a full backup agent.
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index f11adef..e0d702e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -494,7 +494,8 @@
 
         @Override
         public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo,
-                CompatibilityInfo compatibilityInfo, int i, int userId) throws RemoteException {
+                CompatibilityInfo compatibilityInfo, int i, int userId, int operatioType)
+                throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 801cd4d..af02b7b 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -27,6 +27,7 @@
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
@@ -40,8 +41,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
@@ -124,7 +128,7 @@
             }
             mTestClock = new OffsettableClock();
             mTestHandler = new TestHandler(null, mTestClock);
-            mTestHost = new TestHost(mViewRoot);
+            mTestHost = spy(new TestHost(mViewRoot));
             mController = new InsetsController(mTestHost, (controller, type) -> {
                 if (type == ITYPE_IME) {
                     return new InsetsSourceConsumer(type, controller.getState(),
@@ -745,6 +749,99 @@
         });
     }
 
+    @Test
+    public void testInsetsChangedCount_controlSystemBars() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            prepareControls();
+
+            // Hiding visible system bars should only causes insets change once for each bar.
+            clearInvocations(mTestHost);
+            mController.hide(statusBars() | navigationBars());
+            verify(mTestHost, times(2)).notifyInsetsChanged();
+
+            // Sending the same insets state should not cause insets change.
+            // This simulates the callback from server after hiding system bars.
+            clearInvocations(mTestHost);
+            mController.onStateChanged(mController.getState());
+            verify(mTestHost, never()).notifyInsetsChanged();
+
+            // Showing invisible system bars should only causes insets change once for each bar.
+            clearInvocations(mTestHost);
+            mController.show(statusBars() | navigationBars());
+            verify(mTestHost, times(2)).notifyInsetsChanged();
+
+            // Sending the same insets state should not cause insets change.
+            // This simulates the callback from server after showing system bars.
+            clearInvocations(mTestHost);
+            mController.onStateChanged(mController.getState());
+            verify(mTestHost, never()).notifyInsetsChanged();
+        });
+    }
+
+    @Test
+    public void testInsetsChangedCount_controlIme() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            prepareControls();
+
+            // Showing invisible ime should only causes insets change once.
+            clearInvocations(mTestHost);
+            mController.show(ime(), true /* fromIme */);
+            verify(mTestHost, times(1)).notifyInsetsChanged();
+
+            // Sending the same insets state should not cause insets change.
+            // This simulates the callback from server after showing ime.
+            clearInvocations(mTestHost);
+            mController.onStateChanged(mController.getState());
+            verify(mTestHost, never()).notifyInsetsChanged();
+
+            // Hiding visible ime should only causes insets change once.
+            clearInvocations(mTestHost);
+            mController.hide(ime());
+            verify(mTestHost, times(1)).notifyInsetsChanged();
+
+            // Sending the same insets state should not cause insets change.
+            // This simulates the callback from server after hiding ime.
+            clearInvocations(mTestHost);
+            mController.onStateChanged(mController.getState());
+            verify(mTestHost, never()).notifyInsetsChanged();
+        });
+    }
+
+    @Test
+    public void testInsetsChangedCount_onStateChanged() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final InsetsState localState = mController.getState();
+
+            // Changing status bar frame should cause notifyInsetsChanged.
+            clearInvocations(mTestHost);
+            InsetsState newState = new InsetsState(localState, true /* copySources */);
+            newState.getSource(ITYPE_STATUS_BAR).getFrame().bottom++;
+            mController.onStateChanged(newState);
+            verify(mTestHost, times(1)).notifyInsetsChanged();
+
+            // Changing status bar visibility should cause notifyInsetsChanged.
+            clearInvocations(mTestHost);
+            newState = new InsetsState(localState, true /* copySources */);
+            newState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+            mController.onStateChanged(newState);
+            verify(mTestHost, times(1)).notifyInsetsChanged();
+
+            // Changing invisible IME frame should not cause notifyInsetsChanged.
+            clearInvocations(mTestHost);
+            newState = new InsetsState(localState, true /* copySources */);
+            newState.getSource(ITYPE_IME).getFrame().top--;
+            mController.onStateChanged(newState);
+            verify(mTestHost, never()).notifyInsetsChanged();
+
+            // Changing IME visibility should cause notifyInsetsChanged.
+            clearInvocations(mTestHost);
+            newState = new InsetsState(localState, true /* copySources */);
+            newState.getSource(ITYPE_IME).setVisible(true);
+            mController.onStateChanged(newState);
+            verify(mTestHost, times(1)).notifyInsetsChanged();
+        });
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
@@ -777,7 +874,7 @@
         return controls;
     }
 
-    private static class TestHost extends ViewRootInsetsControllerHost {
+    public static class TestHost extends ViewRootInsetsControllerHost {
 
         private InsetsState mModifiedState = new InsetsState();
 
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/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index df2946c..c37a34a 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -201,6 +201,38 @@
     }
 
     @Test
+    public void testCursorDrag_diagonal_thresholdConfig() throws Throwable {
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Editor editor = tv.getEditorForTesting();
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = 1; i <= 9; i++) {
+            sb.append("here is some text").append(i).append("\n");
+        }
+        sb.append(Strings.repeat("abcdefghij\n", 400)).append("Last");
+        String text = sb.toString();
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        int index = text.indexOf("text9");
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+
+        // Configure the drag direction threshold to require the drag to be exactly horizontal. With
+        // this set, a swipe that is slightly off horizontal should not trigger cursor drag.
+        editor.setCursorDragMinAngleFromVertical(90);
+        int startIdx = text.indexOf("5");
+        int endIdx = text.indexOf("here is some text3");
+        onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+
+        // Configure the drag direction threshold to require the drag to be 45 degrees or more from
+        // vertical. With this set, the same swipe gesture as above should now trigger cursor drag.
+        editor.setCursorDragMinAngleFromVertical(45);
+        onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(endIdx));
+    }
+
+    @Test
     public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable {
         String text = "012345_aaa\n"
                 + "0123456789\n"
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index 35fd4bd..94f43de 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -165,7 +165,7 @@
         long event2Time = 1001;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, 180f);
 
         // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
         long event3Time = 5000;
@@ -280,7 +280,7 @@
         long event3Time = 1002;
         MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY);
         mTouchState.update(event3, mConfig);
-        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE);
 
         // Simulate an ACTION_UP event.
         long event4Time = 1003;
@@ -301,15 +301,15 @@
         long event2Time = 1002;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f);
 
         // Simulate another ACTION_MOVE event that is horizontal from the original down event.
-        // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
-        // initial direction of movement.
+        // The drag direction ratio should NOT change since it should only reflect the initial
+        // direction of movement.
         long event3Time = 1003;
         MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f);
         mTouchState.update(event3, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f);
 
         // Simulate an ACTION_UP event.
         long event4Time = 1004;
@@ -330,15 +330,15 @@
         long event2Time = 1002;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f);
 
         // Simulate another ACTION_MOVE event that is vertical from the original down event.
-        // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
-        // initial direction of movement.
+        // The drag direction ratio should NOT change since it should only reflect the initial
+        // direction of movement.
         long event3Time = 1003;
         MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f);
         mTouchState.update(event3, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f);
 
         // Simulate an ACTION_UP event.
         long event4Time = 1004;
@@ -374,7 +374,7 @@
         long event2Time = 1002;
         MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE);
 
         // Simulate an ACTION_CANCEL event.
         long event3Time = 1003;
@@ -411,6 +411,84 @@
         assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
     }
 
+    @Test
+    public void testGetXYRatio() throws Exception {
+        doTestGetXYRatio(-1, 0.0f);
+        doTestGetXYRatio(0, 0.0f);
+        doTestGetXYRatio(30, 0.58f);
+        doTestGetXYRatio(45, 1.0f);
+        doTestGetXYRatio(60, 1.73f);
+        doTestGetXYRatio(90, Float.MAX_VALUE);
+        doTestGetXYRatio(91, Float.MAX_VALUE);
+    }
+
+    private void doTestGetXYRatio(int angleFromVerticalInDegrees, float expectedXYRatioRounded) {
+        float result = EditorTouchState.getXYRatio(angleFromVerticalInDegrees);
+        String msg = String.format(
+                "%d deg should give an x/y ratio of %f; actual unrounded result is %f",
+                angleFromVerticalInDegrees, expectedXYRatioRounded, result);
+        float roundedResult = (result == 0.0f || result == Float.MAX_VALUE) ? result :
+                Math.round(result * 100) / 100f;
+        assertThat(msg, roundedResult, is(expectedXYRatioRounded));
+    }
+
+    @Test
+    public void testUpdate_dragDirection() throws Exception {
+        // Simulate moving straight up.
+        doTestDragDirection(100f, 100f, 100f, 50f, 0f);
+
+        // Simulate moving straight down.
+        doTestDragDirection(100f, 100f, 100f, 150f, 0f);
+
+        // Simulate moving straight left.
+        doTestDragDirection(100f, 100f, 50f, 100f, Float.MAX_VALUE);
+
+        // Simulate moving straight right.
+        doTestDragDirection(100f, 100f, 150f, 100f, Float.MAX_VALUE);
+
+        // Simulate moving up and right, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 110f, 50f, 10f / 50f);
+
+        // Simulate moving up and right, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 150f, 90f, 50f / 10f);
+
+        // Simulate moving down and right, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 110f, 150f, 10f / 50f);
+
+        // Simulate moving down and right, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 150f, 110f, 50f / 10f);
+
+        // Simulate moving down and left, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 90f, 150f, 10f / 50f);
+
+        // Simulate moving down and left, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 50f, 110f, 50f / 10f);
+
+        // Simulate moving up and left, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 90f, 50f, 10f / 50f);
+
+        // Simulate moving up and left, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 50f, 90f, 50f / 10f);
+    }
+
+    private void doTestDragDirection(float downX, float downY, float moveX, float moveY,
+            float expectedInitialDragDirectionXYRatio) {
+        EditorTouchState touchState = new EditorTouchState();
+
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, downX, downY);
+        touchState.update(event1, mConfig);
+
+        // Simulate an ACTION_MOVE event.
+        long event2Time = 1002;
+        MotionEvent event2 = moveEvent(event1Time, event2Time, moveX, moveY);
+        touchState.update(event2, mConfig);
+        String msg = String.format("(%.0f,%.0f)=>(%.0f,%.0f)", downX, downY, moveX, moveY);
+        assertThat(msg, touchState.getInitialDragDirectionXYRatio(),
+                is(expectedInitialDragDirectionXYRatio));
+    }
+
     private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
         return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
     }
@@ -441,7 +519,7 @@
     }
 
     private static void assertDrag(EditorTouchState touchState, float lastDownX,
-            float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) {
+            float lastDownY, float lastUpX, float lastUpY, float initialDragDirectionXYRatio) {
         assertThat(touchState.getLastDownX(), is(lastDownX));
         assertThat(touchState.getLastDownY(), is(lastDownY));
         assertThat(touchState.getLastUpX(), is(lastUpX));
@@ -451,7 +529,7 @@
         assertThat(touchState.isMultiTap(), is(false));
         assertThat(touchState.isMultiTapInSameArea(), is(false));
         assertThat(touchState.isMovedEnoughForDrag(), is(true));
-        assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical));
+        assertThat(touchState.getInitialDragDirectionXYRatio(), is(initialDragDirectionXYRatio));
     }
 
     private static void assertMultiTap(EditorTouchState touchState,
@@ -467,6 +545,5 @@
                 || multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
         assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea));
         assertThat(touchState.isMovedEnoughForDrag(), is(false));
-        assertThat(touchState.isDragCloseToVertical(), is(false));
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 090645f..787879a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -590,6 +590,54 @@
                 is(1));
     }
 
+
+    @Test
+    public void testNearbyShareLogging() throws Exception {
+        Intent sendIntent = createSendTextIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+                Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        onView(withId(R.id.chooser_nearby_button)).check(matches(isDisplayed()));
+        onView(withId(R.id.chooser_nearby_button)).perform(click());
+
+        ChooserActivityLoggerFake logger =
+                (ChooserActivityLoggerFake) activity.getChooserActivityLogger();
+        assertThat(logger.numCalls(), is(6));
+        // first one should be SHARESHEET_TRIGGERED uievent
+        assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(0).event.getId(),
+                is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
+        // second one should be SHARESHEET_STARTED event
+        assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
+        assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
+        assertThat(logger.get(1).mimeType, is("text/plain"));
+        assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+        assertThat(logger.get(1).appProvidedApp, is(0));
+        assertThat(logger.get(1).appProvidedDirect, is(0));
+        assertThat(logger.get(1).isWorkprofile, is(false));
+        assertThat(logger.get(1).previewType, is(3));
+        // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
+        assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(2).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
+        // fourth and fifth are just artifacts of test set-up
+        // sixth one should be ranking atom with SHARESHEET_NEARBY_TARGET_SELECTED event
+        assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+        assertThat(logger.get(5).targetType,
+                is(ChooserActivityLogger
+                        .SharesheetTargetSelectedEvent.SHARESHEET_NEARBY_TARGET_SELECTED.getId()));
+    }
+
+
     @Test
     public void oneVisibleImagePreview() throws InterruptedException {
         Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index d3d5caf..16a2fbd 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -21,6 +21,7 @@
 
 import android.annotation.Nullable;
 import android.app.usage.UsageStatsManager;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -89,6 +90,18 @@
 
     boolean getIsSelected() { return mIsSuccessfullySelected; }
 
+    @Override
+    protected ComponentName getNearbySharingComponent() {
+        // an arbitrary pre-installed activity that handles this type of intent
+        return ComponentName.unflattenFromString("com.google.android.apps.messaging/"
+                + "com.google.android.apps.messaging.ui.conversationlist.ShareIntentActivity");
+    }
+
+    @Override
+    protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
+        return new ChooserWrapperActivity.EmptyTargetInfo();
+    }
+
     UsageStatsManager getUsageStatsManager() {
         if (mUsm == null) {
             mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
index 3e67b8b..22c41f3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
@@ -38,7 +38,8 @@
 @SmallTest
 public class BatteryStatsBinderCallStatsTest extends TestCase {
 
-    private static final int TRANSACTION_CODE = 100;
+    private static final int TRANSACTION_CODE1 = 100;
+    private static final int TRANSACTION_CODE2 = 101;
 
     /**
      * Test BatteryStatsImpl.Uid.noteBinderCallStats.
@@ -53,23 +54,23 @@
 
         Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
         BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid,
-                MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
         stat1.incrementalCallCount = 21;
         stat1.recordedCallCount = 5;
         stat1.cpuTimeMicros = 1000;
         callStats.add(stat1);
 
-        bi.noteBinderCallStats(workSourceUid, 42, callStats);
+        bi.noteBinderCallStats(workSourceUid, 42, callStats, null);
 
         callStats.clear();
         BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
-                MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
         stat2.incrementalCallCount = 9;
         stat2.recordedCallCount = 8;
         stat2.cpuTimeMicros = 500;
         callStats.add(stat2);
 
-        bi.noteBinderCallStats(workSourceUid, 8, callStats);
+        bi.noteBinderCallStats(workSourceUid, 8, callStats, null);
 
         BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
         assertEquals(42 + 8, uid.getBinderCallCount());
@@ -85,9 +86,62 @@
         assertEquals(500, value.recordedCpuTimeMicros);
     }
 
+
+    @Test
+    public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception {
+        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        int callingUid = Process.FIRST_APPLICATION_UID + 1;
+        int workSourceUid1 = Process.FIRST_APPLICATION_UID + 1;
+        int workSourceUid2 = Process.FIRST_APPLICATION_UID + 2;
+        int workSourceUid3 = Process.FIRST_APPLICATION_UID + 3;
+
+        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+        BinderCallsStats.CallStat stat1a = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
+        stat1a.incrementalCallCount = 10;
+        stat1a.recordedCallCount = 5;
+        stat1a.cpuTimeMicros = 1000;
+        callStats.add(stat1a);
+
+        BinderCallsStats.CallStat stat1b = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE2, true /*screenInteractive */);
+        stat1b.incrementalCallCount = 30;
+        stat1b.recordedCallCount = 15;
+        stat1b.cpuTimeMicros = 1500;
+        callStats.add(stat1b);
+
+        bi.noteBinderCallStats(workSourceUid1, 65, callStats, null);
+
+        // No recorded stats for some methods. Must use the global average.
+        callStats.clear();
+        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
+        stat2.incrementalCallCount = 10;
+        callStats.add(stat2);
+
+        bi.noteBinderCallStats(workSourceUid2, 40, callStats, null);
+
+        // No stats for any calls. Must use the global average
+        callStats.clear();
+        bi.noteBinderCallStats(workSourceUid3, 50, callStats, null);
+
+        bi.updateSystemServiceCallStats();
+
+        double prop1 = bi.getUidStatsLocked(workSourceUid1).getProportionalSystemServiceUsage();
+        double prop2 = bi.getUidStatsLocked(workSourceUid2).getProportionalSystemServiceUsage();
+        double prop3 = bi.getUidStatsLocked(workSourceUid3).getProportionalSystemServiceUsage();
+
+        assertEquals(0.419, prop1, 0.01);
+        assertEquals(0.258, prop2, 0.01);
+        assertEquals(0.323, prop3, 0.01);
+        assertEquals(1.000, prop1 + prop2 + prop3, 0.01);
+    }
+
     private static class MockBinder extends Binder {
         public static String getDefaultTransactionName(int txCode) {
-            return txCode == TRANSACTION_CODE ? "testMethod" : "unknown";
+            return txCode == TRANSACTION_CODE1 ? "testMethod" : "unknown";
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index a5117a3..96250db 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.os.Binder;
 import android.os.Handler;
@@ -44,6 +45,7 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Random;
@@ -493,7 +495,9 @@
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
 
         PrintWriter pw = new PrintWriter(new StringWriter());
-        bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), true);
+        bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), Process.INVALID_UID, true);
+
+        bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), WORKSOURCE_UID, true);
     }
 
     @Test
@@ -606,8 +610,8 @@
         assertEquals("-1", callStats.methodName);
         assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
                 callStats.className);
-        assertEquals(false , callStats.screenInteractive);
-        assertEquals(-1 , callStats.callingUid);
+        assertEquals(false, callStats.screenInteractive);
+        assertEquals(-1, callStats.callingUid);
     }
 
     @Test
@@ -768,8 +772,8 @@
 
         final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
         bcs.setCallStatsObserver(
-                (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll(
-                        callStats));
+                (workSourceUid, incrementalCallCount, callStats, binderThreadIds) ->
+                        callStatsList.addAll(callStats));
 
         Binder binder = new Binder();
 
@@ -785,7 +789,7 @@
         bcs.time += 30;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
 
-        for (Runnable runnable: mHandler.mRunnables) {
+        for (Runnable runnable : mHandler.mRunnables) {
             // Execute all pending runnables. Ignore the delay.
             runnable.run();
         }
@@ -839,6 +843,53 @@
         assertEquals(3, tids[2]);
     }
 
+    @Test
+    public void testTrackingSpecificWorksourceUid() {
+        mDeviceState.setCharging(true);
+
+        Binder binder = new Binder();
+
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.recordAllCallsForWorkSourceUid(WORKSOURCE_UID);
+
+        int[] transactions = {41, 42, 43, 42, 43, 43};
+        int[] durationsMs = {100, 200, 300, 400, 500, 600};
+
+        for (int i = 0; i < transactions.length; i++) {
+            CallSession callSession = bcs.callStarted(binder, transactions[i], WORKSOURCE_UID);
+            bcs.time += durationsMs[i];
+            bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+        }
+
+        BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
+        Assert.assertNotNull(uidEntry);
+        assertEquals(6, uidEntry.callCount);
+
+        Collection<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+        assertEquals(3, callStatsList.size());
+        for (BinderCallsStats.CallStat callStat : callStatsList) {
+            switch (callStat.transactionCode) {
+                case 41:
+                    assertEquals(1, callStat.callCount);
+                    assertEquals(1, callStat.incrementalCallCount);
+                    assertEquals(100, callStat.cpuTimeMicros);
+                    break;
+                case 42:
+                    assertEquals(2, callStat.callCount);
+                    assertEquals(2, callStat.incrementalCallCount);
+                    assertEquals(200 + 400, callStat.cpuTimeMicros);
+                    break;
+                case 43:
+                    assertEquals(3, callStat.callCount);
+                    assertEquals(3, callStat.incrementalCallCount);
+                    assertEquals(300 + 500 + 600, callStat.cpuTimeMicros);
+                    break;
+                default:
+                    fail("Unexpected transaction code: " + callStat.transactionCode);
+            }
+        }
+    }
+
     private static class TestHandler extends Handler {
         ArrayList<Runnable> mRunnables = new ArrayList<>();
 
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index bc0e0a4..75dd7fb 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -133,6 +133,12 @@
         return this;
     }
 
+    public MockBatteryStatsImpl setSystemServerCpuThreadReader(
+            SystemServerCpuThreadReader systemServerCpuThreadReader) {
+        mSystemServerCpuThreadReader = systemServerCpuThreadReader;
+        return this;
+    }
+
     public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) {
         mUserInfoProvider = provider;
         return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
new file mode 100644
index 0000000..10ba548
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemServerCpuThreadReaderTest {
+    private File mProcDirectory;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mProcDirectory);
+    }
+
+    @Test
+    public void testReaderDelta_firstTime() throws IOException {
+        int uid = 42;
+        setupDirectory(
+                mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                new int[]{42, 1, 2, 3},
+                new int[]{1000, 2000},
+                // Units are 10ms aka 10000Us
+                new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+
+        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
+                mProcDirectory.toPath(), uid);
+        reader.setBinderThreadNativeTids(new int[]{1, 3});
+        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+                reader.readDelta();
+        assertArrayEquals(new long[]{100 * 10000, 1100 * 10000},
+                systemServiceCpuThreadTimes.threadCpuTimesUs);
+        assertArrayEquals(new long[]{0, 600 * 10000},
+                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+    }
+
+    @Test
+    public void testReaderDelta_nextTime() throws IOException {
+        int uid = 42;
+        setupDirectory(
+                mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                new int[]{42, 1, 2, 3},
+                new int[]{1000, 2000},
+                new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+
+        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
+                mProcDirectory.toPath(), uid);
+        reader.setBinderThreadNativeTids(new int[]{1, 3});
+
+        // First time, populate "last" snapshot
+        reader.readDelta();
+
+        FileUtils.deleteContents(mProcDirectory);
+        setupDirectory(
+                mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                new int[]{42, 1, 2, 3},
+                new int[]{1000, 2000},
+                new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}});
+
+        // Second time, get the actual delta
+        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+                reader.readDelta();
+
+        assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000},
+                systemServiceCpuThreadTimes.threadCpuTimesUs);
+        assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000},
+                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+    }
+
+    private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies,
+            int[][] cpuTimes) throws IOException {
+        // Make /proc/$PID
+        assertTrue(processPath.toFile().mkdirs());
+
+        // Make /proc/$PID/task
+        final Path selfThreadsPath = processPath.resolve("task");
+        assertTrue(selfThreadsPath.toFile().mkdirs());
+
+        // Make thread directories in reverse order, as they are read in order of creation by
+        // CpuThreadProcReader
+        for (int i = 0; i < threadIds.length; i++) {
+            // Make /proc/$PID/task/$TID
+            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
+            assertTrue(threadPath.toFile().mkdirs());
+
+            // Make /proc/$PID/task/$TID/time_in_state
+            final OutputStream timeInStateStream =
+                    Files.newOutputStream(threadPath.resolve("time_in_state"));
+            for (int j = 0; j < cpuFrequencies.length; j++) {
+                final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
+                timeInStateStream.write(line.getBytes());
+            }
+            timeInStateStream.close();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
new file mode 100644
index 0000000..ac5443e
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Binder;
+import android.os.Process;
+
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemServicePowerCalculatorTest {
+
+    private PowerProfile mProfile;
+    private MockBatteryStatsImpl mMockBatteryStats;
+    private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
+    private MockServerCpuThreadReader mMockServerCpuThreadReader;
+    private SystemServicePowerCalculator mSystemServicePowerCalculator;
+
+    @Before
+    public void setUp() throws IOException {
+        Context context = InstrumentationRegistry.getContext();
+        mProfile = new PowerProfile(context, true /* forTest */);
+        mMockServerCpuThreadReader = new MockServerCpuThreadReader();
+        mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
+        mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks())
+                .setPowerProfile(mProfile)
+                .setSystemServerCpuThreadReader(mMockServerCpuThreadReader)
+                .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
+                .setUserInfoProvider(new MockUserInfoProvider());
+        mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
+        mSystemServicePowerCalculator =
+                new SystemServicePowerCalculator(mProfile, mMockBatteryStats);
+    }
+
+    @Test
+    public void testCalculateApp() {
+        // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
+        mMockServerCpuThreadReader.setThreadTimes(
+                new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000},
+                new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000});
+
+        mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
+                new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000}
+        );
+
+        mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false);
+
+        int workSourceUid1 = 100;
+        int workSourceUid2 = 200;
+        int transactionCode = 42;
+
+        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+        BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1,
+                Binder.class, transactionCode, true /*screenInteractive */);
+        stat1.incrementalCallCount = 100;
+        stat1.recordedCallCount = 100;
+        stat1.cpuTimeMicros = 1000000;
+        callStats.add(stat1);
+
+        mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null);
+
+        callStats.clear();
+        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
+                Binder.class, transactionCode, true /*screenInteractive */);
+        stat2.incrementalCallCount = 100;
+        stat2.recordedCallCount = 100;
+        stat2.cpuTimeMicros = 9000000;
+        callStats.add(stat2);
+
+        mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null);
+
+        mMockBatteryStats.updateSystemServiceCallStats();
+        mMockBatteryStats.updateSystemServerThreadStats();
+
+        BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP,
+                mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0);
+        mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0,
+                BatteryStats.STATS_SINCE_CHARGED);
+        assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001);
+
+        BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP,
+                mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0);
+        mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0,
+                BatteryStats.STATS_SINCE_CHARGED);
+        assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001);
+    }
+
+    private static class MockKernelCpuUidFreqTimeReader extends
+            KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader {
+        private long[] mSystemServerCpuTimes;
+
+        MockKernelCpuUidFreqTimeReader() {
+            super(/*throttle */false);
+        }
+
+        void setSystemServerCpuTimes(long[] systemServerCpuTimes) {
+            mSystemServerCpuTimes = systemServerCpuTimes;
+        }
+
+        @Override
+        public boolean perClusterTimesAvailable() {
+            return true;
+        }
+
+        @Override
+        public void readDelta(@Nullable Callback<long[]> cb) {
+            if (cb != null) {
+                cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
+            }
+        }
+    }
+
+    private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader {
+        private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
+
+        MockServerCpuThreadReader() {
+            super(null);
+        }
+
+        public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
+            mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
+            mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
+        }
+
+        @Override
+        public SystemServiceCpuThreadTimes readDelta() {
+            return mThreadTimes;
+        }
+    }
+
+    private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider {
+        @Nullable
+        @Override
+        protected int[] getUserIds() {
+            return new int[0];
+        }
+
+        @Override
+        public boolean exists(int userId) {
+            return true;
+        }
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f834ce7..602ccfe 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -143,6 +143,7 @@
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
         <permission name="android.permission.PACKAGE_USAGE_STATS" />
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+        <permission name="android.permission.MODIFY_AUDIO_ROUTING" />
 
         <!-- For permission hub 2 debugging only -->
         <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
@@ -432,18 +433,6 @@
         <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
         <!-- Permissions required for CTS test - AdbManagerTest -->
         <permission name="android.permission.MANAGE_DEBUGGING" />
-        <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
-        <permission name="android.car.permission.CAR_DRIVING_STATE" />
-        <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
-        <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
-        <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-        <permission name="android.car.permission.CAR_DIAGNOSTICS" />
-        <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-        <permission name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
-        <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-        <permission name="android.car.permission.CONTROL_APP_BLOCKING" />
-        <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-        <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 5711f98..bd2d4af 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -345,10 +345,10 @@
 # key 395 "KEY_LIST"
 # key 396 "KEY_MEMO"
 key 397   CALENDAR
-# key 398 "KEY_RED"
-# key 399 "KEY_GREEN"
-# key 400 "KEY_YELLOW"
-# key 401 "KEY_BLUE"
+key 398   PROG_RED
+key 399   PROG_GREEN
+key 400   PROG_YELLOW
+key 401   PROG_BLUE
 key 402   CHANNEL_UP
 key 403   CHANNEL_DOWN
 # key 404 "KEY_FIRST"
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
index 3ffb8ea..a2ee065 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
@@ -23,12 +23,15 @@
 import com.google.errorprone.VisitorState;
 import com.google.errorprone.bugpatterns.BugChecker;
 import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
 import com.google.errorprone.matchers.Description;
 import com.google.errorprone.util.ASTHelpers;
 import com.sun.source.tree.ExpressionTree;
 import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.MemberSelectTree;
 import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
 import com.sun.tools.javac.code.Symbol.VarSymbol;
 
 import java.util.List;
@@ -44,11 +47,20 @@
     name = "AndroidFrameworkUid",
     summary = "Verifies that PID, UID and user ID arguments aren't crossed",
     severity = WARNING)
-public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher {
+public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher,
+        NewClassTreeMatcher {
     @Override
     public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
-        final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
-        final List<? extends ExpressionTree> args = tree.getArguments();
+        return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree);
+    }
+
+    @Override
+    public Description matchNewClass(NewClassTree tree, VisitorState state) {
+        return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree);
+    }
+
+    private Description matchArguments(List<VarSymbol> vars,
+            List<? extends ExpressionTree> args, Tree tree) {
         for (int i = 0; i < Math.min(vars.size(), args.size()); i++) {
             final Flavor varFlavor = getFlavor(vars.get(i));
             final Flavor argFlavor = getFlavor(args.get(i));
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
index 74da947..75341fd 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
@@ -34,7 +34,7 @@
     }
 
     @Test
-    public void testTypical() {
+    public void testTypical_methodInvocation() {
         compilationHelper
                 .addSourceLines("Example.java",
                         "public abstract class Example {",
@@ -57,7 +57,30 @@
     }
 
     @Test
-    public void testCallingUid() {
+    public void testTypical_newClass() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "public abstract class Example {",
+                        "  class Bar { Bar(int pid, int uid, int userId) {} }",
+                        "  abstract int getUserId();",
+                        "  void foo(int pid, int uid, int userId, int unrelated) {",
+                        "    new Bar(0, 0, 0);",
+                        "    new Bar(pid, uid, userId);",
+                        "    new Bar(pid, uid, getUserId());",
+                        "    new Bar(unrelated, unrelated, unrelated);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(uid, pid, userId);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(pid, userId, uid);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(getUserId(), 0, 0);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testCallingUid_methodInvocation() {
         compilationHelper
                 .addSourceFile("/android/os/Binder.java")
                 .addSourceFile("/android/os/UserHandle.java")
@@ -98,4 +121,48 @@
                         "}")
                 .doTest();
     }
+
+
+    @Test
+    public void testCallingUid_newClass() {
+        compilationHelper
+                .addSourceFile("/android/os/Binder.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceLines("Example.java",
+                        "import android.os.Binder;",
+                        "import android.os.UserHandle;",
+                        "public abstract class Example {",
+                        "  int callingUserId;",
+                        "  int callingUid;",
+                        "  class Foo { Foo(int callingUserId) {} }",
+                        "  class Bar { Bar(int callingUid) {} }",
+                        "  void doUserId(int callingUserId) {",
+                        "    new Foo(UserHandle.getUserId(Binder.getCallingUid()));",
+                        "    new Foo(this.callingUserId);",
+                        "    new Foo(callingUserId);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(Binder.getCallingUid());",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(this.callingUid);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(callingUid);",
+                        "  }",
+                        "  void doUid(int callingUserId) {",
+                        "    new Bar(Binder.getCallingUid());",
+                        "    new Bar(this.callingUid);",
+                        "    new Bar(callingUid);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(UserHandle.getUserId(Binder.getCallingUid()));",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(this.callingUserId);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(callingUserId);",
+                        "  }",
+                        "  void doInner() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(UserHandle.getUserId(callingUserId));",
+                        "  }",
+                        "}")
+                .doTest();
+    }
 }
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
index d8af726..70dedb8 100644
--- a/framework-jarjar-rules.txt
+++ b/framework-jarjar-rules.txt
@@ -1,2 +1,6 @@
 rule android.hidl.** android.internal.hidl.@1
 rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
+
+# Hide media mainline module implementation classes to avoid collisions with
+# app-bundled ExoPlayer classes.
+rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index b3103fd5..0452933 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -634,6 +634,19 @@
     }
 
     /**
+     * Sets the colormode with the desired SDR white point.
+     *
+     * The white point only applies if the color mode is an HDR mode
+     *
+     * @hide
+     */
+    public void setColorMode(@ActivityInfo.ColorMode int colorMode, float whitePoint) {
+        nSetSdrWhitePoint(mNativeProxy, whitePoint);
+        mColorMode = colorMode;
+        nSetColorMode(mNativeProxy, colorMode);
+    }
+
+    /**
      * Blocks until all previously queued work has completed.
      *
      * TODO: Only used for draw finished listeners, but the FrameCompleteCallback does that
@@ -1227,6 +1240,8 @@
 
     private static native void nSetColorMode(long nativeProxy, int colorMode);
 
+    private static native void nSetSdrWhitePoint(long nativeProxy, float whitePoint);
+
     private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
 
     private static native void nDestroy(long nativeProxy, long rootRenderNode);
diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS
index 936ba5c..c232d13 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -1,6 +1,7 @@
+alecmouri@google.com
+djsollen@google.com
 jreck@google.com
 njawad@google.com
-djsollen@google.com
-stani@google.com
-scroggo@google.com
 reed@google.com
+scroggo@google.com
+stani@google.com
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 446e81e..ba44d05 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -78,6 +78,7 @@
 
 int Properties::contextPriority = 0;
 int Properties::defaultRenderAhead = -1;
+float Properties::defaultSdrWhitePoint = 200.f;
 
 bool Properties::load() {
     bool prevDebugLayersUpdates = debugLayersUpdates;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index c8f6b3b..85a0f4a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -249,6 +249,8 @@
 
     static int defaultRenderAhead;
 
+    static float defaultSdrWhitePoint;
+
 private:
     static ProfileType sProfileType;
     static bool sDisableProfileBars;
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 7d6875f..fc594da 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -223,6 +223,11 @@
     proxy->setColorMode(static_cast<ColorMode>(colorMode));
 }
 
+static void android_view_ThreadedRenderer_setSdrWhitePoint(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jfloat sdrWhitePoint) {
+    Properties::defaultSdrWhitePoint = sdrWhitePoint;
+}
+
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
     LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
@@ -671,6 +676,7 @@
         {"nSetLightGeometry", "(JFFFF)V", (void*)android_view_ThreadedRenderer_setLightGeometry},
         {"nSetOpaque", "(JZ)V", (void*)android_view_ThreadedRenderer_setOpaque},
         {"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
+        {"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint},
         {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
         {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
         {"nRegisterAnimatingRenderNode", "(JJ)V",
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 644d5fb..e419801 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -559,6 +559,7 @@
         AStatsEvent_writeBool(event, !lastFullDay);
         AStatsEvent_build(event);
     }
+    delete dump;
 }
 
 
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index eff34a8..87512f0 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -26,6 +26,7 @@
 
 #include <algorithm>
 #include <cmath>
+#include <Properties.h>
 
 namespace android {
 namespace uirenderer {
@@ -344,13 +345,9 @@
             static_cast<uint8_t>(rgb.b * 255));
 }
 
-// Note that SkColorSpace doesn't have the notion of an unspecified SDR white
-// level.
-static constexpr float kDefaultSDRWhiteLevel = 150.f;
-
 skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
     if (sdr_white_level <= 0.f) {
-        sdr_white_level = kDefaultSDRWhiteLevel;
+        sdr_white_level = Properties::defaultSdrWhitePoint;
     }
     // The generic PQ transfer function produces normalized luminance values i.e.
     // the range 0-1 represents 0-10000 nits for the reference display, but we
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5252cd0..dca3501 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -16,6 +16,9 @@
     name: "libinputservice",
     srcs: [
         "PointerController.cpp",
+        "PointerControllerContext.cpp",
+        "MouseCursorController.cpp",
+        "TouchSpotController.cpp",
         "SpriteController.cpp",
         "SpriteIcon.cpp",
     ],
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
new file mode 100644
index 0000000..80b555b
--- /dev/null
+++ b/libs/input/MouseCursorController.cpp
@@ -0,0 +1,460 @@
+/*
+ * 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 LOG_TAG "MouseCursorController"
+//#define LOG_NDEBUG 0
+
+// Log debug messages about pointer updates
+#define DEBUG_MOUSE_CURSOR_UPDATES 0
+
+#include "MouseCursorController.h"
+
+#include <log/log.h>
+
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+
+namespace {
+// Time to spend fading out the pointer completely.
+const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
+} // namespace
+
+namespace android {
+
+// --- MouseCursorController ---
+
+MouseCursorController::MouseCursorController(PointerControllerContext& context)
+      : mContext(context) {
+    std::scoped_lock lock(mLock);
+
+    mLocked.animationFrameIndex = 0;
+    mLocked.lastFrameUpdatedTime = 0;
+
+    mLocked.pointerFadeDirection = 0;
+    mLocked.pointerX = 0;
+    mLocked.pointerY = 0;
+    mLocked.pointerAlpha = 0.0f; // pointer is initially faded
+    mLocked.pointerSprite = mContext.getSpriteController()->createSprite();
+    mLocked.updatePointerIcon = false;
+    mLocked.requestedPointerType = mContext.getPolicy()->getDefaultPointerIconId();
+
+    mLocked.resourcesLoaded = false;
+
+    mLocked.buttonState = 0;
+}
+
+MouseCursorController::~MouseCursorController() {
+    std::scoped_lock lock(mLock);
+
+    mLocked.pointerSprite.clear();
+}
+
+bool MouseCursorController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
+                                      float* outMaxY) const {
+    std::scoped_lock lock(mLock);
+
+    return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
+}
+
+bool MouseCursorController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX,
+                                            float* outMaxY) const REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return false;
+    }
+
+    *outMinX = mLocked.viewport.logicalLeft;
+    *outMinY = mLocked.viewport.logicalTop;
+    *outMaxX = mLocked.viewport.logicalRight - 1;
+    *outMaxY = mLocked.viewport.logicalBottom - 1;
+    return true;
+}
+
+void MouseCursorController::move(float deltaX, float deltaY) {
+#if DEBUG_MOUSE_CURSOR_UPDATES
+    ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
+#endif
+    if (deltaX == 0.0f && deltaY == 0.0f) {
+        return;
+    }
+
+    std::scoped_lock lock(mLock);
+
+    setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
+}
+
+void MouseCursorController::setButtonState(int32_t buttonState) {
+#if DEBUG_MOUSE_CURSOR_UPDATES
+    ALOGD("Set button state 0x%08x", buttonState);
+#endif
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.buttonState != buttonState) {
+        mLocked.buttonState = buttonState;
+    }
+}
+
+int32_t MouseCursorController::getButtonState() const {
+    std::scoped_lock lock(mLock);
+    return mLocked.buttonState;
+}
+
+void MouseCursorController::setPosition(float x, float y) {
+#if DEBUG_MOUSE_CURSOR_UPDATES
+    ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
+#endif
+    std::scoped_lock lock(mLock);
+    setPositionLocked(x, y);
+}
+
+void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
+    float minX, minY, maxX, maxY;
+    if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
+        if (x <= minX) {
+            mLocked.pointerX = minX;
+        } else if (x >= maxX) {
+            mLocked.pointerX = maxX;
+        } else {
+            mLocked.pointerX = x;
+        }
+        if (y <= minY) {
+            mLocked.pointerY = minY;
+        } else if (y >= maxY) {
+            mLocked.pointerY = maxY;
+        } else {
+            mLocked.pointerY = y;
+        }
+        updatePointerLocked();
+    }
+}
+
+void MouseCursorController::getPosition(float* outX, float* outY) const {
+    std::scoped_lock lock(mLock);
+
+    *outX = mLocked.pointerX;
+    *outY = mLocked.pointerY;
+}
+
+int32_t MouseCursorController::getDisplayId() const {
+    std::scoped_lock lock(mLock);
+    return mLocked.viewport.displayId;
+}
+
+void MouseCursorController::fade(PointerControllerInterface::Transition transition) {
+    std::scoped_lock lock(mLock);
+
+    // Remove the inactivity timeout, since we are fading now.
+    mContext.removeInactivityTimeout();
+
+    // Start fading.
+    if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
+        mLocked.pointerFadeDirection = 0;
+        mLocked.pointerAlpha = 0.0f;
+        updatePointerLocked();
+    } else {
+        mLocked.pointerFadeDirection = -1;
+        mContext.startAnimation();
+    }
+}
+
+void MouseCursorController::unfade(PointerControllerInterface::Transition transition) {
+    std::scoped_lock lock(mLock);
+
+    // Always reset the inactivity timer.
+    mContext.resetInactivityTimeout();
+
+    // Start unfading.
+    if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
+        mLocked.pointerFadeDirection = 0;
+        mLocked.pointerAlpha = 1.0f;
+        updatePointerLocked();
+    } else {
+        mLocked.pointerFadeDirection = 1;
+        mContext.startAnimation();
+    }
+}
+
+void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
+    std::scoped_lock lock(mLock);
+
+    loadResourcesLocked(getAdditionalMouseResources);
+    updatePointerLocked();
+}
+
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+    width = viewport.deviceWidth;
+    height = viewport.deviceHeight;
+
+    if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
+        viewport.orientation == DISPLAY_ORIENTATION_270) {
+        std::swap(width, height);
+    }
+}
+
+void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
+                                               bool getAdditionalMouseResources) {
+    std::scoped_lock lock(mLock);
+
+    if (viewport == mLocked.viewport) {
+        return;
+    }
+
+    const DisplayViewport oldViewport = mLocked.viewport;
+    mLocked.viewport = viewport;
+
+    int32_t oldDisplayWidth, oldDisplayHeight;
+    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+    int32_t newDisplayWidth, newDisplayHeight;
+    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+    // Reset cursor position to center if size or display changed.
+    if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
+        oldDisplayHeight != newDisplayHeight) {
+        float minX, minY, maxX, maxY;
+        if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
+            mLocked.pointerX = (minX + maxX) * 0.5f;
+            mLocked.pointerY = (minY + maxY) * 0.5f;
+            // Reload icon resources for density may be changed.
+            loadResourcesLocked(getAdditionalMouseResources);
+        } else {
+            mLocked.pointerX = 0;
+            mLocked.pointerY = 0;
+        }
+    } else if (oldViewport.orientation != viewport.orientation) {
+        // Apply offsets to convert from the pixel top-left corner position to the pixel center.
+        // This creates an invariant frame of reference that we can easily rotate when
+        // taking into account that the pointer may be located at fractional pixel offsets.
+        float x = mLocked.pointerX + 0.5f;
+        float y = mLocked.pointerY + 0.5f;
+        float temp;
+
+        // Undo the previous rotation.
+        switch (oldViewport.orientation) {
+            case DISPLAY_ORIENTATION_90:
+                temp = x;
+                x = oldViewport.deviceHeight - y;
+                y = temp;
+                break;
+            case DISPLAY_ORIENTATION_180:
+                x = oldViewport.deviceWidth - x;
+                y = oldViewport.deviceHeight - y;
+                break;
+            case DISPLAY_ORIENTATION_270:
+                temp = x;
+                x = y;
+                y = oldViewport.deviceWidth - temp;
+                break;
+        }
+
+        // Perform the new rotation.
+        switch (viewport.orientation) {
+            case DISPLAY_ORIENTATION_90:
+                temp = x;
+                x = y;
+                y = viewport.deviceHeight - temp;
+                break;
+            case DISPLAY_ORIENTATION_180:
+                x = viewport.deviceWidth - x;
+                y = viewport.deviceHeight - y;
+                break;
+            case DISPLAY_ORIENTATION_270:
+                temp = x;
+                x = viewport.deviceWidth - y;
+                y = temp;
+                break;
+        }
+
+        // Apply offsets to convert from the pixel center to the pixel top-left corner position
+        // and save the results.
+        mLocked.pointerX = x - 0.5f;
+        mLocked.pointerY = y - 0.5f;
+    }
+
+    updatePointerLocked();
+}
+
+void MouseCursorController::updatePointerIcon(int32_t iconId) {
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.requestedPointerType != iconId) {
+        mLocked.requestedPointerType = iconId;
+        mLocked.updatePointerIcon = true;
+        updatePointerLocked();
+    }
+}
+
+void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
+    std::scoped_lock lock(mLock);
+
+    const int32_t iconId = mContext.getPolicy()->getCustomPointerIconId();
+    mLocked.additionalMouseResources[iconId] = icon;
+    mLocked.requestedPointerType = iconId;
+    mLocked.updatePointerIcon = true;
+    updatePointerLocked();
+}
+
+bool MouseCursorController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) {
+    nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
+
+    std::scoped_lock lock(mLock);
+
+    // Animate pointer fade.
+    if (mLocked.pointerFadeDirection < 0) {
+        mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
+        if (mLocked.pointerAlpha <= 0.0f) {
+            mLocked.pointerAlpha = 0.0f;
+            mLocked.pointerFadeDirection = 0;
+        } else {
+            keepAnimating = true;
+        }
+        updatePointerLocked();
+    } else if (mLocked.pointerFadeDirection > 0) {
+        mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
+        if (mLocked.pointerAlpha >= 1.0f) {
+            mLocked.pointerAlpha = 1.0f;
+            mLocked.pointerFadeDirection = 0;
+        } else {
+            keepAnimating = true;
+        }
+        updatePointerLocked();
+    }
+
+    return keepAnimating;
+}
+
+bool MouseCursorController::doBitmapAnimation(nsecs_t timestamp) {
+    std::scoped_lock lock(mLock);
+
+    std::map<int32_t, PointerAnimation>::const_iterator iter =
+            mLocked.animationResources.find(mLocked.requestedPointerType);
+    if (iter == mLocked.animationResources.end()) {
+        return false;
+    }
+
+    if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
+        sp<SpriteController> spriteController = mContext.getSpriteController();
+        spriteController->openTransaction();
+
+        int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
+        mLocked.animationFrameIndex += incr;
+        mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
+        while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
+            mLocked.animationFrameIndex -= iter->second.animationFrames.size();
+        }
+        mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
+
+        spriteController->closeTransaction();
+    }
+
+    // Keep animating.
+    return true;
+}
+
+void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+    sp<SpriteController> spriteController = mContext.getSpriteController();
+    spriteController->openTransaction();
+
+    mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
+    mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
+
+    if (mLocked.pointerAlpha > 0) {
+        mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
+        mLocked.pointerSprite->setVisible(true);
+    } else {
+        mLocked.pointerSprite->setVisible(false);
+    }
+
+    if (mLocked.updatePointerIcon) {
+        if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) {
+            mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+        } else {
+            std::map<int32_t, SpriteIcon>::const_iterator iter =
+                    mLocked.additionalMouseResources.find(mLocked.requestedPointerType);
+            if (iter != mLocked.additionalMouseResources.end()) {
+                std::map<int32_t, PointerAnimation>::const_iterator anim_iter =
+                        mLocked.animationResources.find(mLocked.requestedPointerType);
+                if (anim_iter != mLocked.animationResources.end()) {
+                    mLocked.animationFrameIndex = 0;
+                    mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+                    mContext.startAnimation();
+                }
+                mLocked.pointerSprite->setIcon(iter->second);
+            } else {
+                ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType);
+                mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+            }
+        }
+        mLocked.updatePointerIcon = false;
+    }
+
+    spriteController->closeTransaction();
+}
+
+void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
+    if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
+
+    sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
+    policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
+    policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
+
+    mLocked.additionalMouseResources.clear();
+    mLocked.animationResources.clear();
+    if (getAdditionalMouseResources) {
+        policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                             &mLocked.animationResources,
+                                             mLocked.viewport.displayId);
+    }
+
+    mLocked.updatePointerIcon = true;
+}
+
+bool MouseCursorController::isViewportValid() {
+    std::scoped_lock lock(mLock);
+    return mLocked.viewport.isValid();
+}
+
+void MouseCursorController::getAdditionalMouseResources() {
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.additionalMouseResources.empty()) {
+        mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                                           &mLocked.animationResources,
+                                                           mLocked.viewport.displayId);
+    }
+    mLocked.updatePointerIcon = true;
+    updatePointerLocked();
+}
+
+bool MouseCursorController::resourcesLoaded() {
+    std::scoped_lock lock(mLock);
+    return mLocked.resourcesLoaded;
+}
+
+} // namespace android
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
new file mode 100644
index 0000000..448165b
--- /dev/null
+++ b/libs/input/MouseCursorController.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_MOUSE_CURSOR_CONTROLLER_H
+#define _UI_MOUSE_CURSOR_CONTROLLER_H
+
+#include <gui/DisplayEventReceiver.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <ui/DisplayInfo.h>
+#include <utils/BitSet.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "PointerControllerContext.h"
+#include "SpriteController.h"
+
+namespace android {
+
+/*
+ * Helper class for PointerController that specifically handles
+ * mouse cursor resources and actions.
+ */
+class MouseCursorController {
+public:
+    MouseCursorController(PointerControllerContext& context);
+    ~MouseCursorController();
+
+    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
+    void move(float deltaX, float deltaY);
+    void setButtonState(int32_t buttonState);
+    int32_t getButtonState() const;
+    void setPosition(float x, float y);
+    void getPosition(float* outX, float* outY) const;
+    int32_t getDisplayId() const;
+    void fade(PointerControllerInterface::Transition transition);
+    void unfade(PointerControllerInterface::Transition transition);
+    void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources);
+
+    void updatePointerIcon(int32_t iconId);
+    void setCustomPointerIcon(const SpriteIcon& icon);
+    void reloadPointerResources(bool getAdditionalMouseResources);
+
+    void getAdditionalMouseResources();
+    bool isViewportValid();
+
+    bool doBitmapAnimation(nsecs_t timestamp);
+    bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating);
+
+    bool resourcesLoaded();
+
+private:
+    mutable std::mutex mLock;
+
+    PointerResources mResources;
+
+    PointerControllerContext& mContext;
+
+    struct Locked {
+        DisplayViewport viewport;
+
+        size_t animationFrameIndex;
+        nsecs_t lastFrameUpdatedTime;
+
+        int32_t pointerFadeDirection;
+        float pointerX;
+        float pointerY;
+        float pointerAlpha;
+        sp<Sprite> pointerSprite;
+        SpriteIcon pointerIcon;
+        bool updatePointerIcon;
+
+        bool resourcesLoaded;
+
+        std::map<int32_t, SpriteIcon> additionalMouseResources;
+        std::map<int32_t, PointerAnimation> animationResources;
+
+        int32_t requestedPointerType;
+
+        int32_t buttonState;
+
+    } mLocked GUARDED_BY(mLock);
+
+    bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
+    void setPositionLocked(float x, float y);
+
+    void updatePointerLocked();
+
+    void loadResourcesLocked(bool getAdditionalMouseResources);
+};
+
+} // namespace android
+
+#endif // _UI_MOUSE_CURSOR_CONTROLLER_H
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 5e480a6..14c96ce 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -21,31 +21,26 @@
 #define DEBUG_POINTER_UPDATES 0
 
 #include "PointerController.h"
+#include "MouseCursorController.h"
+#include "PointerControllerContext.h"
+#include "TouchSpotController.h"
 
 #include <log/log.h>
 
-#include <memory>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
 
 namespace android {
 
 // --- PointerController ---
 
-// Time to wait before starting the fade when the pointer is inactive.
-static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
-static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
-
-// Time to spend fading out the spot completely.
-static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
-
-// Time to spend fading out the pointer completely.
-static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
-
-// The number of events to be read at once for DisplayEventReceiver.
-static const int EVENT_BUFFER_SIZE = 100;
-
 std::shared_ptr<PointerController> PointerController::create(
         const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
         const sp<SpriteController>& spriteController) {
+    // using 'new' to access non-public constructor
     std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
             new PointerController(policy, looper, spriteController));
 
@@ -60,758 +55,175 @@
      * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr.
      */
 
-    controller->mHandler->pointerController = controller;
-    controller->mCallback->pointerController = controller;
-    if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) {
-        controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
-                                   Looper::EVENT_INPUT, controller->mCallback, nullptr);
-    } else {
-        ALOGE("Failed to initialize DisplayEventReceiver.");
-    }
+    controller->mContext.setHandlerController(controller);
+    controller->mContext.setCallbackController(controller);
+    controller->mContext.initializeDisplayEventReceiver();
     return controller;
 }
 
 PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
                                      const sp<Looper>& looper,
                                      const sp<SpriteController>& spriteController)
-      : mPolicy(policy),
-        mLooper(looper),
-        mSpriteController(spriteController),
-        mHandler(new MessageHandler()),
-        mCallback(new LooperCallback()) {
-    AutoMutex _l(mLock);
-
-    mLocked.animationPending = false;
-
-    mLocked.presentation = Presentation::POINTER;
-    mLocked.presentationChanged = false;
-
-    mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
-
-    mLocked.pointerFadeDirection = 0;
-    mLocked.pointerX = 0;
-    mLocked.pointerY = 0;
-    mLocked.pointerAlpha = 0.0f; // pointer is initially faded
-    mLocked.pointerSprite = mSpriteController->createSprite();
-    mLocked.pointerIconChanged = false;
-    mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId();
-
-    mLocked.animationFrameIndex = 0;
-    mLocked.lastFrameUpdatedTime = 0;
-
-    mLocked.buttonState = 0;
+      : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
+    std::scoped_lock lock(mLock);
+    mLocked.presentation = Presentation::SPOT;
 }
 
-PointerController::~PointerController() {
-    mLooper->removeMessages(mHandler);
-
-    AutoMutex _l(mLock);
-
-    mLocked.pointerSprite.clear();
-
-    for (auto& it : mLocked.spotsByDisplay) {
-        const std::vector<Spot*>& spots = it.second;
-        size_t numSpots = spots.size();
-        for (size_t i = 0; i < numSpots; i++) {
-            delete spots[i];
-        }
-    }
-    mLocked.spotsByDisplay.clear();
-    mLocked.recycledSprites.clear();
-}
-
-bool PointerController::getBounds(float* outMinX, float* outMinY,
-        float* outMaxX, float* outMaxY) const {
-    AutoMutex _l(mLock);
-
-    return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
-}
-
-bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
-        float* outMaxX, float* outMaxY) const {
-
-    if (!mLocked.viewport.isValid()) {
-        return false;
-    }
-
-    *outMinX = mLocked.viewport.logicalLeft;
-    *outMinY = mLocked.viewport.logicalTop;
-    *outMaxX = mLocked.viewport.logicalRight - 1;
-    *outMaxY = mLocked.viewport.logicalBottom - 1;
-    return true;
+bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
+                                  float* outMaxY) const {
+    return mCursorController.getBounds(outMinX, outMinY, outMaxX, outMaxY);
 }
 
 void PointerController::move(float deltaX, float deltaY) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
-#endif
-    if (deltaX == 0.0f && deltaY == 0.0f) {
-        return;
-    }
-
-    AutoMutex _l(mLock);
-
-    setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
+    mCursorController.move(deltaX, deltaY);
 }
 
 void PointerController::setButtonState(int32_t buttonState) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("Set button state 0x%08x", buttonState);
-#endif
-    AutoMutex _l(mLock);
-
-    if (mLocked.buttonState != buttonState) {
-        mLocked.buttonState = buttonState;
-    }
+    mCursorController.setButtonState(buttonState);
 }
 
 int32_t PointerController::getButtonState() const {
-    AutoMutex _l(mLock);
-
-    return mLocked.buttonState;
+    return mCursorController.getButtonState();
 }
 
 void PointerController::setPosition(float x, float y) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
-#endif
-    AutoMutex _l(mLock);
-
-    setPositionLocked(x, y);
-}
-
-void PointerController::setPositionLocked(float x, float y) {
-    float minX, minY, maxX, maxY;
-    if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
-        if (x <= minX) {
-            mLocked.pointerX = minX;
-        } else if (x >= maxX) {
-            mLocked.pointerX = maxX;
-        } else {
-            mLocked.pointerX = x;
-        }
-        if (y <= minY) {
-            mLocked.pointerY = minY;
-        } else if (y >= maxY) {
-            mLocked.pointerY = maxY;
-        } else {
-            mLocked.pointerY = y;
-        }
-        updatePointerLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.setPosition(x, y);
 }
 
 void PointerController::getPosition(float* outX, float* outY) const {
-    AutoMutex _l(mLock);
-
-    *outX = mLocked.pointerX;
-    *outY = mLocked.pointerY;
+    mCursorController.getPosition(outX, outY);
 }
 
 int32_t PointerController::getDisplayId() const {
-    AutoMutex _l(mLock);
-
-    return mLocked.viewport.displayId;
+    return mCursorController.getDisplayId();
 }
 
 void PointerController::fade(Transition transition) {
-    AutoMutex _l(mLock);
-
-    // Remove the inactivity timeout, since we are fading now.
-    removeInactivityTimeoutLocked();
-
-    // Start fading.
-    if (transition == Transition::IMMEDIATE) {
-        mLocked.pointerFadeDirection = 0;
-        mLocked.pointerAlpha = 0.0f;
-        updatePointerLocked();
-    } else {
-        mLocked.pointerFadeDirection = -1;
-        startAnimationLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.fade(transition);
 }
 
 void PointerController::unfade(Transition transition) {
-    AutoMutex _l(mLock);
-
-    // Always reset the inactivity timer.
-    resetInactivityTimeoutLocked();
-
-    // Start unfading.
-    if (transition == Transition::IMMEDIATE) {
-        mLocked.pointerFadeDirection = 0;
-        mLocked.pointerAlpha = 1.0f;
-        updatePointerLocked();
-    } else {
-        mLocked.pointerFadeDirection = 1;
-        startAnimationLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.unfade(transition);
 }
 
 void PointerController::setPresentation(Presentation presentation) {
-    AutoMutex _l(mLock);
+    std::scoped_lock lock(mLock);
 
     if (mLocked.presentation == presentation) {
         return;
     }
 
     mLocked.presentation = presentation;
-    mLocked.presentationChanged = true;
 
-    if (!mLocked.viewport.isValid()) {
+    if (!mCursorController.isViewportValid()) {
         return;
     }
 
     if (presentation == Presentation::POINTER) {
-        if (mLocked.additionalMouseResources.empty()) {
-            mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                                                  &mLocked.animationResources,
-                                                  mLocked.viewport.displayId);
-        }
-        fadeOutAndReleaseAllSpotsLocked();
-        updatePointerLocked();
+        mCursorController.getAdditionalMouseResources();
+        clearSpotsLocked();
     }
 }
 
-void PointerController::setSpots(const PointerCoords* spotCoords,
-        const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("setSpots: idBits=%08x", spotIdBits.value);
-    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
-        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
-                c.getAxisValue(AMOTION_EVENT_AXIS_X),
-                c.getAxisValue(AMOTION_EVENT_AXIS_Y),
-                c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                displayId);
+void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                                 BitSet32 spotIdBits, int32_t displayId) {
+    std::scoped_lock lock(mLock);
+    auto it = mLocked.spotControllers.find(displayId);
+    if (it == mLocked.spotControllers.end()) {
+        mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
     }
-#endif
-
-    AutoMutex _l(mLock);
-    if (!mLocked.viewport.isValid()) {
-        return;
-    }
-
-    std::vector<Spot*> newSpots;
-    std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
-            mLocked.spotsByDisplay.find(displayId);
-    if (iter != mLocked.spotsByDisplay.end()) {
-        newSpots = iter->second;
-    }
-
-    mSpriteController->openTransaction();
-
-    // Add or move spots for fingers that are down.
-    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
-                ? mResources.spotTouch : mResources.spotHover;
-        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
-        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
-
-        Spot* spot = getSpot(id, newSpots);
-        if (!spot) {
-            spot = createAndAddSpotLocked(id, newSpots);
-        }
-
-        spot->updateSprite(&icon, x, y, displayId);
-    }
-
-    // Remove spots for fingers that went up.
-    for (size_t i = 0; i < newSpots.size(); i++) {
-        Spot* spot = newSpots[i];
-        if (spot->id != Spot::INVALID_ID
-                && !spotIdBits.hasBit(spot->id)) {
-            fadeOutAndReleaseSpotLocked(spot);
-        }
-    }
-
-    mSpriteController->closeTransaction();
-    mLocked.spotsByDisplay[displayId] = newSpots;
+    mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits);
 }
 
 void PointerController::clearSpots() {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("clearSpots");
-#endif
+    std::scoped_lock lock(mLock);
+    clearSpotsLocked();
+}
 
-    AutoMutex _l(mLock);
-    if (!mLocked.viewport.isValid()) {
-        return;
+void PointerController::clearSpotsLocked() REQUIRES(mLock) {
+    for (auto& [displayID, spotController] : mLocked.spotControllers) {
+        spotController.clearSpots();
     }
-
-    fadeOutAndReleaseAllSpotsLocked();
 }
 
 void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
-    AutoMutex _l(mLock);
-
-    if (mLocked.inactivityTimeout != inactivityTimeout) {
-        mLocked.inactivityTimeout = inactivityTimeout;
-        resetInactivityTimeoutLocked();
-    }
+    mContext.setInactivityTimeout(inactivityTimeout);
 }
 
 void PointerController::reloadPointerResources() {
-    AutoMutex _l(mLock);
+    std::scoped_lock lock(mLock);
 
-    loadResourcesLocked();
-    updatePointerLocked();
-}
+    for (auto& [displayID, spotController] : mLocked.spotControllers) {
+        spotController.reloadSpotResources();
+    }
 
-/**
- * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
- * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
- */
-static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
-    width = viewport.deviceWidth;
-    height = viewport.deviceHeight;
-
-    if (viewport.orientation == DISPLAY_ORIENTATION_90
-            || viewport.orientation == DISPLAY_ORIENTATION_270) {
-        std::swap(width, height);
+    if (mCursorController.resourcesLoaded()) {
+        bool getAdditionalMouseResources = false;
+        if (mLocked.presentation == PointerController::Presentation::POINTER) {
+            getAdditionalMouseResources = true;
+        }
+        mCursorController.reloadPointerResources(getAdditionalMouseResources);
     }
 }
 
 void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
-    AutoMutex _l(mLock);
-    if (viewport == mLocked.viewport) {
-        return;
+    std::scoped_lock lock(mLock);
+
+    bool getAdditionalMouseResources = false;
+    if (mLocked.presentation == PointerController::Presentation::POINTER) {
+        getAdditionalMouseResources = true;
     }
-
-    const DisplayViewport oldViewport = mLocked.viewport;
-    mLocked.viewport = viewport;
-
-    int32_t oldDisplayWidth, oldDisplayHeight;
-    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
-    int32_t newDisplayWidth, newDisplayHeight;
-    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
-
-    // Reset cursor position to center if size or display changed.
-    if (oldViewport.displayId != viewport.displayId
-            || oldDisplayWidth != newDisplayWidth
-            || oldDisplayHeight != newDisplayHeight) {
-
-        float minX, minY, maxX, maxY;
-        if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
-            mLocked.pointerX = (minX + maxX) * 0.5f;
-            mLocked.pointerY = (minY + maxY) * 0.5f;
-            // Reload icon resources for density may be changed.
-            loadResourcesLocked();
-        } else {
-            mLocked.pointerX = 0;
-            mLocked.pointerY = 0;
-        }
-
-        fadeOutAndReleaseAllSpotsLocked();
-    } else if (oldViewport.orientation != viewport.orientation) {
-        // Apply offsets to convert from the pixel top-left corner position to the pixel center.
-        // This creates an invariant frame of reference that we can easily rotate when
-        // taking into account that the pointer may be located at fractional pixel offsets.
-        float x = mLocked.pointerX + 0.5f;
-        float y = mLocked.pointerY + 0.5f;
-        float temp;
-
-        // Undo the previous rotation.
-        switch (oldViewport.orientation) {
-        case DISPLAY_ORIENTATION_90:
-            temp = x;
-            x =  oldViewport.deviceHeight - y;
-            y = temp;
-            break;
-        case DISPLAY_ORIENTATION_180:
-            x = oldViewport.deviceWidth - x;
-            y = oldViewport.deviceHeight - y;
-            break;
-        case DISPLAY_ORIENTATION_270:
-            temp = x;
-            x = y;
-            y = oldViewport.deviceWidth - temp;
-            break;
-        }
-
-        // Perform the new rotation.
-        switch (viewport.orientation) {
-        case DISPLAY_ORIENTATION_90:
-            temp = x;
-            x = y;
-            y = viewport.deviceHeight - temp;
-            break;
-        case DISPLAY_ORIENTATION_180:
-            x = viewport.deviceWidth - x;
-            y = viewport.deviceHeight - y;
-            break;
-        case DISPLAY_ORIENTATION_270:
-            temp = x;
-            x = viewport.deviceWidth - y;
-            y = temp;
-            break;
-        }
-
-        // Apply offsets to convert from the pixel center to the pixel top-left corner position
-        // and save the results.
-        mLocked.pointerX = x - 0.5f;
-        mLocked.pointerY = y - 0.5f;
-    }
-
-    updatePointerLocked();
+    mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources);
 }
 
 void PointerController::updatePointerIcon(int32_t iconId) {
-    AutoMutex _l(mLock);
-    if (mLocked.requestedPointerType != iconId) {
-        mLocked.requestedPointerType = iconId;
-        mLocked.presentationChanged = true;
-        updatePointerLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.updatePointerIcon(iconId);
 }
 
 void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
-    AutoMutex _l(mLock);
-
-    const int32_t iconId = mPolicy->getCustomPointerIconId();
-    mLocked.additionalMouseResources[iconId] = icon;
-    mLocked.requestedPointerType = iconId;
-    mLocked.presentationChanged = true;
-
-    updatePointerLocked();
-}
-
-void PointerController::MessageHandler::handleMessage(const Message& message) {
-    std::shared_ptr<PointerController> controller = pointerController.lock();
-
-    if (controller == nullptr) {
-        ALOGE("PointerController instance was released before processing message: what=%d",
-              message.what);
-        return;
-    }
-    switch (message.what) {
-    case MSG_INACTIVITY_TIMEOUT:
-        controller->doInactivityTimeout();
-        break;
-    }
-}
-
-int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) {
-    std::shared_ptr<PointerController> controller = pointerController.lock();
-    if (controller == nullptr) {
-        ALOGW("PointerController instance was released with pending callbacks.  events=0x%x",
-              events);
-        return 0; // Remove the callback, the PointerController is gone anyways
-    }
-    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
-        ALOGE("Display event receiver pipe was closed or an error occurred.  events=0x%x", events);
-        return 0; // remove the callback
-    }
-
-    if (!(events & Looper::EVENT_INPUT)) {
-        ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
-        return 1; // keep the callback
-    }
-
-    bool gotVsync = false;
-    ssize_t n;
-    nsecs_t timestamp;
-    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-        for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
-            if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                timestamp = buf[i].header.timestamp;
-                gotVsync = true;
-            }
-        }
-    }
-    if (gotVsync) {
-        controller->doAnimate(timestamp);
-    }
-    return 1;  // keep the callback
+    std::scoped_lock lock(mLock);
+    mCursorController.setCustomPointerIcon(icon);
 }
 
 void PointerController::doAnimate(nsecs_t timestamp) {
-    AutoMutex _l(mLock);
+    std::scoped_lock lock(mLock);
 
-    mLocked.animationPending = false;
+    mContext.setAnimationPending(false);
 
-    bool keepFading = doFadingAnimationLocked(timestamp);
-    bool keepBitmapFlipping = doBitmapAnimationLocked(timestamp);
+    bool keepFading = false;
+    keepFading = mCursorController.doFadingAnimation(timestamp, keepFading);
+
+    for (auto& [displayID, spotController] : mLocked.spotControllers) {
+        keepFading = spotController.doFadingAnimation(timestamp, keepFading);
+    }
+
+    bool keepBitmapFlipping = mCursorController.doBitmapAnimation(timestamp);
     if (keepFading || keepBitmapFlipping) {
-        startAnimationLocked();
+        mContext.startAnimation();
     }
 }
 
-bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) {
-    bool keepAnimating = false;
-    nsecs_t frameDelay = timestamp - mLocked.animationTime;
-
-    // Animate pointer fade.
-    if (mLocked.pointerFadeDirection < 0) {
-        mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
-        if (mLocked.pointerAlpha <= 0.0f) {
-            mLocked.pointerAlpha = 0.0f;
-            mLocked.pointerFadeDirection = 0;
-        } else {
-            keepAnimating = true;
-        }
-        updatePointerLocked();
-    } else if (mLocked.pointerFadeDirection > 0) {
-        mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
-        if (mLocked.pointerAlpha >= 1.0f) {
-            mLocked.pointerAlpha = 1.0f;
-            mLocked.pointerFadeDirection = 0;
-        } else {
-            keepAnimating = true;
-        }
-        updatePointerLocked();
-    }
-
-    // Animate spots that are fading out and being removed.
-    for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) {
-        std::vector<Spot*>& spots = it->second;
-        size_t numSpots = spots.size();
-        for (size_t i = 0; i < numSpots;) {
-            Spot* spot = spots[i];
-            if (spot->id == Spot::INVALID_ID) {
-                spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
-                if (spot->alpha <= 0) {
-                    spots.erase(spots.begin() + i);
-                    releaseSpotLocked(spot);
-                    numSpots--;
-                    continue;
-                } else {
-                    spot->sprite->setAlpha(spot->alpha);
-                    keepAnimating = true;
-                }
-            }
-            ++i;
-        }
-
-        if (spots.size() == 0) {
-            it = mLocked.spotsByDisplay.erase(it);
-        } else {
-            ++it;
-        }
-    }
-
-    return keepAnimating;
-}
-
-bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) {
-    std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find(
-            mLocked.requestedPointerType);
-    if (iter == mLocked.animationResources.end()) {
-        return false;
-    }
-
-    if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
-        mSpriteController->openTransaction();
-
-        int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
-        mLocked.animationFrameIndex += incr;
-        mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
-        while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
-            mLocked.animationFrameIndex -= iter->second.animationFrames.size();
-        }
-        mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
-
-        mSpriteController->closeTransaction();
-    }
-
-    // Keep animating.
-    return true;
-}
-
 void PointerController::doInactivityTimeout() {
     fade(Transition::GRADUAL);
 }
 
-void PointerController::startAnimationLocked() {
-    if (!mLocked.animationPending) {
-        mLocked.animationPending = true;
-        mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
-        mDisplayEventReceiver.requestNextVsync();
-    }
-}
-
-void PointerController::resetInactivityTimeoutLocked() {
-    mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
-
-    nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
-            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
-            : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
-    mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
-}
-
-void PointerController::removeInactivityTimeoutLocked() {
-    mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
-}
-
-void PointerController::updatePointerLocked() REQUIRES(mLock) {
-    if (!mLocked.viewport.isValid()) {
-        return;
+void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
+    std::unordered_set<int32_t> displayIdSet;
+    for (DisplayViewport viewport : viewports) {
+        displayIdSet.insert(viewport.displayId);
     }
 
-    mSpriteController->openTransaction();
-
-    mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
-    mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
-    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
-
-    if (mLocked.pointerAlpha > 0) {
-        mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
-        mLocked.pointerSprite->setVisible(true);
-    } else {
-        mLocked.pointerSprite->setVisible(false);
-    }
-
-    if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
-        if (mLocked.presentation == Presentation::POINTER) {
-            if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
-                mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
-            } else {
-                std::map<int32_t, SpriteIcon>::const_iterator iter =
-                    mLocked.additionalMouseResources.find(mLocked.requestedPointerType);
-                if (iter != mLocked.additionalMouseResources.end()) {
-                    std::map<int32_t, PointerAnimation>::const_iterator anim_iter =
-                            mLocked.animationResources.find(mLocked.requestedPointerType);
-                    if (anim_iter != mLocked.animationResources.end()) {
-                        mLocked.animationFrameIndex = 0;
-                        mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                        startAnimationLocked();
-                    }
-                    mLocked.pointerSprite->setIcon(iter->second);
-                } else {
-                    ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType);
-                    mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
-                }
-            }
+    std::scoped_lock lock(mLock);
+    for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
+        int32_t displayID = it->first;
+        if (!displayIdSet.count(displayID)) {
+            it = mLocked.spotControllers.erase(it);
         } else {
-            mLocked.pointerSprite->setIcon(mResources.spotAnchor);
-        }
-        mLocked.pointerIconChanged = false;
-        mLocked.presentationChanged = false;
-    }
-
-    mSpriteController->closeTransaction();
-}
-
-PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) {
-    for (size_t i = 0; i < spots.size(); i++) {
-        Spot* spot = spots[i];
-        if (spot->id == id) {
-            return spot;
-        }
-    }
-
-    return nullptr;
-}
-
-PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id,
-        std::vector<Spot*>& spots) {
-    // Remove spots until we have fewer than MAX_SPOTS remaining.
-    while (spots.size() >= MAX_SPOTS) {
-        Spot* spot = removeFirstFadingSpotLocked(spots);
-        if (!spot) {
-            spot = spots[0];
-            spots.erase(spots.begin());
-        }
-        releaseSpotLocked(spot);
-    }
-
-    // Obtain a sprite from the recycled pool.
-    sp<Sprite> sprite;
-    if (! mLocked.recycledSprites.empty()) {
-        sprite = mLocked.recycledSprites.back();
-        mLocked.recycledSprites.pop_back();
-    } else {
-        sprite = mSpriteController->createSprite();
-    }
-
-    // Return the new spot.
-    Spot* spot = new Spot(id, sprite);
-    spots.push_back(spot);
-    return spot;
-}
-
-PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) {
-    for (size_t i = 0; i < spots.size(); i++) {
-        Spot* spot = spots[i];
-        if (spot->id == Spot::INVALID_ID) {
-            spots.erase(spots.begin() + i);
-            return spot;
-        }
-    }
-    return nullptr;
-}
-
-void PointerController::releaseSpotLocked(Spot* spot) {
-    spot->sprite->clearIcon();
-
-    if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
-        mLocked.recycledSprites.push_back(spot->sprite);
-    }
-
-    delete spot;
-}
-
-void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) {
-    if (spot->id != Spot::INVALID_ID) {
-        spot->id = Spot::INVALID_ID;
-        startAnimationLocked();
-    }
-}
-
-void PointerController::fadeOutAndReleaseAllSpotsLocked() {
-    for (auto& it : mLocked.spotsByDisplay) {
-        const std::vector<Spot*>& spots = it.second;
-        size_t numSpots = spots.size();
-        for (size_t i = 0; i < numSpots; i++) {
-            Spot* spot = spots[i];
-            fadeOutAndReleaseSpotLocked(spot);
-        }
-    }
-}
-
-void PointerController::loadResourcesLocked() REQUIRES(mLock) {
-    if (!mLocked.viewport.isValid()) {
-        return;
-    }
-
-    mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
-    mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
-
-    mLocked.additionalMouseResources.clear();
-    mLocked.animationResources.clear();
-    if (mLocked.presentation == Presentation::POINTER) {
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                &mLocked.animationResources, mLocked.viewport.displayId);
-    }
-
-    mLocked.pointerIconChanged = true;
-}
-
-
-// --- PointerController::Spot ---
-
-void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
-        int32_t displayId) {
-    sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
-    sprite->setAlpha(alpha);
-    sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
-    sprite->setPosition(x, y);
-    sprite->setDisplayId(displayId);
-    this->x = x;
-    this->y = y;
-
-    if (icon != lastIcon) {
-        lastIcon = icon;
-        if (icon) {
-            sprite->setIcon(*icon);
-            sprite->setVisible(true);
-        } else {
-            sprite->setVisible(false);
+            ++it;
         }
     }
 }
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 14c0679..1f561da 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -30,48 +30,14 @@
 #include <memory>
 #include <vector>
 
+#include "MouseCursorController.h"
+#include "PointerControllerContext.h"
 #include "SpriteController.h"
+#include "TouchSpotController.h"
 
 namespace android {
 
 /*
- * Pointer resources.
- */
-struct PointerResources {
-    SpriteIcon spotHover;
-    SpriteIcon spotTouch;
-    SpriteIcon spotAnchor;
-};
-
-struct PointerAnimation {
-    std::vector<SpriteIcon> animationFrames;
-    nsecs_t durationPerFrame;
-};
-
-/*
- * Pointer controller policy interface.
- *
- * The pointer controller policy is used by the pointer controller to interact with
- * the Window Manager and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI.  This interface is also mocked in the unit tests.
- */
-class PointerControllerPolicyInterface : public virtual RefBase {
-protected:
-    PointerControllerPolicyInterface() { }
-    virtual ~PointerControllerPolicyInterface() { }
-
-public:
-    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0;
-    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0;
-    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
-            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
-    virtual int32_t getDefaultPointerIconId() = 0;
-    virtual int32_t getCustomPointerIconId() = 0;
-};
-
-/*
  * Tracks pointer movements and draws the pointer sprite to a surface.
  *
  * Handles pointer acceleration and animation.
@@ -81,15 +47,10 @@
     static std::shared_ptr<PointerController> create(
             const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
             const sp<SpriteController>& spriteController);
-    enum class InactivityTimeout {
-        NORMAL = 0,
-        SHORT = 1,
-    };
 
-    virtual ~PointerController();
+    virtual ~PointerController() = default;
 
-    virtual bool getBounds(float* outMinX, float* outMinY,
-            float* outMaxX, float* outMaxY) const;
+    virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     virtual void move(float deltaX, float deltaY);
     virtual void setButtonState(int32_t buttonState);
     virtual int32_t getButtonState() const;
@@ -101,129 +62,37 @@
     virtual void setDisplayViewport(const DisplayViewport& viewport);
 
     virtual void setPresentation(Presentation presentation);
-    virtual void setSpots(const PointerCoords* spotCoords,
-            const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId);
+    virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                          BitSet32 spotIdBits, int32_t displayId);
     virtual void clearSpots();
 
     void updatePointerIcon(int32_t iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
+    void doInactivityTimeout();
+    void doAnimate(nsecs_t timestamp);
     void reloadPointerResources();
+    void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
 
 private:
-    static constexpr size_t MAX_RECYCLED_SPRITES = 12;
-    static constexpr size_t MAX_SPOTS = 12;
+    friend PointerControllerContext::LooperCallback;
+    friend PointerControllerContext::MessageHandler;
 
-    enum {
-        MSG_INACTIVITY_TIMEOUT,
-    };
+    mutable std::mutex mLock;
 
-    struct Spot {
-        static const uint32_t INVALID_ID = 0xffffffff;
+    PointerControllerContext mContext;
 
-        uint32_t id;
-        sp<Sprite> sprite;
-        float alpha;
-        float scale;
-        float x, y;
-
-        inline Spot(uint32_t id, const sp<Sprite>& sprite)
-              : id(id),
-                sprite(sprite),
-                alpha(1.0f),
-                scale(1.0f),
-                x(0.0f),
-                y(0.0f),
-                lastIcon(nullptr) {}
-
-        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
-
-    private:
-        const SpriteIcon* lastIcon;
-    };
-
-    class MessageHandler : public virtual android::MessageHandler {
-    public:
-        void handleMessage(const Message& message) override;
-        std::weak_ptr<PointerController> pointerController;
-    };
-
-    class LooperCallback : public virtual android::LooperCallback {
-    public:
-        int handleEvent(int fd, int events, void* data) override;
-        std::weak_ptr<PointerController> pointerController;
-    };
-
-    mutable Mutex mLock;
-
-    sp<PointerControllerPolicyInterface> mPolicy;
-    sp<Looper> mLooper;
-    sp<SpriteController> mSpriteController;
-    sp<MessageHandler> mHandler;
-    sp<LooperCallback> mCallback;
-
-    DisplayEventReceiver mDisplayEventReceiver;
-
-    PointerResources mResources;
+    MouseCursorController mCursorController;
 
     struct Locked {
-        bool animationPending;
-        nsecs_t animationTime;
-
-        size_t animationFrameIndex;
-        nsecs_t lastFrameUpdatedTime;
-
-        DisplayViewport viewport;
-
-        InactivityTimeout inactivityTimeout;
-
         Presentation presentation;
-        bool presentationChanged;
 
-        int32_t pointerFadeDirection;
-        float pointerX;
-        float pointerY;
-        float pointerAlpha;
-        sp<Sprite> pointerSprite;
-        SpriteIcon pointerIcon;
-        bool pointerIconChanged;
-
-        std::map<int32_t, SpriteIcon> additionalMouseResources;
-        std::map<int32_t, PointerAnimation> animationResources;
-
-        int32_t requestedPointerType;
-
-        int32_t buttonState;
-
-        std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
-        std::vector<sp<Sprite>> recycledSprites;
+        std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
     } mLocked GUARDED_BY(mLock);
 
     PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
                       const sp<SpriteController>& spriteController);
-
-    bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
-    void setPositionLocked(float x, float y);
-
-    void doAnimate(nsecs_t timestamp);
-    bool doFadingAnimationLocked(nsecs_t timestamp);
-    bool doBitmapAnimationLocked(nsecs_t timestamp);
-    void doInactivityTimeout();
-
-    void startAnimationLocked();
-
-    void resetInactivityTimeoutLocked();
-    void removeInactivityTimeoutLocked();
-    void updatePointerLocked();
-
-    Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
-    Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots);
-    Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots);
-    void releaseSpotLocked(Spot* spot);
-    void fadeOutAndReleaseSpotLocked(Spot* spot);
-    void fadeOutAndReleaseAllSpotsLocked();
-
-    void loadResourcesLocked();
+    void clearSpotsLocked();
 };
 
 } // namespace android
diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp
new file mode 100644
index 0000000..2d7e22b
--- /dev/null
+++ b/libs/input/PointerControllerContext.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 "PointerControllerContext.h"
+#include "PointerController.h"
+
+namespace {
+// Time to wait before starting the fade when the pointer is inactive.
+const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
+const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL;   // 3 seconds
+
+// The number of events to be read at once for DisplayEventReceiver.
+const int EVENT_BUFFER_SIZE = 100;
+} // namespace
+
+namespace android {
+
+// --- PointerControllerContext ---
+
+PointerControllerContext::PointerControllerContext(
+        const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController, PointerController& controller)
+      : mPolicy(policy),
+        mLooper(looper),
+        mSpriteController(spriteController),
+        mHandler(new MessageHandler()),
+        mCallback(new LooperCallback()),
+        mController(controller) {
+    std::scoped_lock lock(mLock);
+    mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
+    mLocked.animationPending = false;
+}
+
+PointerControllerContext::~PointerControllerContext() {
+    mLooper->removeMessages(mHandler);
+}
+
+void PointerControllerContext::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.inactivityTimeout != inactivityTimeout) {
+        mLocked.inactivityTimeout = inactivityTimeout;
+        resetInactivityTimeoutLocked();
+    }
+}
+
+void PointerControllerContext::startAnimation() {
+    std::scoped_lock lock(mLock);
+    if (!mLocked.animationPending) {
+        mLocked.animationPending = true;
+        mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        mDisplayEventReceiver.requestNextVsync();
+    }
+}
+
+void PointerControllerContext::resetInactivityTimeout() {
+    std::scoped_lock lock(mLock);
+    resetInactivityTimeoutLocked();
+}
+
+void PointerControllerContext::resetInactivityTimeoutLocked() REQUIRES(mLock) {
+    mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT);
+
+    nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
+            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
+            : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+    mLooper->sendMessageDelayed(timeout, mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT);
+}
+
+void PointerControllerContext::removeInactivityTimeout() {
+    std::scoped_lock lock(mLock);
+    mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT);
+}
+
+void PointerControllerContext::setAnimationPending(bool animationPending) {
+    std::scoped_lock lock(mLock);
+    mLocked.animationPending = animationPending;
+}
+
+nsecs_t PointerControllerContext::getAnimationTime() {
+    std::scoped_lock lock(mLock);
+    return mLocked.animationTime;
+}
+
+void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) {
+    mHandler->pointerController = controller;
+}
+
+void PointerControllerContext::setCallbackController(
+        std::shared_ptr<PointerController> controller) {
+    mCallback->pointerController = controller;
+}
+
+sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() {
+    return mPolicy;
+}
+
+sp<SpriteController> PointerControllerContext::getSpriteController() {
+    return mSpriteController;
+}
+
+void PointerControllerContext::initializeDisplayEventReceiver() {
+    if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
+        mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, Looper::EVENT_INPUT,
+                       mCallback, nullptr);
+    } else {
+        ALOGE("Failed to initialize DisplayEventReceiver.");
+    }
+}
+
+void PointerControllerContext::handleDisplayEvents() {
+    bool gotVsync = false;
+    ssize_t n;
+    nsecs_t timestamp;
+    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+    while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+        for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
+            if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+                timestamp = buf[i].header.timestamp;
+                gotVsync = true;
+            }
+        }
+    }
+    if (gotVsync) {
+        mController.doAnimate(timestamp);
+    }
+}
+
+void PointerControllerContext::MessageHandler::handleMessage(const Message& message) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+
+    if (controller == nullptr) {
+        ALOGE("PointerController instance was released before processing message: what=%d",
+              message.what);
+        return;
+    }
+    switch (message.what) {
+        case MSG_INACTIVITY_TIMEOUT:
+            controller->doInactivityTimeout();
+            break;
+    }
+}
+
+int PointerControllerContext::LooperCallback::handleEvent(int /* fd */, int events,
+                                                          void* /* data */) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+    if (controller == nullptr) {
+        ALOGW("PointerController instance was released with pending callbacks.  events=0x%x",
+              events);
+        return 0; // Remove the callback, the PointerController is gone anyways
+    }
+    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+        ALOGE("Display event receiver pipe was closed or an error occurred.  events=0x%x", events);
+        return 0; // remove the callback
+    }
+
+    if (!(events & Looper::EVENT_INPUT)) {
+        ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
+        return 1; // keep the callback
+    }
+
+    controller->mContext.handleDisplayEvents();
+    return 1; // keep the callback
+}
+
+} // namespace android
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
new file mode 100644
index 0000000..92e1bda
--- /dev/null
+++ b/libs/input/PointerControllerContext.h
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_POINTER_CONTROLLER_CONTEXT_H
+#define _UI_POINTER_CONTROLLER_CONTEXT_H
+
+#include <PointerControllerInterface.h>
+#include <gui/DisplayEventReceiver.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <ui/DisplayInfo.h>
+#include <utils/BitSet.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "SpriteController.h"
+
+namespace android {
+
+class PointerController;
+
+/*
+ * Pointer resources.
+ */
+struct PointerResources {
+    SpriteIcon spotHover;
+    SpriteIcon spotTouch;
+    SpriteIcon spotAnchor;
+};
+
+struct PointerAnimation {
+    std::vector<SpriteIcon> animationFrames;
+    nsecs_t durationPerFrame;
+};
+
+enum class InactivityTimeout {
+    NORMAL = 0,
+    SHORT = 1,
+};
+
+/*
+ * Pointer controller policy interface.
+ *
+ * The pointer controller policy is used by the pointer controller to interact with
+ * the Window Manager and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ */
+class PointerControllerPolicyInterface : public virtual RefBase {
+protected:
+    PointerControllerPolicyInterface() {}
+    virtual ~PointerControllerPolicyInterface() {}
+
+public:
+    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0;
+    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0;
+    virtual void loadAdditionalMouseResources(
+            std::map<int32_t, SpriteIcon>* outResources,
+            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
+    virtual int32_t getDefaultPointerIconId() = 0;
+    virtual int32_t getCustomPointerIconId() = 0;
+};
+
+/*
+ * Contains logic and resources shared among PointerController,
+ * MouseCursorController, and TouchSpotController.
+ */
+
+class PointerControllerContext {
+public:
+    PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy,
+                             const sp<Looper>& looper, const sp<SpriteController>& spriteController,
+                             PointerController& controller);
+    ~PointerControllerContext();
+
+    void removeInactivityTimeout();
+    void resetInactivityTimeout();
+    void startAnimation();
+    void setInactivityTimeout(InactivityTimeout inactivityTimeout);
+
+    void setAnimationPending(bool animationPending);
+    nsecs_t getAnimationTime();
+
+    void clearSpotsByDisplay(int32_t displayId);
+
+    void setHandlerController(std::shared_ptr<PointerController> controller);
+    void setCallbackController(std::shared_ptr<PointerController> controller);
+
+    sp<PointerControllerPolicyInterface> getPolicy();
+    sp<SpriteController> getSpriteController();
+
+    void initializeDisplayEventReceiver();
+    void handleDisplayEvents();
+
+    class MessageHandler : public virtual android::MessageHandler {
+    public:
+        enum {
+            MSG_INACTIVITY_TIMEOUT,
+        };
+
+        void handleMessage(const Message& message) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
+    class LooperCallback : public virtual android::LooperCallback {
+    public:
+        int handleEvent(int fd, int events, void* data) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
+private:
+    sp<PointerControllerPolicyInterface> mPolicy;
+    sp<Looper> mLooper;
+    sp<SpriteController> mSpriteController;
+    sp<MessageHandler> mHandler;
+    sp<LooperCallback> mCallback;
+
+    DisplayEventReceiver mDisplayEventReceiver;
+
+    PointerController& mController;
+
+    mutable std::mutex mLock;
+
+    struct Locked {
+        bool animationPending;
+        nsecs_t animationTime;
+
+        InactivityTimeout inactivityTimeout;
+    } mLocked GUARDED_BY(mLock);
+
+    void resetInactivityTimeoutLocked();
+};
+
+} // namespace android
+
+#endif // _UI_POINTER_CONTROLLER_CONTEXT_H
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
new file mode 100644
index 0000000..c7430ce
--- /dev/null
+++ b/libs/input/TouchSpotController.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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 LOG_TAG "TouchSpotController"
+
+// Log debug messages about pointer updates
+#define DEBUG_SPOT_UPDATES 0
+
+#include "TouchSpotController.h"
+
+#include <log/log.h>
+
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+
+namespace {
+// Time to spend fading out the spot completely.
+const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
+} // namespace
+
+namespace android {
+
+// --- Spot ---
+
+void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+                                             int32_t displayId) {
+    sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
+    sprite->setAlpha(alpha);
+    sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
+    sprite->setPosition(x, y);
+    sprite->setDisplayId(displayId);
+    this->x = x;
+    this->y = y;
+
+    if (icon != mLastIcon) {
+        mLastIcon = icon;
+        if (icon) {
+            sprite->setIcon(*icon);
+            sprite->setVisible(true);
+        } else {
+            sprite->setVisible(false);
+        }
+    }
+}
+
+// --- TouchSpotController ---
+
+TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
+      : mDisplayId(displayId), mContext(context) {
+    mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
+}
+
+TouchSpotController::~TouchSpotController() {
+    std::scoped_lock lock(mLock);
+
+    size_t numSpots = mLocked.displaySpots.size();
+    for (size_t i = 0; i < numSpots; i++) {
+        delete mLocked.displaySpots[i];
+    }
+    mLocked.displaySpots.clear();
+}
+
+void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                                   BitSet32 spotIdBits) {
+#if DEBUG_SPOT_UPDATES
+    ALOGD("setSpots: idBits=%08x", spotIdBits.value);
+    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+        uint32_t id = idBits.firstMarkedBit();
+        idBits.clearBit(id);
+        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
+              c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y),
+              c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId);
+    }
+#endif
+
+    std::scoped_lock lock(mLock);
+    sp<SpriteController> spriteController = mContext.getSpriteController();
+    spriteController->openTransaction();
+
+    // Add or move spots for fingers that are down.
+    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+        const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
+                ? mResources.spotTouch
+                : mResources.spotHover;
+        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
+        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
+
+        Spot* spot = getSpot(id, mLocked.displaySpots);
+        if (!spot) {
+            spot = createAndAddSpotLocked(id, mLocked.displaySpots);
+        }
+
+        spot->updateSprite(&icon, x, y, mDisplayId);
+    }
+
+    for (Spot* spot : mLocked.displaySpots) {
+        if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) {
+            fadeOutAndReleaseSpotLocked(spot);
+        }
+    }
+
+    spriteController->closeTransaction();
+}
+
+void TouchSpotController::clearSpots() {
+#if DEBUG_SPOT_UPDATES
+    ALOGD("clearSpots");
+#endif
+
+    std::scoped_lock lock(mLock);
+    fadeOutAndReleaseAllSpotsLocked();
+}
+
+TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id,
+                                                        const std::vector<Spot*>& spots) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
+        if (spot->id == id) {
+            return spot;
+        }
+    }
+    return nullptr;
+}
+
+TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id,
+                                                                       std::vector<Spot*>& spots) {
+    // Remove spots until we have fewer than MAX_SPOTS remaining.
+    while (spots.size() >= MAX_SPOTS) {
+        Spot* spot = removeFirstFadingSpotLocked(spots);
+        if (!spot) {
+            spot = spots[0];
+            spots.erase(spots.begin());
+        }
+        releaseSpotLocked(spot);
+    }
+
+    // Obtain a sprite from the recycled pool.
+    sp<Sprite> sprite;
+    if (!mLocked.recycledSprites.empty()) {
+        sprite = mLocked.recycledSprites.back();
+        mLocked.recycledSprites.pop_back();
+    } else {
+        sprite = mContext.getSpriteController()->createSprite();
+    }
+
+    // Return the new spot.
+    Spot* spot = new Spot(id, sprite);
+    spots.push_back(spot);
+    return spot;
+}
+
+TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked(
+        std::vector<Spot*>& spots) REQUIRES(mLock) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
+        if (spot->id == Spot::INVALID_ID) {
+            spots.erase(spots.begin() + i);
+            return spot;
+        }
+    }
+    return NULL;
+}
+
+void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) {
+    spot->sprite->clearIcon();
+
+    if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
+        mLocked.recycledSprites.push_back(spot->sprite);
+    }
+
+    delete spot;
+}
+
+void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) {
+    if (spot->id != Spot::INVALID_ID) {
+        spot->id = Spot::INVALID_ID;
+        mContext.startAnimation();
+    }
+}
+
+void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) {
+    size_t numSpots = mLocked.displaySpots.size();
+    for (size_t i = 0; i < numSpots; i++) {
+        Spot* spot = mLocked.displaySpots[i];
+        fadeOutAndReleaseSpotLocked(spot);
+    }
+}
+
+void TouchSpotController::reloadSpotResources() {
+    mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
+}
+
+bool TouchSpotController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) {
+    std::scoped_lock lock(mLock);
+    nsecs_t animationTime = mContext.getAnimationTime();
+    nsecs_t frameDelay = timestamp - animationTime;
+    size_t numSpots = mLocked.displaySpots.size();
+    for (size_t i = 0; i < numSpots;) {
+        Spot* spot = mLocked.displaySpots[i];
+        if (spot->id == Spot::INVALID_ID) {
+            spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
+            if (spot->alpha <= 0) {
+                mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i);
+                releaseSpotLocked(spot);
+                numSpots--;
+                continue;
+            } else {
+                spot->sprite->setAlpha(spot->alpha);
+                keepAnimating = true;
+            }
+        }
+        ++i;
+    }
+    return keepAnimating;
+}
+
+} // namespace android
diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h
new file mode 100644
index 0000000..f3b3550
--- /dev/null
+++ b/libs/input/TouchSpotController.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_TOUCH_SPOT_CONTROLLER_H
+#define _UI_TOUCH_SPOT_CONTROLLER_H
+
+#include "PointerControllerContext.h"
+
+namespace android {
+
+/*
+ * Helper class for PointerController that specifically handles
+ * touch spot resources and actions for a single display.
+ */
+class TouchSpotController {
+public:
+    TouchSpotController(int32_t displayId, PointerControllerContext& context);
+    ~TouchSpotController();
+    void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                  BitSet32 spotIdBits);
+    void clearSpots();
+
+    void reloadSpotResources();
+    bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating);
+
+private:
+    struct Spot {
+        static const uint32_t INVALID_ID = 0xffffffff;
+
+        uint32_t id;
+        sp<Sprite> sprite;
+        float alpha;
+        float scale;
+        float x, y;
+
+        inline Spot(uint32_t id, const sp<Sprite>& sprite)
+              : id(id),
+                sprite(sprite),
+                alpha(1.0f),
+                scale(1.0f),
+                x(0.0f),
+                y(0.0f),
+                mLastIcon(nullptr) {}
+
+        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
+
+    private:
+        const SpriteIcon* mLastIcon;
+    };
+
+    int32_t mDisplayId;
+
+    mutable std::mutex mLock;
+
+    PointerResources mResources;
+
+    PointerControllerContext& mContext;
+
+    static constexpr size_t MAX_RECYCLED_SPRITES = 12;
+    static constexpr size_t MAX_SPOTS = 12;
+
+    struct Locked {
+        std::vector<Spot*> displaySpots;
+        std::vector<sp<Sprite>> recycledSprites;
+
+    } mLocked GUARDED_BY(mLock);
+
+    Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
+    Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots);
+    Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots);
+    void releaseSpotLocked(Spot* spot);
+    void fadeOutAndReleaseSpotLocked(Spot* spot);
+    void fadeOutAndReleaseAllSpotsLocked();
+};
+
+} // namespace android
+
+#endif // _UI_TOUCH_SPOT_CONTROLLER_H
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 6e129a0..b67088a 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -178,9 +178,6 @@
     viewport.deviceWidth = 400;
     viewport.deviceHeight = 300;
     mPointerController->setDisplayViewport(viewport);
-
-    // The first call to setDisplayViewport should trigger the loading of the necessary resources.
-    EXPECT_TRUE(mPolicy->allResourcesAreLoaded());
 }
 
 void PointerControllerTest::loopThread() {
@@ -208,6 +205,7 @@
 
 TEST_F(PointerControllerTest, updatePointerIcon) {
     ensureDisplayViewportIsSet();
+    mPointerController->setPresentation(PointerController::Presentation::POINTER);
     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     int32_t type = CURSOR_TYPE_ADDITIONAL;
@@ -247,8 +245,6 @@
 
 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
     mPointerController->setPresentation(PointerController::Presentation::POINTER);
-    mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
-    mPointerController->clearSpots();
     mPointerController->setPosition(1.0f, 1.0f);
     mPointerController->move(1.0f, 1.0f);
     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
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/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 6fa3787..2b3f420c 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -1616,9 +1616,9 @@
         Drawable mIcon;
         // playback information
         int mPlaybackType = PLAYBACK_TYPE_LOCAL;
-        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
-        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
-        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
+        int mVolumeMax = DEFAULT_PLAYBACK_MAX_VOLUME;
+        int mVolume = DEFAULT_PLAYBACK_VOLUME;
+        int mVolumeHandling = PLAYBACK_VOLUME_VARIABLE;
         int mPlaybackStream = AudioManager.STREAM_MUSIC;
         VolumeCallbackInfo mVcb;
         Display mPresentationDisplay;
@@ -1722,6 +1722,21 @@
          */
         public final static int PLAYBACK_VOLUME_VARIABLE = 1;
 
+        /**
+         * Default playback max volume if not set.
+         * Hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC]
+         *
+         * @see #getVolumeMax()
+         */
+        private static final int DEFAULT_PLAYBACK_MAX_VOLUME = 15;
+
+        /**
+         * Default playback volume if not set.
+         *
+         * @see #getVolume()
+         */
+        private static final int DEFAULT_PLAYBACK_VOLUME = DEFAULT_PLAYBACK_MAX_VOLUME;
+
         RouteInfo(RouteCategory category) {
             mCategory = category;
             mDeviceType = DEVICE_TYPE_UNKNOWN;
@@ -2430,13 +2445,13 @@
                 }
                 return;
             }
-            if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
+            if (mPlaybackType == PLAYBACK_TYPE_REMOTE) {
                 int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED;
                 switch (mVolumeHandling) {
-                    case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
+                    case PLAYBACK_VOLUME_VARIABLE:
                         volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
                         break;
-                    case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
+                    case PLAYBACK_VOLUME_FIXED:
                     default:
                         break;
                 }
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index e959e8e..5d44040 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -225,13 +225,35 @@
             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.setJobProgress(newProgress);
+
+            // Notifies client the progress update.
+            if (job.mProgressUpdateExecutor != null && job.mProgressUpdateListener != null) {
+                job.mProgressUpdateExecutor.execute(
+                        () -> job.mProgressUpdateListener.onProgressUpdate(newProgress));
+            }
+        }
+    }
+
     // Just forwards all the events to the event handler.
     private ITranscodingClientCallback mTranscodingClientCallback =
             new ITranscodingClientCallback.Stub() {
@@ -294,8 +316,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);
                 }
             };
 
@@ -821,17 +843,8 @@
             return mResult;
         }
 
-        private void setJobProgress(int newProgress) {
-            synchronized (this) {
-                mProgress = newProgress;
-            }
-
-            // Notify listener.
-            OnProgressUpdateListener onProgressUpdateListener = mProgressUpdateListener;
-            if (mProgressUpdateListener != null) {
-                mProgressUpdateExecutor.execute(
-                        () -> onProgressUpdateListener.onProgressUpdate(mProgress));
-            }
+        private synchronized void setJobProgress(int newProgress) {
+            mProgress = newProgress;
         }
     }
 
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 561a884..697d80c 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -220,6 +220,34 @@
         return textDump;
     }
 
+    /**
+     * Very short dump of configuration
+     * @return a condensed dump of configuration, uniquely identifies a policy in a log
+     */
+    public String toCompactLogString() {
+        String compactDump = "reg:" + mRegistrationId;
+        int mixNum = 0;
+        for (AudioMix mix : mMixes) {
+            compactDump += " Mix:" + mixNum + "-Typ:" + mixTypePrefix(mix.getMixType())
+                    + "-Rul:" + mix.getRule().getCriteria().size();
+            mixNum++;
+        }
+        return compactDump;
+    }
+
+    private static String mixTypePrefix(int mixType) {
+        switch (mixType) {
+            case AudioMix.MIX_TYPE_PLAYERS:
+                return "p";
+            case AudioMix.MIX_TYPE_RECORDERS:
+                return "r";
+            case AudioMix.MIX_TYPE_INVALID:
+            default:
+                return "#";
+
+        }
+    }
+
     protected void reset() {
         mMixCounter = 0;
     }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 8bf462c..70bd160 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -105,6 +106,7 @@
      *
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
 
     /**
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 3a5e692..8e41530 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -37,6 +37,7 @@
 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 +231,63 @@
                 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);
+
+        // 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);
+                        mPreviousProgress = newProgress;
+                        progressUpdateCount.getAndIncrement();
+                        Log.i(TAG, "Get progress update " + newProgress);
+                    }
+                });
+
+        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 54618a5..e06cf94 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11900,6 +11900,7 @@
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
@@ -24046,6 +24047,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 3c0b955..35b483b 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -39,6 +39,14 @@
 
 }
 
+package android.media.session {
+
+  public final class MediaSession {
+    field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000
+  }
+
+}
+
 package android.os {
 
   public class Binder implements android.os.IBinder {
@@ -46,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/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 844e929..0906a06 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -9357,7 +9357,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle);
     method @Deprecated public android.content.ComponentName getDefaultPhoneApp();
-    method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
+    method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
@@ -11575,6 +11575,9 @@
   public interface PacProcessor {
     method @Nullable public String findProxyForUrl(@NonNull String);
     method @NonNull public static android.webkit.PacProcessor getInstance();
+    method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long);
+    method public default long getNetworkHandle();
+    method public default void releasePacProcessor();
     method public boolean setProxyScript(@NonNull String);
   }
 
@@ -11714,6 +11717,7 @@
     method public android.webkit.CookieManager getCookieManager();
     method public android.webkit.GeolocationPermissions getGeolocationPermissions();
     method @NonNull public default android.webkit.PacProcessor getPacProcessor();
+    method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long);
     method public android.webkit.ServiceWorkerController getServiceWorkerController();
     method public android.webkit.WebViewFactoryProvider.Statics getStatics();
     method @Deprecated public android.webkit.TokenBindingService getTokenBindingService();
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/SystemUI/res/drawable/tv_circle_dark.xml b/packages/CarSystemUI/samples/sample1/rro/AndroidManifest.xml
similarity index 62%
copy from packages/SystemUI/res/drawable/tv_circle_dark.xml
copy to packages/CarSystemUI/samples/sample1/rro/AndroidManifest.xml
index d1ba8e7..5c25056 100644
--- a/packages/SystemUI/res/drawable/tv_circle_dark.xml
+++ b/packages/CarSystemUI/samples/sample1/rro/AndroidManifest.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ 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.
@@ -15,10 +14,11 @@
   ~ limitations under the License.
   -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-
-  <solid
-      android:color="@color/tv_audio_recording_indicator_background" />
-
-</shape>
\ No newline at end of file
+<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/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
index 5dcb9de..a2cd044 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
@@ -39,7 +39,7 @@
 
 /**
  * A class that detects unsafe apps.
- * An app is considered safe if is a system app or installed through whitelisted sources.
+ * An app is considered safe if is a system app or installed through allowed sources.
  */
 @Singleton
 public class SideLoadedAppDetector {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index 31f1170..f77294e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
@@ -19,6 +19,8 @@
 import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE;
 import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -40,11 +42,13 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @CarSystemUiTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
+// TODO(b/162866441): Refactor to use the Executor pattern instead.
 public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
 
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -52,13 +56,15 @@
 
     private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier;
     private TestableLooper mTestableLooper;
+    private Handler mHandler;
     private Handler mTestHandler;
     private BluetoothDevice mBluetoothDevice;
 
     @Before
     public void setUp() throws Exception {
         mTestableLooper = TestableLooper.get(this);
-        mTestHandler = spy(new Handler(mTestableLooper.getLooper()));
+        mHandler = new Handler(mTestableLooper.getLooper());
+        mTestHandler = spy(mHandler);
         mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
                 BLUETOOTH_REMOTE_ADDRESS);
         mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier(
@@ -74,8 +80,14 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler).post(any());
+        mHandler.post(() -> {
+            ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+            verify(mTestHandler).post(argumentCaptor.capture());
+            assertThat(argumentCaptor.getValue()).isNotNull();
+            assertThat(argumentCaptor.getValue()).isNotEqualTo(this);
+        });
     }
 
     @Test
@@ -86,8 +98,11 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 
     @Test
@@ -97,8 +112,11 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 
     @Test
@@ -108,7 +126,10 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 }
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/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
similarity index 87%
rename from packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
index a12aa83..a08f566 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
@@ -19,9 +19,9 @@
 import android.os.Bundle;
 
 /**
- * A controller that manages event for master switch.
+ * A controller that manages event for Primary switch.
  */
-public abstract class MasterSwitchController extends SwitchController {
+public abstract class PrimarySwitchController extends SwitchController {
 
     @Override
     protected final MetaData getMetaData() {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
index 73f1a90..f2b3e30 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
@@ -88,7 +88,7 @@
 
             controller.setAuthority(mAuthority);
             mControllerMap.put(key, controller);
-            if (!(controller instanceof MasterSwitchController)) {
+            if (!(controller instanceof PrimarySwitchController)) {
                 mSwitchDataList.add(controller.getBundle());
             }
         });
@@ -116,7 +116,7 @@
 
         switch (method) {
             case METHOD_GET_SWITCH_DATA:
-                if (!(controller instanceof MasterSwitchController)) {
+                if (!(controller instanceof PrimarySwitchController)) {
                     return controller.getBundle();
                 }
                 break;
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 1e4c7ca..52d2b3c 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -376,8 +376,12 @@
      * Check whether tile only has primary profile.
      */
     public boolean isPrimaryProfileOnly() {
-        String profile = mMetaData != null
-                ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+        return isPrimaryProfileOnly(mMetaData);
+    }
+
+    static boolean isPrimaryProfileOnly(Bundle metaData) {
+        String profile = metaData != null
+                ? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
         profile = (profile != null ? profile : PROFILE_ALL);
         return TextUtils.equals(profile, PROFILE_PRIMARY);
     }
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index ace50f3..49f6bd8 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -339,6 +339,16 @@
     private static void loadTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache,
             String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData,
             ComponentInfo componentInfo) {
+        // Skip loading tile if the component is tagged primary_profile_only but not running on
+        // the current user.
+        if (user.getIdentifier() != ActivityManager.getCurrentUser()
+                && Tile.isPrimaryProfileOnly(componentInfo.metaData)) {
+            Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent "
+                    + intent + " is primary profile only, skip loading tile for uid "
+                    + user.getIdentifier());
+            return;
+        }
+
         String categoryKey = defaultCategory;
         // Load category
         if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 704d264..6751fa4 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Wys Bluetooth-toestelle sonder name"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiveer absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiveer Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde konnektiwiteit"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-weergawe"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Kies Bluetooth AVRCP-weergawe"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-weergawe"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 585924d..470e780 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"የብሉቱዝ መሣሪያዎችን ያለ ስሞች አሳይ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ፍጹማዊ ድምፅን አሰናክል"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheን አንቃ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"የተሻሻለ ተገናኝነት"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"የብሉቱዝ AVRCP ስሪት"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"የብሉቱዝ AVRCP ስሪት ይምረጡ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"የብሉቱዝ MAP ስሪት"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 39777cd..9acfa0d 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -152,7 +152,7 @@
     <string name="user_guest" msgid="6939192779649870792">"ضيف"</string>
     <string name="unknown" msgid="3544487229740637809">"غير معروف"</string>
     <string name="running_process_item_user_label" msgid="3988506293099805796">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
-    <string name="launch_defaults_some" msgid="3631650616557252926">"تم تعيين بعض الإعدادات التلقائية"</string>
+    <string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string>
     <string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string>
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"عرض أجهزة البلوتوث بدون أسماء"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"إيقاف مستوى الصوت المطلق"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏تفعيل Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"إمكانية اتصال محسّن"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏إصدار Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏اختيار إصدار Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏إصدار Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index e0455cb..f993dba 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"পূৰ্ণ মাত্ৰাৰ ভলিউম অক্ষম কৰক"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche সক্ষম কৰক"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"উন্নত সংযোগ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP সংস্কৰণ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP সংস্কৰণ বাছনি কৰক"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP সংস্কৰণ"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 6565d53..6a50661 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth cihazlarını adsız göstərin"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mütləq səs həcmi deaktiv edin"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'ni aktiv edin"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Təkmilləşdirilmiş Bağlantı"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Versiya"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Versiyasını seçin"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Versiyası"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3ced29b..cf988ab 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući glavno podešavanje jačine zvuka"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšano povezivanje"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija Bluetooth AVRCP-a"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izaberite verziju Bluetooth AVRCP-a"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija Bluetooth MAP-a"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 01d7682..8f71509 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Паказваць прылады Bluetooth без назваў"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Адключыць абсалютны гук"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Уключыць Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Палепшанае падключэнне"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выбраць версію Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index d042c0f..747cb26 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показване на устройствата с Bluetooth без имена"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Деактивиране на пълната сила на звука"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Активиране на Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена свързаност"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия на AVRCP за Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Избиране на версия на AVRCP за Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP версия за Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 2db23f7..87f3b7a 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখুন"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"চূড়ান্ত ভলিউম অক্ষম করুন"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ফিচার চালু করুন"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"কানেক্টিভিটি উন্নত করা হয়েছে"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP ভার্সন"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP ভার্সন বেছে নিন"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP ভার্সন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index f26fe9d..e329c99 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu jačinu zvuka"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP verzija"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite Bluetooth AVRCP verziju"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP verzija"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 3ca9d52..5ffdacd 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra els dispositius Bluetooth sense el nom"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactiva el volum absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activa Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivitat millorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versió AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versió AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versió MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index a5532e0..0aef99f 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovat zařízení Bluetooth bez názvů"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázat absolutní hlasitost"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Zapnout funkci Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepší připojování"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verze profilu Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vyberte verzi profilu Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verze MAP pro Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 8ca22d7..98068cb 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheder uden navne"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiver absolut lydstyrke"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivér Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version for Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vælg AVRCP-version for Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version for Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 6b0ae2e2..0837ad3 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-Geräte ohne Namen anzeigen"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absolute Lautstärkeregelung deaktivieren"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bluetooth-Gabeldorsche aktivieren"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbesserte Konnektivität"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-Version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP-Version auswählen"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-Version"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 4d7c882..1f9d977 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Εμφάνιση συσκευών Bluetooth χωρίς ονόματα"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Απενεργοποίηση απόλυτης έντασης"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ενεργοποίηση Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Βελτιωμένη συνδεσιμότητα"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Έκδοση AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Επιλογή έκδοσης AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Έκδοση MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index cc3b2aa..abe61a9 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index a9f039a..959ad46 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index cc3b2aa..abe61a9 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index cc3b2aa..abe61a9 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 41c20e0..738dd2a 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎Show Bluetooth devices without names‎‏‎‎‏‎"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎Disable absolute volume‎‏‎‎‏‎"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎Enable Gabeldorsche‎‏‎‎‏‎"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‎‎‎Enhanced Connectivity‎‏‎‎‏‎"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎Bluetooth AVRCP Version‎‏‎‎‏‎"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎Select Bluetooth AVRCP Version‎‏‎‎‏‎"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎Bluetooth MAP Version‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 287a1ac..d1e4fb5 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de AVRCP del Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión de AVRCP del Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 9d34557..9ad71e2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index d003ef0..14d3b57 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Kuva ilma nimedeta Bluetoothi seadmed"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Keela absoluutne helitugevus"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Luba Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Täiustatud ühenduvus"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothi AVRCP versioon"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valige Bluetoothi AVRCP versioon"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothi MAP-i versioon"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 0042321..dcdadbd 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth bidezko gailuak izenik gabe"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desgaitu bolumen absolutua"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Konexio hobeak"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP bertsioa"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Hautatu Bluetooth AVRCP bertsioa"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAParen bertsioa"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1c08815..f3b22d3 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"نمایش دستگاه‌های بلوتوث بدون نام"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"غیرفعال کردن میزان صدای مطلق"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏فعال کردن Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"اتصال بهبودیافته"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏نسخه AVRCP بلوتوث"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏انتخاب نسخه AVRCP بلوتوث"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏نسخه MAP بلوتوث"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 3945e55..3d28f1d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Näytä nimettömät Bluetooth-laitteet"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Poista yleinen äänenvoimakkuuden säätö käytöstä"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ota Gabeldorsche käyttöön"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Parannetut yhteydet"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothin AVRCP-versio"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valitse Bluetoothin AVRCP-versio"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothin MAP-versio"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 140d4ce..87d3de1 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer le Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version du profil Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version du profil Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version du profil Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 1b1ae8e..5f5be97 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -165,7 +165,7 @@
     <string name="tts_lang_not_selected" msgid="7927823081096056147">"Langue non sélectionnée"</string>
     <string name="tts_default_lang_summary" msgid="9042620014800063470">"Définir la langue utilisée par la synthèse vocale"</string>
     <string name="tts_play_example_title" msgid="1599468547216481684">"Écouter un échantillon"</string>
-    <string name="tts_play_example_summary" msgid="634044730710636383">"Lire une courte démonstration de la synthèse vocale"</string>
+    <string name="tts_play_example_summary" msgid="634044730710636383">"Écoutez une courte démonstration de la synthèse vocale"</string>
     <string name="tts_install_data_title" msgid="1829942496472751703">"Installer les données vocales"</string>
     <string name="tts_install_data_summary" msgid="3608874324992243851">"Installer les données nécessaires à la synthèse vocale"</string>
     <string name="tts_engine_security_warning" msgid="3372432853837988146">"Ce moteur de synthèse vocale est susceptible de collecter tout ce qui sera lu, y compris les données personnelles comme les mots de passe et les numéros de carte de paiement. Il provient du moteur <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Voulez-vous activer son utilisation ?"</string>
@@ -189,8 +189,8 @@
     <item msgid="1158955023692670059">"Rapide"</item>
     <item msgid="5664310435707146591">"Plus rapide"</item>
     <item msgid="5491266922147715962">"Très rapide"</item>
-    <item msgid="7659240015901486196">"Rapide"</item>
-    <item msgid="7147051179282410945">"Très rapide"</item>
+    <item msgid="7659240015901486196">"Beaucoup plus rapide"</item>
+    <item msgid="7147051179282410945">"Extrêmement rapide"</item>
     <item msgid="581904787661470707">"La plus rapide"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Sélectionner un profil"</string>
@@ -251,13 +251,12 @@
     <string name="wifi_display_certification" msgid="1805579519992520381">"Certification affichage sans fil"</string>
     <string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string>
     <string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche Wi‑Fi"</string>
-    <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC sur Wi-Fi"</string>
+    <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC en Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8275958101875563572">"Données mobiles toujours actives"</string>
     <string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string>
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version Bluetooth MAP"</string>
@@ -284,7 +283,7 @@
     <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afficher les options pour la certification de l\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Détailler les infos Wi-Fi, afficher par RSSI de SSID dans l\'outil de sélection Wi-Fi"</string>
     <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string>
-    <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse e-mail MAC de cet appareil peut changer lors de chaque connexion à un réseau pour lequel le changement aléatoire d\'adresse MAC est activé."</string>
+    <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse MAC de cet appareil peut changer lors de chaque connexion à un réseau Wi-Fi pour lequel le changement aléatoire d\'adresse MAC est activé"</string>
     <string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string>
     <string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string>
     <string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index f9d57c4..af43099 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sen nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactivar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade mellorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona a versión de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index aa1f960..3261f69 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"નામ વિનાના બ્લૂટૂથ ઉપકરણો બતાવો"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ચોક્કસ વૉલ્યૂમને અક્ષમ કરો"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ચાલુ કરો"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"વિસ્તૃત કનેક્ટિવિટી"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"બ્લૂટૂથ AVRCP સંસ્કરણ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"બ્લૂટૂથ AVRCP સંસ્કરણ પસંદ કરો"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"બ્લૂટૂથ MAP વર્ઝન"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 9b6a27a..904a70e 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ब्लूटूथ से आवाज़ के नियंत्रण की सुविधा रोकें"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche चालू करें"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"कनेक्टिविटी बेहतर बनाएं"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ एवीआरसीपी वर्शन"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP वर्शन चुनें"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ का MAP वर्शन"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 14e3330..3edc452 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu glasnoću"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija AVRCP-a za Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite verziju AVRCP-a za Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija MAP-a za Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d16ff03..fec2dd6 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Név nélküli Bluetooth-eszközök megjelenítése"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Abszolút hangerő funkció letiltása"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"A Gabeldorsche engedélyezése"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"A Bluetooth AVRCP-verziója"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"A Bluetooth AVRCP-verziójának kiválasztása"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"A Bluetooth MAP-verziója"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index b010b50..f219d24 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ցուցադրել Bluetooth սարքերն առանց անունների"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Անջատել ձայնի բացարձակ ուժգնությունը"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Միացնել Gabeldorsche-ը"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Տվյալների լավացված փոխանակում"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP տարբերակը"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Ընտրել Bluetooth AVRCP տարբերակը"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ի տարբերակ"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 37cf189f..3ab50cc 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -31,7 +31,7 @@
     <item msgid="7852381437933824454">"Memutus sambungan..."</item>
     <item msgid="5046795712175415059">"Sambungan terputus"</item>
     <item msgid="2473654476624070462">"Gagal"</item>
-    <item msgid="9146847076036105115">"Dicekal"</item>
+    <item msgid="9146847076036105115">"Diblokir"</item>
     <item msgid="4543924085816294893">"Menghindari sambungan buruk untuk sementara"</item>
   </string-array>
   <string-array name="wifi_status_with_ssid">
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 42ccd53..a6f8846 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -180,8 +180,8 @@
     <string name="tts_engine_settings_button" msgid="477155276199968948">"Luncurkan setelan mesin"</string>
     <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mesin yang dipilih"</string>
     <string name="tts_general_section_title" msgid="8919671529502364567">"Umum"</string>
-    <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Setel ulang tinggi nada ucapan"</string>
-    <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Setel ulang tinggi nada diucapkannya teks menjadi default."</string>
+    <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Reset tinggi nada ucapan"</string>
+    <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Reset tinggi nada diucapkannya teks menjadi default."</string>
   <string-array name="tts_rate_entries">
     <item msgid="9004239613505400644">"Sangat lambat"</item>
     <item msgid="1815382991399815061">"Lambat"</item>
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tampilkan perangkat Bluetooth tanpa nama"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Nonaktifkan volume absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktifkan Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Konektivitas Yang Disempurnakan"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 0ebc341..caf2323 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Sýna Bluetooth-tæki án heita"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slökkva á samstillingu hljóðstyrks"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Virkja Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Aukin tengigeta"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-útgáfa"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velja Bluetooth AVRCP-útgáfu"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-útgáfa"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 50fdfc9..8d18727 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra dispositivi Bluetooth senza nome"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disattiva volume assoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Attiva Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connettività migliorata"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versione Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Seleziona versione Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versione Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index fb7d00f..fff881c 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"‏הצגת מכשירי Bluetooth ללא שמות"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"השבת עוצמת קול מוחלטת"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏הפעלת Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"קישוריות משופרת"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏Bluetooth גרסה AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏בחר Bluetooth גרסה AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏גרסת Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 3537cea..5e579b7 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth デバイスを名前なしで表示"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"絶対音量を無効にする"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche を有効にする"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"接続強化"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP バージョン"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP バージョンを選択する"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP バージョン"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 9b671a8..1b5fae9 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-მოწყობილობების ჩვენება სახელების გარეშე"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ხმის აბსოლუტური სიძლიერის გათიშვა"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-ის ჩართვა"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"კავშირის გაძლიერებული შესაძლებლობა"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-ის AVRCP-ის ვერსია"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"აირჩიეთ Bluetooth-ის AVRCP-ის ვერსია"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ის ვერსია"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 279aca0..9c290e9 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth құрылғыларын атаусыз көрсету"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Абсолютті дыбыс деңгейін өшіру"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын іске қосу"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Жетілдірілген байланыс"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP нұсқасы"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP нұсқасын таңдау"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP нұсқасы"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 03065e8..2878db1 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"បង្ហាញ​ឧបករណ៍​ប្ល៊ូធូស​គ្មានឈ្មោះ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"បិទកម្រិតសំឡេងលឺខ្លាំង"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"បើក Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ការតភ្ជាប់​ដែលបានធ្វើឱ្យប្រសើរឡើង"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"កំណែប្ល៊ូធូស AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ជ្រើសរើសកំណែប្ល៊ូធូស AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"កំណែ​ប៊្លូធូស MAP"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 71c5e49..7b01056 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ಹೆಸರುಗಳಿಲ್ಲದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ಸಂಪೂರ್ಣ ವಾಲ್ಯೂಮ್‌ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ವರ್ಧಿತ ಸಂಪರ್ಕ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿಯನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ಬ್ಲೂಟೂತ್ MAP ಆವೃತ್ತಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 5d82eae..696ed29 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"이름이 없는 블루투스 기기 표시"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"절대 볼륨 사용 안함"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche 사용 설정"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"향상된 연결"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"블루투스 AVRCP 버전"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"블루투스 AVRCP 버전 선택"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"블루투스 MAP 버전"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 2702392..c4b5f7e 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Аталышсыз Bluetooth түзмөктөрү көрсөтүлсүн"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үндүн абсолюттук деңгээли өчүрүлсүн"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын иштетүү"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Жакшыртылган туташуу"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP версиясы"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP версиясын тандоо"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP версиясы"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 7a2c338..a72861e 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ປິດໃຊ້ລະດັບສຽງສົມບູນ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ເປີດໃຊ້ Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ການເຊື່ອມຕໍ່ທີ່ເສີມແຕ່ງແລ້ວ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ເວີຊັນ Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ເລືອກເວີຊັນ Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ເວີຊັນ Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index b73aa66..c72bf21 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rodyti „Bluetooth“ įrenginius be pavadinimų"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Išjungti didžiausią garsą"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Įgalinti „Gabeldorsche“"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Patobulintas ryšys"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"„Bluetooth“ AVRCP versija"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pasirinkite „Bluetooth“ AVRCP versiją"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"„Bluetooth“ MRK versija"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 8e5d24c..d95e57d 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rādīt Bluetooth ierīces bez nosaukumiem"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Atspējot absolūto skaļumu"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Iespējot Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Uzlabota savienojamība"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versija"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Atlasiet Bluetooth AVRCP versiju"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versija"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 34299d8..fb7b634 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажувај уреди со Bluetooth без имиња"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Оневозможете апсолутна јачина на звук"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Овозможи Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена поврзливост"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изберете верзија Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија на Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index c95f8bf..3c281c8 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ കാണിക്കുക"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"അബ്‌സൊല്യൂട്ട് വോളിയം പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche പ്രവർത്തനക്ഷമമാക്കുക"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"മെച്ചപ്പെടുത്തിയ കണക്റ്റിവിറ്റി"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP പതിപ്പ്"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP പതിപ്പ് തിരഞ്ഞെടുക്കുക"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP പതിപ്പ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 8407db6..37fc5b4 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Нэргүй Bluetooth төхөөрөмжийг харуулах"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үнэмлэхүй дууны түвшинг идэвхгүй болгох"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-г идэвхжүүлэх"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Сайжруулсан холболт"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP хувилбар"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP хувилбарыг сонгох"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP хувилбар"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index c50f365..360f158 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नावांशिवाय ब्‍लूटूथ डिव्‍हाइस दाखवा"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"संपूर्ण आवाज बंद करा"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"गाबलडॉर्ष सुरू करा"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"वर्धित कनेक्टिव्हिटी"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ AVRCP आवृत्ती"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP आवृत्ती निवडा"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ MAP आवृत्ती"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a0a434f..68356df2 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tunjukkan peranti Bluetooth tanpa nama"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Lumpuhkan kelantangan mutlak"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Dayakan Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Kesambungan Dipertingkat"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index fa49929..3729a83 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"အမည်မရှိသော ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသရန်"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ပကတိ အသံနှုန်း သတ်မှတ်ချက် ပိတ်ရန်"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ကို ဖွင့်ရန်"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"အရည်အသွေးမြှင့်တင်ထားသော ချိတ်ဆက်နိုင်မှု"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ဘလူးတုသ် AVRCP ဗားရှင်း"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ဘလူးတုသ် AVRCP ဗားရှင်းကို ရွေးပါ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ဘလူးတုသ် MAP ဗားရှင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index aeaba31..0e0e761 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheter uten navn"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slå av funksjonen for absolutt volum"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiver Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Forbedret tilkobling"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-versjon"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velg Bluetooth AVRCP-versjon"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-versjon"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 4a2c171..762d830 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू देखाउनुहोस्"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"निरपेक्ष आवाज असक्षम गर्नुहोस्"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche सक्षम पार्नुहोस्"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"परिष्कृत जडान"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लुटुथको AVRCP संस्करण"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लुटुथको AVRCP संस्करण चयन गर्नुहोस्"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लुटुथको MAP संस्करण"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 32cc39e..83b72e9 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-apparaten zonder namen weergeven"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absoluut volume uitschakelen"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche inschakelen"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde connectiviteit"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-AVRCP-versie"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth-AVRCP-versie selecteren"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-versie voor bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 8e5bf25..d200f50 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ବ୍ଲୁଟୂଥ୍‍‌ ଡିଭାଇସ୍‌ଗୁଡ଼ିକୁ ନାମ ବିନା ଦେଖନ୍ତୁ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ପୂର୍ଣ୍ଣ ଭଲ୍ୟୁମ୍‌ ଅକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ଗାବେଲ୍‌ଡୋର୍ସ ସକ୍ରିୟ କରନ୍ତୁ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ଏନହାନ୍ସଡ୍ କନେକ୍ଟିଭିଟି"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ବ୍ଲୁଟୂଥ୍‌ AVRCP ଭର୍ସନ୍"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ବ୍ଲୁଟୂଥ୍‍‌ AVRCP ଭର୍ସନ୍‌"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ବ୍ଲୁଟୁଥ୍ MAP ସଂସ୍କରଣ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 1570013..354ee12 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਓ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ਪੂਰਨ ਅਵਾਜ਼ ਨੂੰ ਬੰਦ ਕਰੋ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ਵਿਸਤ੍ਰਿਤ ਕਨੈਕਟੀਵਿਟੀ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ ਚੁਣੋ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP ਵਰਜਨ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 1120c50..095412c 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Pokaż urządzenia Bluetooth bez nazw"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Wyłącz głośność bezwzględną"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Włącz Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepsza obsługa połączeń"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Wersja AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Wybierz wersję AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Wersja MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 4214a27..895a987 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 508cbfc..2d9f037 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar o Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conetividade melhorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão do MAP do Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 4214a27..895a987 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 663d3f7..728db17 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afișați dispozitivele Bluetooth fără nume"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Dezactivați volumul absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activați Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectivitate îmbunătățită"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectați versiunea AVRCP pentru Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 9c0a2f5..ff2115e 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показывать Bluetooth-устройства без названий"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Отключить абсолютный уровень громкости"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Включить Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Улучшенный обмен данными"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выберите версию Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версия Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 8d0e93e..a883cc6 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"නම් නොමැති බ්ලූටූත් උපාංග පෙන්වන්න"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"නිරපේක්ෂ හඩ පරිමාව අබල කරන්න"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche සබල කරන්න"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"වැඩිදියුණු කළ සබැඳුම් හැකියාව"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"බ්ලූටූත් AVRCP අනුවාදය"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"බ්ලූටූත් AVRCP අනුවාදය තෝරන්න"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP අනුවාදය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index f39a741..05c6379 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovať zariadenia Bluetooth bez názvov"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázať absolútnu hlasitosť"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Povoliť Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Zlepšené možnosti pripojenia"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzia rozhrania Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zvoľte verziu rozhrania Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzia profilu Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 233c8e4..fd216e8 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži naprave Bluetooth brez imen"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogočanje absolutne glasnosti"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogoči Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Izboljšana povezljivost"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Različica profila AVRCP za Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izberite različico profila AVRCP za Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Različica profila MAP za Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 6af1062..002c7fc 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Shfaq pajisjet me Bluetooth pa emra"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Çaktivizo volumin absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivizo Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Lidhshmëria e përmirësuar"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versioni AVRCP i Bluetooth-it"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 74c2aec..25a1beb7 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажи Bluetooth уређаје без назива"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Онемогући главно подешавање јачине звука"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Омогући Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Побољшано повезивање"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP-а"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изаберите верзију Bluetooth AVRCP-а"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија Bluetooth MAP-а"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index fe1b0a8..352cb0a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Visa namnlösa Bluetooth-enheter"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inaktivera Absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivera Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Förbättrad anslutning"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version för Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Välj AVRCP-version för Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version för Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 5c80627..d2891a0 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Onyesha vifaa vya Bluetooth visivyo na majina"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zima sauti kamili"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Washa Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Muunganisho Ulioboreshwa"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Toleo la Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chagua Toleo la Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Toleo la Ramani ya Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 241644f..7837dd8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கு"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheவை இயக்கு"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"மேம்படுத்தப்பட்ட இணைப்புநிலை"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"புளூடூத் AVRCP பதிப்பு"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"புளூடூத் AVRCP பதிப்பைத் தேர்ந்தெடு"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"புளூடூத்தின் MAP பதிப்பு"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a9ec2ea9..60001a0 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"పేర్లు లేని బ్లూటూత్ పరికరాలు  చూపించు"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"సంపూర్ణ వాల్యూమ్‌‍ను నిలిపివేయి"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheను ఎనేబుల్ చేయి"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"మెరుగైన కనెక్టివిటీ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"బ్లూటూత్ AVRCP వెర్షన్"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP సంస్కరణను ఎంచుకోండి"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"బ్లూటూత్ MAP వెర్షన్‌"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b8343c6..defc33e 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"แสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ปิดใช้การควบคุมระดับเสียงของอุปกรณ์อื่น"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"เปิดใช้ Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"การเชื่อมต่อที่ปรับปรุงแล้ว"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"เวอร์ชันของบลูทูธ AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"เลือกเวอร์ชันของบลูทูธ AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"เวอร์ชัน MAP ของบลูทูธ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 8aeb392..5d4e975 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ipakita ang mga Bluetooth device na walang pangalan"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"I-disable ang absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"I-enable ang Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Pinagandang Pagkakonekta"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bersyon ng AVRCP ng Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pumili ng Bersyon ng AVRCP ng Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bersyon ng MAP ng Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index e6d9380..f01f3fa 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Adsız Bluetooth cihazlarını göster"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mutlak sesi iptal et"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'yi etkileştir"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Gelişmiş Bağlantı"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Sürümü"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Sürümünü seçin"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Sürümü"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index cf1fafd..9ca2f06 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показувати пристрої Bluetooth без назв"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Вимкнути абсолютну гучність"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Увімкнути Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Покращене з\'єднання"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Виберіть версію Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index b7fbe6f..8953f50 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"مطلق والیوم کو غیر فعال کریں"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏Gabeldorsche فعال کریں"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"بہتر کردہ کنیکٹوٹی"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏بلوٹوتھ AVRCP ورژن"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏بلوٹوتھ AVRCP ورژن منتخب کریں"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏بلوٹوتھ MAP ورژن"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index f81731a..f25b3ac 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth qurilmalarini nomlarisiz ko‘rsatish"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Tovush balandligining mutlaq darajasini faolsizlantirish"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche funksiyasini yoqish"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Kuchaytirilgan aloqa"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versiyasi"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP versiyasini tanlang"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versiyasi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b7ccf8d..b5798f3 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Hiển thị các thiết bị Bluetooth không có tên"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Vô hiệu hóa âm lượng tuyệt đối"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bật tính năng Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Kết nối nâng cao"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Phiên bản Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chọn phiên bản Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Phiên bản Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 75c1333..c4dcfff 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"显示没有名称的蓝牙设备"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用绝对音量功能"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"启用“Gabeldorsche”"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"增强连接性"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"蓝牙 AVRCP 版本"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"选择蓝牙 AVRCP 版本"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"蓝牙 MAP 版本"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 26ddfb1..e04651c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"強化連線功能"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選擇藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 72ea043..a1ae6b6 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"加強型連線"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選取藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 6b8739f..2dafad8 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bonisa amadivayisi e-Bluetooth ngaphandle kwamagama"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Khubaza ivolumu ngokuphelele"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Nika amandla i-Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Ukuxhumeka Okuthuthukisiwe"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Inguqulo ye-Bluetooth ye-AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Khetha inguqulo ye-Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Inguqulo ye-Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
index f757aa4..b29595e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -24,7 +24,7 @@
 import com.android.settingslib.core.AbstractPreferenceController;
 
 /**
- * This controller is used handle changes for the master switch in the developer options page.
+ * This controller is used handle changes for the primary switch in the developer options page.
  *
  * All Preference Controllers that are a part of the developer options page should inherit this
  * class.
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
similarity index 69%
rename from packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
rename to packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 3c647a7..c501b3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -34,44 +34,50 @@
 import com.android.internal.util.ArrayUtils;
 
 /**
- * Handles getting/changing the whitelist for the exceptions to battery saving features.
+ * Handles getting/changing the allowlist for the exceptions to battery saving features.
  */
-public class PowerWhitelistBackend {
+public class PowerAllowlistBackend {
 
-    private static final String TAG = "PowerWhitelistBackend";
+    private static final String TAG = "PowerAllowlistBackend";
 
     private static final String DEVICE_IDLE_SERVICE = "deviceidle";
 
-    private static PowerWhitelistBackend sInstance;
+    private static PowerAllowlistBackend sInstance;
 
     private final Context mAppContext;
     private final IDeviceIdleController mDeviceIdleService;
-    private final ArraySet<String> mWhitelistedApps = new ArraySet<>();
-    private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>();
+    private final ArraySet<String> mAllowlistedApps = new ArraySet<>();
+    private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>();
     private final ArraySet<String> mDefaultActiveApps = new ArraySet<>();
 
-    public PowerWhitelistBackend(Context context) {
+    public PowerAllowlistBackend(Context context) {
         this(context, IDeviceIdleController.Stub.asInterface(
                 ServiceManager.getService(DEVICE_IDLE_SERVICE)));
     }
 
     @VisibleForTesting
-    PowerWhitelistBackend(Context context, IDeviceIdleController deviceIdleService) {
+    PowerAllowlistBackend(Context context, IDeviceIdleController deviceIdleService) {
         mAppContext = context.getApplicationContext();
         mDeviceIdleService = deviceIdleService;
         refreshList();
     }
 
-    public int getWhitelistSize() {
-        return mWhitelistedApps.size();
+    public int getAllowlistSize() {
+        return mAllowlistedApps.size();
     }
 
-    public boolean isSysWhitelisted(String pkg) {
-        return mSysWhitelistedApps.contains(pkg);
+    /**
+    * Check if target package is in System allow list
+    */
+    public boolean isSysAllowlisted(String pkg) {
+        return mSysAllowlistedApps.contains(pkg);
     }
 
-    public boolean isWhitelisted(String pkg) {
-        if (mWhitelistedApps.contains(pkg)) {
+    /**
+     * Check if target package is in allow list
+     */
+    public boolean isAllowlisted(String pkg) {
+        if (mAllowlistedApps.contains(pkg)) {
             return true;
         }
 
@@ -87,7 +93,7 @@
      */
     public boolean isDefaultActiveApp(String pkg) {
         // Additionally, check if pkg is default dialer/sms. They are considered essential apps and
-        // should be automatically whitelisted (otherwise user may be able to set restriction on
+        // should be automatically allowlisted (otherwise user may be able to set restriction on
         // them, leading to bad device behavior.)
 
         if (mDefaultActiveApps.contains(pkg)) {
@@ -103,12 +109,17 @@
         return false;
     }
 
-    public boolean isWhitelisted(String[] pkgs) {
+    /**
+     *
+     * @param pkgs a list of packageName
+     * @return true when one of package is in allow list
+     */
+    public boolean isAllowlisted(String[] pkgs) {
         if (ArrayUtils.isEmpty(pkgs)) {
             return false;
         }
         for (String pkg : pkgs) {
-            if (isWhitelisted(pkg)) {
+            if (isAllowlisted(pkg)) {
                 return true;
             }
         }
@@ -116,40 +127,51 @@
         return false;
     }
 
+    /**
+     * Add app into power save allow list.
+     * @param pkg packageName
+     */
     public void addApp(String pkg) {
         try {
             mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
-            mWhitelistedApps.add(pkg);
+            mAllowlistedApps.add(pkg);
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         }
     }
 
+    /**
+     * Remove package from power save allow list.
+     * @param pkg
+     */
     public void removeApp(String pkg) {
         try {
             mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
-            mWhitelistedApps.remove(pkg);
+            mAllowlistedApps.remove(pkg);
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         }
     }
 
+    /**
+     * Refresh all of lists
+     */
     @VisibleForTesting
     public void refreshList() {
-        mSysWhitelistedApps.clear();
-        mWhitelistedApps.clear();
+        mSysAllowlistedApps.clear();
+        mAllowlistedApps.clear();
         mDefaultActiveApps.clear();
         if (mDeviceIdleService == null) {
             return;
         }
         try {
-            final String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist();
-            for (String app : whitelistedApps) {
-                mWhitelistedApps.add(app);
+            final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist();
+            for (String app : allowlistedApps) {
+                mAllowlistedApps.add(app);
             }
-            final String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist();
-            for (String app : sysWhitelistedApps) {
-                mSysWhitelistedApps.add(app);
+            final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
+            for (String app : sysAllowlistedApps) {
+                mSysAllowlistedApps.add(app);
             }
             final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TELEPHONY);
@@ -171,9 +193,13 @@
         }
     }
 
-    public static PowerWhitelistBackend getInstance(Context context) {
+    /**
+     * @param context
+     * @return a PowerAllowlistBackend object
+     */
+    public static PowerAllowlistBackend getInstance(Context context) {
         if (sInstance == null) {
-            sInstance = new PowerWhitelistBackend(context);
+            sInstance = new PowerAllowlistBackend(context);
         }
         return sInstance;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
index 4941f7e..8ac4349 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -135,7 +135,7 @@
                 // Ignore
             }
         } else {
-            // Blacklist all other apps, system or downloaded
+            // Denylist all other apps, system or downloaded
             try {
                 ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
                 if (info != null) {
@@ -258,11 +258,11 @@
             }
         }
 
-        // Establish master/slave relationship for entries that share a package name
+        // Establish primary/secondary relationship for entries that share a package name
         HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
         for (SelectableAppInfo info : mVisibleApps) {
             if (packageMap.containsKey(info.packageName)) {
-                info.masterEntry = packageMap.get(info.packageName);
+                info.primaryEntry = packageMap.get(info.packageName);
             } else {
                 packageMap.put(info.packageName, info);
             }
@@ -366,12 +366,12 @@
         public CharSequence appName;
         public CharSequence activityName;
         public Drawable icon;
-        public SelectableAppInfo masterEntry;
+        public SelectableAppInfo primaryEntry;
 
         @Override
         public String toString() {
             return packageName + ": appName=" + appName + "; activityName=" + activityName
-                    + "; icon=" + icon + "; masterEntry=" + masterEntry;
+                    + "; icon=" + icon + "; primaryEntry=" + primaryEntry;
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index 2fb2481..df98b17 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -108,8 +108,8 @@
     public TestAccessPointBuilder setActive(boolean active) {
         if (active) {
             mNetworkInfo = new NetworkInfo(
-                ConnectivityManager.TYPE_DUMMY,
-                ConnectivityManager.TYPE_DUMMY,
+                ConnectivityManager.TYPE_WIFI,
+                ConnectivityManager.TYPE_WIFI,
                 "TestNetwork",
                 "TestNetwork");
         } else {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 11c799e..94e28f2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -290,12 +290,12 @@
     @Test
     public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() {
         EnforcedAdmin enforcedAdmin = new EnforcedAdmin();
-        enforcedAdmin.enforcedRestriction = "Dummy";
+        enforcedAdmin.enforcedRestriction = "Fake";
         RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin);
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
-        assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Dummy");
+        assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Fake");
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
similarity index 86%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
index 69d0f2e..9e4cde8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
@@ -23,16 +23,16 @@
 import org.robolectric.RobolectricTestRunner;
 
 @RunWith(RobolectricTestRunner.class)
-public class MasterSwitchControllerTest {
+public class PrimarySwitchControllerTest {
 
     @Rule
     public final ExpectedException thrown = ExpectedException.none();
 
-    private MasterSwitchController mController;
+    private PrimarySwitchController mController;
 
     @Before
     public void setUp() {
-        mController = new TestMasterSwitchController("123");
+        mController = new TestPrimarySwitchController("123");
     }
 
     @Test
@@ -49,11 +49,11 @@
         mController.getBundle();
     }
 
-    static class TestMasterSwitchController extends MasterSwitchController {
+    static class TestPrimarySwitchController extends PrimarySwitchController {
 
         private String mKey;
 
-        TestMasterSwitchController(String key) {
+        TestPrimarySwitchController(String key) {
             mKey = key;
         }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
index a740e68..bd0100b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
@@ -35,7 +35,7 @@
 import android.content.pm.ProviderInfo;
 import android.os.Bundle;
 
-import com.android.settingslib.drawer.MasterSwitchControllerTest.TestMasterSwitchController;
+import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController;
 import com.android.settingslib.drawer.SwitchController.MetaData;
 
 import org.junit.Before;
@@ -124,8 +124,8 @@
     }
 
     @Test
-    public void getSwitchData_shouldNotReturnMasterSwitchData() {
-        final SwitchController controller = new TestMasterSwitchController("123");
+    public void getSwitchData_shouldNotReturnPrimarySwitchData() {
+        final SwitchController controller = new TestPrimarySwitchController("123");
         mSwitchesProvider.addSwitchController(controller);
         mSwitchesProvider.attachInfo(mContext, mProviderInfo);
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 9b4b97e..1769053 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -17,11 +17,14 @@
 package com.android.settingslib.drawer;
 
 import static com.android.settingslib.drawer.TileUtils.IA_SETTINGS_ACTION;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -189,7 +192,7 @@
         List<Tile> outTiles = new ArrayList<>();
         List<ResolveInfo> info = new ArrayList<>();
         ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
-                URI_GET_SUMMARY, "my title", 0);
+                URI_GET_SUMMARY, "my title", 0, PROFILE_ALL);
         info.add(resolveInfo);
 
         when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
@@ -211,7 +214,7 @@
         List<Tile> outTiles = new ArrayList<>();
         List<ResolveInfo> info = new ArrayList<>();
         ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
-                URI_GET_SUMMARY, null, 123);
+                URI_GET_SUMMARY, null, 123, PROFILE_ALL);
         info.add(resolveInfo);
 
         when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
@@ -235,7 +238,7 @@
         List<Tile> outTiles = new ArrayList<>();
         List<ResolveInfo> info = new ArrayList<>();
         ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
-                URI_GET_SUMMARY, null, 123);
+                URI_GET_SUMMARY, null, 123, PROFILE_ALL);
         resolveInfo.activityInfo.packageName = "com.android.settings";
         resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings";
         info.add(resolveInfo);
@@ -258,7 +261,7 @@
         final List<Tile> outTiles = new ArrayList<>();
         final List<ResolveInfo> info = new ArrayList<>();
         final ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
-                URI_GET_SUMMARY, null, 123);
+                URI_GET_SUMMARY, null, 123, PROFILE_ALL);
         resolveInfo.activityInfo.packageName = "com.android.settings";
         resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings";
         info.add(resolveInfo);
@@ -290,7 +293,7 @@
         List<Tile> outTiles = new ArrayList<>();
         List<ResolveInfo> info = new ArrayList<>();
         ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
-                URI_GET_SUMMARY, null, 123);
+                URI_GET_SUMMARY, null, 123, PROFILE_ALL);
         resolveInfo.activityInfo.metaData
                 .putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, true);
         info.add(resolveInfo);
@@ -327,6 +330,26 @@
         assertThat(outTiles).hasSize(2);
     }
 
+    @Test
+    public void loadTilesForAction_isPrimaryProfileOnly_shouldSkipNonPrimaryUserTiles() {
+        Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+        List<Tile> outTiles = new ArrayList<>();
+        List<ResolveInfo> info = new ArrayList<>();
+        ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
+                URI_GET_SUMMARY, null, 123, PROFILE_PRIMARY);
+        info.add(resolveInfo);
+
+        when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
+                .thenReturn(info);
+        when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
+                anyInt())).thenReturn(info);
+
+        TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION,
+                addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
+
+        assertThat(outTiles).isEmpty();
+    }
+
     public static ResolveInfo newInfo(boolean systemApp, String category) {
         return newInfo(systemApp, category, null);
     }
@@ -337,14 +360,14 @@
 
     private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint,
             String iconUri, String summaryUri) {
-        return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0);
+        return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0, PROFILE_ALL);
     }
 
     private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint,
-            String iconUri, String summaryUri, String title, int titleResId) {
+            String iconUri, String summaryUri, String title, int titleResId, String profile) {
 
         final Bundle metaData = newMetaData(category, keyHint, iconUri, summaryUri, title,
-                titleResId);
+                titleResId, profile);
         final ResolveInfo info = new ResolveInfo();
         info.system = systemApp;
 
@@ -358,6 +381,7 @@
         info.providerInfo.packageName = "abc";
         info.providerInfo.name = "456";
         info.providerInfo.authority = "auth";
+        info.providerInfo.metaData = metaData;
         ShadowTileUtils.setMetaData(metaData);
         info.providerInfo.applicationInfo = new ApplicationInfo();
 
@@ -369,7 +393,7 @@
     }
 
     private static Bundle newMetaData(String category, String keyHint, String iconUri,
-            String summaryUri, String title, int titleResId) {
+            String summaryUri, String title, int titleResId, String profile) {
         final Bundle metaData = new Bundle();
         metaData.putString("com.android.settings.category", category);
         metaData.putInt(META_DATA_PREFERENCE_ICON, 314159);
@@ -388,6 +412,9 @@
         } else if (title != null) {
             metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE, title);
         }
+        if (profile != null) {
+            metaData.putString(META_DATA_KEY_PROFILE, profile);
+        }
         return metaData;
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
similarity index 67%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 2090892..4f11fb1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -47,7 +47,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
-public class PowerWhitelistBackendTest {
+public class PowerAllowlistBackendTest {
 
     private static final String PACKAGE_ONE = "com.example.packageone";
     private static final String PACKAGE_TWO = "com.example.packagetwo";
@@ -56,7 +56,7 @@
     private IDeviceIdleController mDeviceIdleService;
     @Mock
     private DevicePolicyManager mDevicePolicyManager;
-    private PowerWhitelistBackend mPowerWhitelistBackend;
+    private PowerAllowlistBackend mPowerAllowlistBackend;
     private ShadowPackageManager mPackageManager;
     private Context mContext;
 
@@ -74,81 +74,81 @@
         mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true);
         doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class);
 
-        mPowerWhitelistBackend = new PowerWhitelistBackend(mContext, mDeviceIdleService);
+        mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService);
     }
 
     @Test
-    public void testIsWhitelisted() throws Exception {
+    public void testIsAllowlisted() throws Exception {
         doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
 
-        mPowerWhitelistBackend.addApp(PACKAGE_TWO);
+        mPowerAllowlistBackend.addApp(PACKAGE_TWO);
 
         verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO);
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(
                 new String[] {PACKAGE_ONE, PACKAGE_TWO})).isTrue();
 
-        mPowerWhitelistBackend.removeApp(PACKAGE_TWO);
+        mPowerAllowlistBackend.removeApp(PACKAGE_TWO);
 
         verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO);
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
 
-        mPowerWhitelistBackend.removeApp(PACKAGE_ONE);
+        mPowerAllowlistBackend.removeApp(PACKAGE_ONE);
 
         verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE);
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(
                 new String[] {PACKAGE_ONE, PACKAGE_TWO})).isFalse();
     }
 
     @Test
-    public void isWhitelisted_shouldWhitelistDefaultSms() {
+    public void isAllowlisted_shouldAllowlistDefaultSms() {
         final String testSms = "com.android.test.defaultsms";
         ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(testSms, "receiver"));
 
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(testSms)).isTrue();
-        assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testSms)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(testSms)).isTrue();
+        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms)).isTrue();
     }
 
     @Test
-    public void isWhitelisted_shouldWhitelistDefaultDialer() {
+    public void isAllowlisted_shouldAllowlistDefaultDialer() {
         final String testDialer = "com.android.test.defaultdialer";
         ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer);
 
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(testDialer)).isTrue();
-        assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testDialer)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer)).isTrue();
+        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer)).isTrue();
     }
 
     @Test
-    public void isWhitelisted_shouldWhitelistActiveDeviceAdminApp() {
+    public void isAllowlisted_shouldAllowlistActiveDeviceAdminApp() {
         doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
     }
 
     @Test
-    public void testIsSystemWhitelisted() throws Exception {
+    public void testIsSystemAllowlisted() throws Exception {
         doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
+        assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index b930aa6..84d722a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -197,61 +197,61 @@
     public void isValidSystemNonAuxAsciiCapableIme() {
         // System IME w/ no subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false)))
+                createFakeIme(true, false)))
                 .isFalse();
 
         // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false, createDummySubtype("keyboard", false, false))))
+                createFakeIme(true, false, createFakeSubtype("keyboard", false, false))))
                 .isFalse();
 
         // System IME w/ non-Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false, createDummySubtype("keyboard", false, true))))
+                createFakeIme(true, false, createFakeSubtype("keyboard", false, true))))
                 .isTrue();
 
         // System IME w/ Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, true, createDummySubtype("keyboard", true, true))))
+                createFakeIme(true, true, createFakeSubtype("keyboard", true, true))))
                 .isFalse();
 
         // System IME w/ non-Aux and ASCII-capable "voice" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false, createDummySubtype("voice", false, true))))
+                createFakeIme(true, false, createFakeSubtype("voice", false, true))))
                 .isFalse();
 
         // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false,
-                        createDummySubtype("keyboard", false, true),
-                        createDummySubtype("keyboard", false, false))))
+                createFakeIme(true, false,
+                        createFakeSubtype("keyboard", false, true),
+                        createFakeSubtype("keyboard", false, false))))
                 .isTrue();
 
         // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
+                createFakeIme(false, false, createFakeSubtype("keyboard", false, true))))
                 .isFalse();
     }
 
-    private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
+    private static InputMethodInfo createFakeIme(boolean isSystem, boolean isAuxIme,
             InputMethodSubtype... subtypes) {
         final ResolveInfo ri = new ResolveInfo();
         final ServiceInfo si = new ServiceInfo();
         final ApplicationInfo ai = new ApplicationInfo();
-        ai.packageName = "com.example.android.dummyime";
+        ai.packageName = "com.example.android.fakeime";
         ai.enabled = true;
         ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
         si.applicationInfo = ai;
         si.enabled = true;
-        si.packageName = "com.example.android.dummyime";
-        si.name = "Dummy IME";
+        si.packageName = "com.example.android.fakeime";
+        si.name = "Fake IME";
         si.exported = true;
-        si.nonLocalizedLabel = "Dummy IME";
+        si.nonLocalizedLabel = "Fake IME";
         ri.serviceInfo = si;
         return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
     }
 
-    private static InputMethodSubtype createDummySubtype(
+    private static InputMethodSubtype createFakeSubtype(
             String mode, boolean isAuxiliary, boolean isAsciiCapable) {
         return new InputMethodSubtypeBuilder()
                 .setSubtypeNameResId(0)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 5171dda..97d8705 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -195,55 +195,55 @@
     public void isValidNonAuxAsciiCapableIme() {
         // IME w/ no subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false)))
+                createFakeIme(false)))
                 .isFalse();
 
         // IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false, createDummySubtype("keyboard", false, false))))
+                createFakeIme(false, createFakeSubtype("keyboard", false, false))))
                 .isFalse();
 
         // IME w/ non-Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false, createDummySubtype("keyboard", false, true))))
+                createFakeIme(false, createFakeSubtype("keyboard", false, true))))
                 .isTrue();
 
         // IME w/ Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(true, createDummySubtype("keyboard", true, true))))
+                createFakeIme(true, createFakeSubtype("keyboard", true, true))))
                 .isFalse();
 
         // IME w/ non-Aux and ASCII-capable "voice" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false, createDummySubtype("voice", false, true))))
+                createFakeIme(false, createFakeSubtype("voice", false, true))))
                 .isFalse();
 
         // IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false,
-                        createDummySubtype("keyboard", false, true),
-                        createDummySubtype("keyboard", false, false))))
+                createFakeIme(false,
+                        createFakeSubtype("keyboard", false, true),
+                        createFakeSubtype("keyboard", false, false))))
                 .isTrue();
    }
 
-    private static InputMethodInfo createDummyIme(boolean isAuxIme,
+    private static InputMethodInfo createFakeIme(boolean isAuxIme,
             InputMethodSubtype... subtypes) {
         final ResolveInfo ri = new ResolveInfo();
         final ServiceInfo si = new ServiceInfo();
         final ApplicationInfo ai = new ApplicationInfo();
-        ai.packageName = "com.example.android.dummyime";
+        ai.packageName = "com.example.android.fakeime";
         ai.enabled = true;
         si.applicationInfo = ai;
         si.enabled = true;
-        si.packageName = "com.example.android.dummyime";
-        si.name = "Dummy IME";
+        si.packageName = "com.example.android.fakeime";
+        si.name = "Fake IME";
         si.exported = true;
-        si.nonLocalizedLabel = "Dummy IME";
+        si.nonLocalizedLabel = "Fake IME";
         ri.serviceInfo = si;
         return new InputMethodInfo(ri, isAuxIme, "",  Arrays.asList(subtypes), 1, false);
     }
 
-    private static InputMethodSubtype createDummySubtype(
+    private static InputMethodSubtype createFakeSubtype(
             String mode, boolean isAuxiliary, boolean isAsciiCapable) {
         return new InputMethodSubtypeBuilder()
                 .setSubtypeNameResId(0)
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index c1543fd..bfd5b1cc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -81,6 +81,7 @@
         sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME);
         sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
         sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+        sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
     }
 
     private interface SettingsLookup {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index aa96087..319b44c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -320,19 +320,6 @@
     <!-- Permissions required for CTS test - AdbManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
 
-    <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
-    <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />
-    <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
-    <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
-    <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-    <uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" />
-    <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-    <uses-permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
-    <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-    <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" />
-    <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-    <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
-
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
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/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
index 176035f..1c680bb 100644
--- a/packages/SimAppDialog/Android.bp
+++ b/packages/SimAppDialog/Android.bp
@@ -7,7 +7,8 @@
 
     static_libs: [
         "androidx.legacy_legacy-support-v4",
-        "setup-wizard-lib",
+        "setupcompat",
+        "setupdesign",
     ],
 
     resource_dirs: ["res"],
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
index 873f6c5..e7368f3 100644
--- a/packages/SimAppDialog/AndroidManifest.xml
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -23,7 +23,7 @@
             android:name=".InstallCarrierAppActivity"
             android:exported="true"
             android:permission="android.permission.NETWORK_SETTINGS"
-            android:theme="@style/SuwThemeGlif.Light">
+            android:theme="@style/SudThemeGlif.Light">
         </activity>
     </application>
 </manifest>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 12f9bb6..68113db 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -14,18 +14,17 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.android.setupwizardlib.GlifLayout
+<com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:icon="@drawable/ic_signal_cellular_alt_rounded"
-    app:suwHeaderText="@string/install_carrier_app_title"
-    app:suwFooter="@layout/install_carrier_app_footer">
+    app:sucHeaderText="@string/install_carrier_app_title">
 
     <LinearLayout
-        style="@style/SuwContentFrame"
+        style="@style/SudContentFrame"
         android:id="@+id/content_frame"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -33,12 +32,12 @@
 
         <TextView
             android:id="@+id/install_carrier_app_description"
-            style="@style/SuwDescription.Glif"
+            style="@style/SudDescription.Glif"
             android:text="@string/install_carrier_app_description_default"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
 
-        <com.android.setupwizardlib.view.FillContentLayout
+        <com.google.android.setupdesign.view.FillContentLayout
             android:id="@+id/illo_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -47,12 +46,12 @@
 
             <ImageView
                 android:src="@drawable/illo_sim_app_dialog"
-                style="@style/SuwContentIllustration"
+                style="@style/SudContentIllustration"
                 android:contentDescription="@string/install_carrier_app_image_content_description"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"/>
 
-        </com.android.setupwizardlib.view.FillContentLayout>
-</LinearLayout>
+        </com.google.android.setupdesign.view.FillContentLayout>
+    </LinearLayout>
 
-</com.android.setupwizardlib.GlifLayout>
+</com.google.android.setupdesign.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
deleted file mode 100644
index 10dcb77..0000000
--- a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    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.
--->
-
-<com.android.setupwizardlib.view.ButtonBarLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/footer"
-    style="@style/SuwGlifButtonBar.Stackable"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <Button
-        android:id="@+id/skip_button"
-        style="@style/SuwGlifButton.Secondary"
-        android:text="@string/install_carrier_app_defer_action"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
-
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
-
-    <Button
-        android:id="@+id/download_button"
-        style="@style/SuwGlifButton.Primary"
-        android:text="@string/install_carrier_app_download_action"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
-</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/styles.xml b/packages/SimAppDialog/res/values/styles.xml
new file mode 100644
index 0000000..824e380
--- /dev/null
+++ b/packages/SimAppDialog/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?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>
+
+  <style name="SetupWizardPartnerResource">
+    <!-- Disable to use partner overlay theme for outside setupwizard flow. -->
+    <item name="sucUsePartnerResource">false</item>
+    <!-- Enable heavy theme style inside setupwizard flow. -->
+    <item name="sudUsePartnerHeavyTheme">true</item>
+  </style>
+
+</resources>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index abe82a8..0b6f9bb 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -17,14 +17,17 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.sysprop.SetupWizardProperties;
 import android.text.TextUtils;
 import android.view.View;
-import android.widget.Button;
 import android.widget.TextView;
 
-import com.android.setupwizardlib.util.WizardManagerHelper;
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.util.ThemeResolver;
 
 /**
  * Activity that gives a user the choice to download the SIM app or defer until a later time
@@ -35,7 +38,7 @@
  * Can display the carrier app name if its passed into the intent with key
  * {@link #BUNDLE_KEY_CARRIER_NAME}
  */
-public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
+public class InstallCarrierAppActivity extends Activity {
     /**
      * Key for the carrier app name that will be displayed as the app to download.  If unset, a
      * default description will be used
@@ -50,20 +53,33 @@
     protected void onCreate(Bundle icicle) {
         // Setup theme for aosp/pixel
         setTheme(
-                WizardManagerHelper.getThemeRes(
-                        SetupWizardProperties.theme().orElse(""),
-                        R.style.SuwThemeGlif_Light
-                )
-        );
+                new ThemeResolver.Builder()
+                        .setDefaultTheme(R.style.SudThemeGlifV3_Light)
+                        .build()
+                        .resolve(SetupWizardProperties.theme().orElse(""),
+                                /* suppressDayNight= */ false));
 
         super.onCreate(icicle);
         setContentView(R.layout.install_carrier_app_activity);
 
-        Button notNowButton = findViewById(R.id.skip_button);
-        notNowButton.setOnClickListener(this);
+        GlifLayout layout = findViewById(R.id.setup_wizard_layout);
+        FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
+        mixin.setSecondaryButton(
+                new FooterButton.Builder(this)
+                        .setText(R.string.install_carrier_app_defer_action)
+                        .setListener(this::onSkipButtonClick)
+                        .setButtonType(FooterButton.ButtonType.SKIP)
+                        .setTheme(R.style.SudGlifButton_Secondary)
+                        .build());
 
-        Button downloadButton = findViewById(R.id.download_button);
-        downloadButton.setOnClickListener(this);
+        mixin.setPrimaryButton(
+                new FooterButton.Builder(this)
+                        .setText(R.string.install_carrier_app_download_action)
+                        .setListener(this::onDownloadButtonClick)
+                        .setButtonType(FooterButton.ButtonType.OTHER)
+                        .setTheme(R.style.SudGlifButton_Primary)
+                        .build());
+
 
         // Show/hide illo depending on whether one was provided in a resource overlay
         boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo);
@@ -82,15 +98,17 @@
     }
 
     @Override
-    public void onClick(View v) {
-        switch (v.getId()) {
-            case R.id.skip_button:
-                finish(DEFER_RESULT);
-                break;
-            case R.id.download_button:
-                finish(DOWNLOAD_RESULT);
-                break;
-        }
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        theme.applyStyle(R.style.SetupWizardPartnerResource, true);
+        super.onApplyThemeResource(theme, resid, first);
+    }
+
+    protected void onSkipButtonClick(View view) {
+        finish(DEFER_RESULT);
+    }
+
+    protected void onDownloadButtonClick(View view) {
+        finish(DOWNLOAD_RESULT);
     }
 
     private void finish(int resultCode) {
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-product/values-in/strings.xml b/packages/SystemUI/res-product/values-in/strings.xml
index 2e0580f..1451e2c 100644
--- a/packages/SystemUI/res-product/values-in/strings.xml
+++ b/packages/SystemUI/res-product/values-in/strings.xml
@@ -26,10 +26,10 @@
     <string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Tidak ada kartu SIM dalam tablet."</string>
     <string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Tidak ada kartu SIM dalam ponsel."</string>
     <string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"Kode PIN tidak cocok"</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, tablet ini akan disetel ulang, sehingga semua datanya akan dihapus."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, ponsel ini akan disetel ulang, sehingga semua datanya akan dihapus."</string>
-    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Tablet ini akan disetel ulang, sehingga semua datanya akan dihapus."</string>
-    <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Ponsel ini akan disetel ulang, sehingga semua datanya akan dihapus."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, tablet ini akan direset, sehingga semua datanya akan dihapus."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, ponsel ini akan direset, sehingga semua datanya akan dihapus."</string>
+    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Tablet ini akan direset, sehingga semua datanya akan dihapus."</string>
+    <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Ponsel ini akan direset, sehingga semua datanya akan dihapus."</string>
     <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string>
     <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string>
     <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media.xml b/packages/SystemUI/res/drawable-television/ic_volume_media.xml
new file mode 100644
index 0000000..e43c4b4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-television/ic_volume_media.xml
@@ -0,0 +1,26 @@
+<?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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="@color/tv_volume_dialog_accent"
+        android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml
new file mode 100644
index 0000000..0f6dc95
--- /dev/null
+++ b/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml
@@ -0,0 +1,26 @@
+<?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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="@color/tv_volume_dialog_accent"
+        android:pathData="M3,15V9H7L12,4V20L7,15H3ZM14,7.97C15.48,8.71 16.5,10.23 16.5,12C16.5,13.77 15.48,15.29 14,16.02V7.97Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml
new file mode 100644
index 0000000..4b59e13
--- /dev/null
+++ b/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml
@@ -0,0 +1,27 @@
+<?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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="@color/tv_volume_dialog_accent"
+        android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_low.xml b/packages/SystemUI/res/drawable/ic_volume_media_low.xml
new file mode 100644
index 0000000..87591de
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_media_low.xml
@@ -0,0 +1,18 @@
+<!--
+     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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/ic_volume_media" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/tv_circle_dark.xml b/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
similarity index 75%
copy from packages/SystemUI/res/drawable/tv_circle_dark.xml
copy to packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
index d1ba8e7..93f8724 100644
--- a/packages/SystemUI/res/drawable/tv_circle_dark.xml
+++ b/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
@@ -16,9 +16,10 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
+       android:shape="rectangle">
 
-  <solid
-      android:color="@color/tv_audio_recording_indicator_background" />
+    <corners android:radius="20dp"/>
+    <solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
+    <stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
 
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_ring_white.xml b/packages/SystemUI/res/drawable/tv_ring_white.xml
deleted file mode 100644
index 0f7cc10..0000000
--- a/packages/SystemUI/res/drawable/tv_ring_white.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-
-  <stroke
-      android:width="1dp"
-      android:color="@android:color/white" />
-
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_circle_dark.xml b/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
similarity index 75%
rename from packages/SystemUI/res/drawable/tv_circle_dark.xml
rename to packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
index d1ba8e7..fee6e57 100644
--- a/packages/SystemUI/res/drawable/tv_circle_dark.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ 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.
@@ -16,9 +16,9 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
+    android:shape="rectangle">
 
-  <solid
-      android:color="@color/tv_audio_recording_indicator_background" />
+  <solid android:color="@color/tv_volume_dialog_background" />
+  <corners android:radius="@dimen/tv_volume_dialog_corner_radius"/>
 
-</shape>
\ No newline at end of file
+</shape>
diff --git a/packages/SystemUI/res/drawable/tv_circle_white_translucent.xml b/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
similarity index 84%
rename from packages/SystemUI/res/drawable/tv_circle_white_translucent.xml
rename to packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
index 55d21de..3c4fc05 100644
--- a/packages/SystemUI/res/drawable/tv_circle_white_translucent.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ 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.
@@ -17,8 +17,6 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval">
+  <solid android:color="@color/tv_volume_dialog_circle" />
 
-  <solid
-      android:color="@color/tv_audio_recording_indicator_pulse" />
-
-</shape>
\ No newline at end of file
+</shape>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
index e0d158d..56d847c 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
@@ -20,7 +20,7 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:background="@android:color/transparent"
-    android:theme="@style/qs_theme">
+    android:theme="@style/volume_dialog_theme">
 
     <FrameLayout
         android:id="@+id/volume_dialog"
@@ -46,7 +46,7 @@
             android:translationZ="@dimen/volume_dialog_elevation"
             android:clipChildren="false"
             android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full">
+            android:background="@drawable/tv_volume_dialog_background">
 
             <LinearLayout
                 android:id="@+id/volume_dialog_rows"
@@ -54,9 +54,7 @@
                 android:layout_height="wrap_content"
                 android:minWidth="@dimen/volume_dialog_panel_width"
                 android:gravity="center"
-                android:orientation="horizontal"
-                android:paddingRight="@dimen/volume_dialog_stream_padding"
-                android:paddingLeft="@dimen/volume_dialog_stream_padding">
+                android:orientation="horizontal">
                 <!-- volume rows added and removed here! :-) -->
             </LinearLayout>
 
@@ -98,4 +96,4 @@
 
     </FrameLayout>
 
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index 08209ab..c0f0aa8 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -21,11 +21,12 @@
     android:background="@android:color/transparent"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:theme="@style/qs_theme">
+    android:theme="@style/volume_dialog_theme">
 
     <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
+        android:padding="@dimen/tv_volume_dialog_row_padding"
         android:background="@android:color/transparent"
         android:gravity="center"
         android:layout_gravity="center"
@@ -33,9 +34,9 @@
         <com.android.keyguard.AlphaOptimizedImageButton
             android:id="@+id/volume_row_icon"
             style="@style/VolumeButtons"
-            android:layout_width="@dimen/volume_dialog_tap_target_size"
-            android:layout_height="@dimen/volume_dialog_tap_target_size"
-            android:background="@drawable/ripple_drawable_20dp"
+            android:layout_width="@dimen/tv_volume_dialog_bubble_size"
+            android:layout_height="@dimen/tv_volume_dialog_bubble_size"
+            android:background="@drawable/tv_volume_dialog_circle"
             android:tint="@color/accent_tint_color_selector"
             android:soundEffectsEnabled="false" />
         <TextView
@@ -62,6 +63,15 @@
                 android:layout_gravity="center"
                 android:rotation="0" />
         </FrameLayout>
+        <TextView
+            android:id="@+id/volume_number"
+            android:layout_width="@dimen/tv_volume_dialog_bubble_size"
+            android:layout_height="@dimen/tv_volume_dialog_bubble_size"
+            android:maxLength="2"
+            android:gravity="center"
+            android:background="@drawable/tv_volume_dialog_circle"
+            android:textSize="@dimen/tv_volume_number_text_size"
+            android:textColor="@color/accent_tint_color_selector"/>
     </LinearLayout>
 
     <include layout="@layout/volume_dnd_icon"/>
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 5da7819..c420117 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -22,7 +22,7 @@
     android:gravity="right"
     android:layout_gravity="right"
     android:background="@android:color/transparent"
-    android:theme="@style/qs_theme">
+    android:theme="@style/volume_dialog_theme">
 
     <FrameLayout
         android:id="@+id/volume_dialog"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 3c641af..ed870f8 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -210,8 +210,98 @@
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size" />
 
-    <!-- Buttons to remove this view when no longer needed -->
-    <include
-        layout="@layout/qs_media_panel_options"
-        android:visibility="gone" />
+    <!-- Constraints are set here as they are the same regardless of host -->
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:id="@+id/media_text"
+        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+        android:textColor="@color/media_primary_text"
+        android:text="@string/controls_media_title"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/remove_text"
+        app:layout_constraintVertical_chainStyle="spread_inside"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:id="@+id/remove_text"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        android:singleLine="true"
+        android:textColor="@color/media_primary_text"
+        android:text="@string/controls_media_close_session"
+        app:layout_constraintTop_toBottomOf="@id/media_text"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/settings"/>
+
+    <FrameLayout
+        android:id="@+id/settings"
+        android:background="@drawable/qs_media_light_source"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
+        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/remove_text">
+
+        <TextView
+            android:layout_gravity="bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textColor="@android:color/white"
+            android:text="@string/controls_media_settings_button" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/cancel"
+        android:background="@drawable/qs_media_light_source"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/dismiss" >
+
+        <TextView
+            android:layout_gravity="bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textColor="@android:color/white"
+            android:text="@string/cancel" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/dismiss"
+        android:background="@drawable/qs_media_light_source"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <TextView
+            android:layout_gravity="bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textColor="@android:color/white"
+            android:text="@string/controls_media_dismiss_button"
+        />
+    </FrameLayout>
 </com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml
deleted file mode 100644
index e72c0e8..0000000
--- a/packages/SystemUI/res/layout/qs_media_panel_options.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/qs_media_controls_options"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center"
-    android:padding="16dp"
-    android:orientation="vertical">
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:layout_weight="1"
-        android:minWidth="48dp"
-        android:layout_gravity="start|bottom"
-        android:gravity="bottom"
-        android:id="@+id/remove"
-        android:orientation="horizontal">
-        <ImageView
-            android:layout_width="18dp"
-            android:layout_height="18dp"
-            android:id="@+id/remove_icon"
-            android:layout_marginEnd="16dp"
-            android:tint="@color/media_primary_text"
-            android:src="@drawable/ic_clear"/>
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/remove_text"
-            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-            android:singleLine="true"
-            android:textColor="@color/media_primary_text"
-            android:text="@string/controls_media_close_session" />
-    </LinearLayout>
-    <TextView
-        android:id="@+id/cancel"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:layout_weight="1"
-        android:minWidth="48dp"
-        android:layout_gravity="end|bottom"
-        android:gravity="bottom"
-        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-        android:textColor="@android:color/white"
-        android:text="@string/cancel" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
index 1d2340d..f9336a54 100644
--- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
+++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
@@ -19,7 +19,7 @@
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:orientation="horizontal"
-              android:padding="32dp">
+              android:padding="12dp">
 
     <FrameLayout
         android:layout_width="wrap_content"
@@ -32,45 +32,25 @@
             android:orientation="horizontal">
 
             <FrameLayout
-                android:layout_width="45dp"
-                android:layout_height="47dp">
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent">
 
                 <View
                     android:id="@+id/icon_container_bg"
-                    android:layout_width="match_parent"
+                    android:layout_width="50dp"
                     android:layout_height="match_parent"
                     android:background="@drawable/tv_rect_dark_left_rounded"/>
 
                 <FrameLayout
                     android:id="@+id/icon_mic"
-                    android:layout_width="35dp"
-                    android:layout_height="35dp"
-                    android:layout_marginStart="6dp"
-                    android:layout_marginTop="6dp"
-                    android:layout_marginBottom="6dp">
-
-                    <View
-                        android:layout_width="27dp"
-                        android:layout_height="27dp"
-                        android:layout_gravity="center"
-                        android:background="@drawable/tv_circle_dark"/>
+                    android:layout_width="34dp"
+                    android:layout_height="24dp"
+                    android:layout_gravity="center"
+                    android:background="@drawable/tv_rect_shadow_rounded">
 
                     <ImageView
-                        android:id="@+id/pulsating_circle"
-                        android:layout_width="27dp"
-                        android:layout_height="27dp"
-                        android:layout_gravity="center"
-                        android:background="@drawable/tv_circle_white_translucent"/>
-
-                    <ImageView
-                        android:layout_width="27dp"
-                        android:layout_height="27dp"
-                        android:layout_gravity="center"
-                        android:src="@drawable/tv_ring_white"/>
-
-                    <ImageView
-                        android:layout_width="16dp"
-                        android:layout_height="16dp"
+                        android:layout_width="13dp"
+                        android:layout_height="13dp"
                         android:layout_gravity="center"
                         android:background="@drawable/tv_ic_mic_white"/>
                 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 7d6547b..a90b1eb 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -22,7 +22,7 @@
     android:gravity="right"
     android:layout_gravity="right"
     android:background="@android:color/transparent"
-    android:theme="@style/qs_theme">
+    android:theme="@style/volume_dialog_theme">
 
     <!-- right-aligned to be physically near volume button -->
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 6128da8..b9efc5b 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -20,7 +20,7 @@
     android:layout_width="@dimen/volume_dialog_panel_width"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:theme="@style/qs_theme">
+    android:theme="@style/volume_dialog_theme">
 
     <LinearLayout
         android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 308fd88..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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Hou aan/af-skakelaar in om nuwe kontroles te sien"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Voeg kontroles by"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Wysig kontroles"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Gebruik eenhandmodus"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Swiep van die onderkant van die skerm af op of tik enige plek bo die program om uit te gaan"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index f2bf577..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>
@@ -1084,4 +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>
+    <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-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 36280e7..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>
@@ -1108,4 +1109,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_description" msgid="7674850272340517174">"للخروج، مرِّر سريعًا من أسفل الشاشة إلى أعلاها أو انقر في أي مكان فوق التطبيق."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a4cc17b..3778df8 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"বেটাৰি সঞ্চয়কাৰী অন হৈ আছে"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"কাৰ্যদক্ষতা আৰু নেপথ্য ডেটা হ্ৰাস কৰে"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"বেটাৰি সঞ্চয়কাৰী অফ কৰক"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিংৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিঙৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"এই সুবিধাটো প্ৰদান কৰা সেৱাটোৱে আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিংৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ৰেকর্ডিং অথবা কাষ্টিং আৰম্ভ কৰিবনে?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ জৰিয়তে ৰেকর্ডিং অথবা কাষ্টিং আৰম্ভ কৰিবনে ?"</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,4 +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>
+    <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-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 9443ba8..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Yeni nizamlayıcıları görmək üçün yandırıb-söndürmə düyməsinə basıb saxlayın"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Nizamlayıcılar əlavə edin"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Nizamlayıcıları redaktə edin"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Bir əlli rejimdən istifadə edilir"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Çıxmaq üçün ekranın aşağısından yuxarıya doğru sürüşdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 5b8c445..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>
@@ -1090,4 +1091,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite dugme za uključivanje da biste videli nove kontrole"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Izmeni kontrole"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korišćenje režima jednom rukom"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Da biste izašli, prevucite nagore od dna ekrana ili dodirnite bilo gde iznad aplikacije"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 6ee51682..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>
@@ -1096,4 +1097,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_description" msgid="7674850272340517174">"Каб выйсці, правядзіце па экране пальцам знізу ўверх або націсніце ў любым месцы над праграмай"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index c7bb3d0..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>
@@ -1084,4 +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>
+    <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-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 42e7523a..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,4 +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>
+    <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 16a1aa6..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>
@@ -1090,4 +1091,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite dugme za uključivanje da vidite nove kontrole"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korištenje načina rada jednom rukom"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Da izađete, prevucite s dna ekrana prema gore ili dodirnite bilo gdje iznad aplikacije"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index fdcec98..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén premut el botó d\'engegada per veure controls nous"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Afegeix controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edita els controls"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"S\'està utilitzant el mode d\'una mà"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Per sortir, llisca cap amunt des de la part inferior de la pantalla o toca qualsevol lloc a sobre de l\'aplicació"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index ab3b4fb..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>
@@ -1096,4 +1097,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Nové ovládací prvky zobrazíte podržením vypínače"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Přidat ovládací prvky"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Upravit ovládací prvky"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Používáte režim jedné ruky"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Režim ukončíte, když přejedete prstem z dolní části obrazovky nahoru nebo klepnete kamkoli nad aplikaci"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0fc9b94..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold afbryderknappen nede for at se nye betjeningselementer"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Tilføj styring"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Rediger styring"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Brug af enhåndstilstand"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Du kan afslutte ved at stryge opad fra bunden af skærmen eller trykke et vilkårligt sted over appen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a6b137a..2410e47 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Aufnehmen oder Streamen mit der App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" starten?"</string>
@@ -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,4 +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>
+    <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 bdd19a1..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>
@@ -1084,4 +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>
+    <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-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 68a8d30..0e22b58 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index e9856af..acf087d 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 68a8d30..0e22b58 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 68a8d30..0e22b58 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index eacef26..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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎Hold Power button to see new controls‎‏‎‎‏‎"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎Add controls‎‏‎‎‏‎"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎Edit controls‎‏‎‎‏‎"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎Using one-handed mode‎‏‎‎‏‎"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‎To exit, swipe up from the bottom of the screen or tap anywhere above the app‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 94a464b..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén presionado el botón de encendido para ver los nuevos controles"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Agregar controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Cómo usar el Modo de una mano"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o presiona cualquier parte arriba de la app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 0e7d376e..f7ebbd0 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -502,10 +502,10 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Ahorro de batería activado"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduce el rendimiento y los datos en segundo plano"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactivar Ahorro de batería"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que se muestre en pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestre en pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que se muestre en la pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestre en la pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"¿Empezar a grabar o enviar contenido?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Quieres iniciar la grabación o el envío de contenido con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Iniciar grabación o el envío de contenido en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"No volver a mostrar"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionar"</string>
@@ -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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén pulsado el botón de encendido para ver los controles nuevos"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Añadir controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utilizar el modo una mano"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para salir, desliza dos dedos hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index fc9c73b..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Uute juhtelementide vaatamiseks hoidke all toitenuppu"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Lisa juhtelemente"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Muuda juhtelemente"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Ühekäerežiimi kasutamine"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Väljumiseks pühkige ekraani alaosast üles või puudutage ekraani rakenduse kohal"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 041beab..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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Eduki sakatuta etengailua kontrolatzeko aukera berriak ikusteko"</string>
     <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 gainaldea"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 05e076f..d39d0c3 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -742,7 +742,7 @@
     <string name="feedback_promoted" msgid="8075757485407091976">"سیستمْ این اعلان را ارتقا داده است."</string>
     <string name="feedback_demoted" msgid="5848066008939031913">"سیستمْ این اعلان را تنزل داده است."</string>
     <string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string>
-    <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاسگزاریم!"</string>
+    <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاس‌گذاریم!"</string>
     <string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string>
     <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string>
     <string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> بسته شد"</string>
@@ -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>
@@ -1084,4 +1084,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_description" msgid="7674850272340517174">"برای خارج شدن، از پایین صفحه‌نمایش تند به‌طرف بالا بکشید یا در هر جایی از بالای برنامه که می‌خواهید ضربه بزنید"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4fccaac..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Paina virtapainiketta pitkään nähdäksesi uudet säätimet"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Lisää säätimiä"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Muokkaa säätimiä"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Yhden käden moodin käyttö"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Poistu pyyhkäisemällä ylös näytön alareunasta tai napauttamalla sovelluksen yllä"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 4e2074c..9c418f7 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Économiseur de pile activé"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Réduire les performances et de fond"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver la fonction Économiseur de pile"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et l\'audio que vous faites jouer."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service offrant cette fonction aura accès à toute l\'information qui est visible sur votre écran ou sur ce qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Commencer à enregistrer ou à diffuser?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Maintenez enfoncé l\'interrupteur pour afficher les nouvelles commandes"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utiliser le mode Une main"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Pour quitter, balayez l\'écran du bas vers le haut, ou touchez n\'importe où sur l\'écran en haut de l\'application"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index fedec56..3e2f5fd 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Économiseur de batterie activé"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Limite les performances et les données en arrière-plan."</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver l\'économiseur de batterie"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Par exemple, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore vos contenus audio lus."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil pendant un enregistrement ou une diffusion de contenu. Il peut s\'agir de mots de passe, données de paiement, photos, messages ou encore contenus audio lus."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service qui fournit cette fonction aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Cela comprend, entre autres, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore les contenus audio que vous lisez."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Démarrer l\'enregistrement ou la diffusion ?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Démarrer l\'enregistrement ou la diffusion avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ?"</string>
@@ -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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Appuyez de manière prolongée sur le bouton Marche/Arrêt pour afficher les nouvelles commandes"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utiliser le mode une main"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Pour quitter, balayez l\'écran de bas en haut ou appuyez n\'importe où au-dessus de l\'application"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index c1b9024..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén premido o botón de acendido para ver os novos controis"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Engadir controis"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controis"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Como se usa o modo dunha soa man?"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para saír, pasa o dedo cara arriba desde a parte inferior da pantalla ou toca calquera lugar da zona situada encima da aplicación"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 9c2f717..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,4 +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>
+    <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 cc2faa2..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,4 +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_description" msgid="7674850272340517174">"इसे बंद करने के लिए, स्क्रीन के सबसे निचले हिस्से से ऊपर की ओर स्वाइप करें या ऐप्लिकेशन के आइकॉन के ऊपर कहीं भी टैप करें"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 90251995..4bebe79 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -505,10 +505,10 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Štednja baterije je uključena"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Smanjuje količinu rada i pozadinske podatke"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključite Štednju baterije"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkcionalnost imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Započeti snimanje ili emitiranje?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranja pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranje pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj ponovo"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
@@ -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>
@@ -1090,4 +1091,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite tipku za uključivanje/isključivanje za prikaz novih kontrola"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korištenje načina rada jednom rukom"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Za izlaz prijeđite prstom od dna zaslona prema gore ili dodirnite bio gdje iznad aplikacije"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 689d869..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Az új vezérlők megtekintéséhez tartsa nyomva a bekapcsológombot"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Vezérlők hozzáadása"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Vezérlők szerkesztése"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Egykezes mód használata"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"A kilépéshez csúsztasson felfelé a képernyő aljáról, vagy koppintson az alkalmazás felett a képernyő bármelyik részére"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index a574f26..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>
@@ -1084,4 +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>
+    <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-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 7cc1b89..fec4205 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -855,7 +855,7 @@
   </string-array>
     <string name="menu_ime" msgid="5677467548258017952">"Pengalih keyboard"</string>
     <string name="save" msgid="3392754183673848006">"Simpan"</string>
-    <string name="reset" msgid="8715144064608810383">"Setel ulang"</string>
+    <string name="reset" msgid="8715144064608810383">"Reset"</string>
     <string name="adjust_button_width" msgid="8313444823666482197">"Sesuaikan lebar tombol"</string>
     <string name="clipboard" msgid="8517342737534284617">"Papan klip"</string>
     <string name="accessibility_key" msgid="3471162841552818281">"Tombol navigasi khusus"</string>
@@ -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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Tahan Tombol daya untuk melihat kontrol baru"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Tambahkan kontrol"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit kontrol"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Menggunakan mode satu tangan"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Untuk keluar, geser layar dari bawah ke atas atau ketuk di mana saja di atas aplikasi"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 8b686b8..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Haltu aflrofanum inni til að sjá nýjar stýringar"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Bæta við stýringum"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Breyta stýringum"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Notkun stillingar fyrir eina hönd"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Til að loka skaltu strjúka upp frá neðri hluta skjásins eða ýta hvar sem er fyrir ofan forritið"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 35742fd..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Tieni premuto il tasto di accensione per visualizzare i nuovi controlli"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Aggiungi controlli"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifica controlli"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Usare la modalità one-hand"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Per uscire, scorri verso l\'alto dalla parte inferiore dello schermo oppure tocca un punto qualsiasi sopra l\'app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 1029bb2..e88c951 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -508,7 +508,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"תכונת החיסכון בסוללה פועלת"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"מפחית את הביצועים ונתונים ברקע"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"כיבוי תכונת החיסכון בסוללה"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"‏לאפליקציה <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"‏לאפליקציית <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל המידע הגלוי במסך שלך ולכל תוכן שמופעל במכשיר שלך בזמן הקלטה או העברה (casting). המידע הזה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"‏לשירות שמספק את הפונקציה הזו תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"‏להתחיל להקליט או להעביר (cast)?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"‏להתחיל להקליט או להעביר (cast) באמצעות <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -522,7 +522,7 @@
     <string name="notification_section_header_conversations" msgid="821834744538345661">"שיחות"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ניקוי כל ההתראות השקטות"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"התראות הושהו על ידי מצב \'נא לא להפריע\'"</string>
-    <string name="media_projection_action_text" msgid="3634906766918186440">"התחל כעת"</string>
+    <string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"אין התראות"</string>
     <string name="profile_owned_footer" msgid="2756770645766113964">"ייתכן שהפרופיל נתון למעקב"</string>
     <string name="vpn_footer" msgid="3457155078010607471">"ייתכן שהרשת נמצאת במעקב"</string>
@@ -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>
@@ -1096,4 +1097,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_description" msgid="7674850272340517174">"כדי לצאת, יש להחליק למעלה מתחתית המסך או להקיש במקום כלשהו במסך מעל האפליקציה"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index fedd9bb7..130f682 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -502,10 +502,10 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"バッテリー セーバー ON"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"パフォーマンスとバックグラウンドデータを制限します"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"バッテリー セーバーを OFF"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>は、記録中やキャスト中に画面上に表示またはデバイスから再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などの情報が含まれます。"</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"この機能を提供するサービスは、記録中やキャスト中に画面上に表示またはデバイスから再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などの情報が含まれます。"</string>
-    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"記録やキャストを開始しますか?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>で記録やキャストを開始しますか?"</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> は、録画中やキャスト中に画面に表示されたり、デバイスで再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などが含まれます。"</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"この機能を提供するサービスは、録画中やキャスト中に画面に表示されたり、デバイスで再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などが含まれます。"</string>
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"録画やキャストを開始しますか?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> で録画やキャストを開始しますか?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"次回から表示しない"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"すべて消去"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"管理"</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,4 +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>
+    <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-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 5c55112..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>
@@ -1084,4 +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>
+    <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-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 94c8ea3..36c726e 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -502,8 +502,8 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery saver қосулы"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Өнімділікті және фондық деректерді азайтады"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Battery saver функциясын өшіру"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экранда көрсетілетін немесе жазу не трансляциялау кезінде құрылғыда ойнатылған барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және ойнатылатын аудио сияқты ақпарат кіреді."</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Осы функцияны ұсынатын қызмет экранда көрсетілетін немесе жазу не трансляциялау кезінде құрылғыда ойнатылған барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және ойнатылатын аудио сияқты ақпарат кіреді."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> жазу не трансляциялау кезінде экранда көрсетілетін немесе дыбысталатын барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және аудиоматериалдар кіреді."</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Осы функцияны ұсынатын қызмет жазу не трансляциялау кезінде экранда көрсетілетін немесе құрылғыда дыбысталатын ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және аудиоматериалдар кіреді."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Жазу немесе трансляциялау басталсын ба?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> арқылы жазу немесе трансляциялау басталсын ба?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Қайта көрсетпеу"</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,4 +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>
+    <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-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 5ba460a..9d87c58 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -502,10 +502,10 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"កម្មវិធីសន្សំថ្មបានបើក"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"ការ​បន្ថយ​ការ​ប្រតិបត្តិ និង​ទិន្នន័យ​ផ្ទៃ​ខាងក្រោយ"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"បិទ​កម្មវិធី​សន្សំ​ថ្ម"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> នឹងមានសិទ្ធិ​ចូលប្រើ​ព័ត៌មាន​ទាំងអស់​ដែលអាច​មើលឃើញ​នៅលើ​អេក្រង់​របស់អ្នក ឬដែលចាក់​ពីឧបករណ៍​របស់អ្នក នៅពេល​កំពុង​ថត ឬបញ្ជូន។ ព័ត៌មាន​នេះមាន​ដូចជា ពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិត​អំពីការទូទាត់​ប្រាក់ រូបថត សារ និង​សំឡេង​ដែល​អ្នកចាក់​ជាដើម។"</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"សេវាកម្មដែល​ផ្ដល់​មុខងារ​នេះ​នឹងមាន​សិទ្ធិ​ចូលប្រើ​ព័ត៌មាន​ទាំងអស់​ដែល​អាច​មើលឃើញ​នៅលើ​អេក្រង់​របស់អ្នក ឬ​ដែលចាក់​ពីឧបករណ៍​របស់អ្នក នៅពេល​កំពុង​ថត ឬបញ្ជូន។ ព័ត៌មាន​នេះមាន​ដូចជា ពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិត​អំពីការទូទាត់​ប្រាក់ រូបថត សារ និង​សំឡេង​ដែល​អ្នកចាក់​ជាដើម។"</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> នឹងមានសិទ្ធិ​ចូលប្រើ​ព័ត៌មាន​ទាំងអស់​ដែលអាច​មើលឃើញ​នៅលើ​អេក្រង់​របស់អ្នក ឬដែលចាក់​ពីឧបករណ៍​របស់អ្នក នៅពេល​កំពុង​ថត ឬភ្ជាប់។ ព័ត៌មាន​នេះមាន​ដូចជា ពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិត​អំពីការទូទាត់​ប្រាក់ រូបថត សារ និង​សំឡេង​ដែល​អ្នកចាក់​ជាដើម។"</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"សេវាកម្មដែល​ផ្ដល់​មុខងារ​នេះ​នឹងមាន​សិទ្ធិ​ចូលប្រើ​ព័ត៌មាន​ទាំងអស់​ដែល​អាច​មើលឃើញ​នៅលើ​អេក្រង់​របស់អ្នក ឬ​ដែលចាក់​ពីឧបករណ៍​របស់អ្នក នៅពេល​កំពុង​ថត ឬភ្ជាប់។ ព័ត៌មាន​នេះមាន​ដូចជា ពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិត​អំពីការទូទាត់​ប្រាក់ រូបថត សារ និង​សំឡេង​ដែល​អ្នកចាក់​ជាដើម។"</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ចាប់ផ្ដើម​ថត ឬបញ្ជូន​មែនទេ?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"ចាប់ផ្ដើម​ថត ឬបញ្ជូន​ដោយប្រើ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"ចាប់ផ្ដើម​ថត ឬភ្ជាប់​ដោយប្រើ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"កុំ​បង្ហាញ​ម្ដងទៀត"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"សម្អាត​ទាំងអស់"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"គ្រប់គ្រង"</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,4 +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>
+    <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-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 7b8ed88..901f024 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -502,10 +502,10 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಆನ್ ಆಗಿದೆ"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"ಕಾರ್ಯಕ್ಷಮತೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಡೇಟಾವನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ಬ್ಯಾಟರಿ ಸೇವರ್‌ ಆಫ್ ಮಾಡಿ"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುವ ಅಥವಾ ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವಾಗ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತವೆ. ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಆಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್‌ನಂತಹ ಮಾಹಿತಿಯನ್ನು ಇದು ಒಳಗೊಂಡಿದೆ."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"ರೆಕಾರ್ಡ್ ಮಾಡುವಾಗ ಅಥವಾ ಬಿತ್ತರಿಸುವಾಗ ಸ್ಕ್ರೀನ್‌ ಮೇಲೆ ಕಾಣಿಸುವ ಸಕಲ ಮಾಹಿತಿಗೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಪ್ರವೇಶ ಹೊಂದಿರುತ್ತದೆ. ಇದು ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಆಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್‌ನಂತಹ ಮಾಹಿತಿಯನ್ನು ಕೂಡ ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"ಈ ವೈಶಿಷ್ಟ್ಯವು ಒದಗಿಸುವ ಸೇವೆಗಳು, ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುವ ಅಥವಾ ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವಾಗ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತವೆ. ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಆಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್‌ನಂತಹ ಮಾಹಿತಿಯನ್ನು ಇದು ಒಳಗೊಂಡಿದೆ."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವಿಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಬೇಕೆ?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಬಳಸಿಕೊಂಡು ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸುವುದೇ?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಮೂಲಕ ರೆಕಾರ್ಡಿಂಗ್, ಬಿತ್ತರಿಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸುವುದೇ?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"ಮತ್ತೊಮ್ಮೆ ತೋರಿಸದಿರು"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"ನಿರ್ವಹಿಸಿ"</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,4 +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>
+    <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-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 8d1177b..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>
@@ -1084,4 +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>
+    <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-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 794de98..3cf4126 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -502,10 +502,10 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Батареяны үнөмдөгүч режими күйүк"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Иштин майнаптуулугун начарлатып, фондук дайын-даректерди чектейт"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Батареяны үнөмдөгүчтү өчүрүү"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"Бул функцияны аткарган <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> кызматы экраныңызда көрүнүп турган бардык маалыматты же жаздыруу жана тышкы экранга чыгаруу учурунда түзмөгүңүздө ойнотулган маалыматты колдоно алат. Буга сырсөздөр, төлөмдүн чоо-жайы, сүрөттөр, билдирүүлөр жана ойнотулган аудио кирет."</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Бул функцияны аткарган кызматка экраныңыздагы бардык маалымат же түзмөктө ойнотулуп жаткан нерсе, сырсөздөр, төлөмдөрдүн чоо-жайы, сүрөттөр, билдирүүлөр жана аудио файлдар жеткиликтүү болот."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"Жаздырып же тышкы экранга чыгарып жатканда, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу экраныңыздагы бардык маалыматты же түзмөктө ойнолуп жаткан бардык нерселерди (сырсөздөрдү, төлөмдүн чоо-жайын, сүрөттөрдү, билдирүүлөрдү жана угуп жаткан аудиофайлдарды) көрө алат."</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Жаздырып же тышкы экранга чыгарып жатканда, бул колдонмо экраныңыздагы бардык маалыматты же түзмөктө ойнолуп жаткан бардык нерселерди (сырсөздөрдү, төлөмдүн чоо-жайын, сүрөттөрдү, билдирүүлөрдү жана угуп жаткан аудиофайлдарды) көрө алат."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Жаздырып же тышкы экранга чыгарып баштайсызбы?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> менен жаздырылып же тышкы экранга чыгарылып башталсынбы?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу аркылуу жаздырып же тышкы экранга чыгарып баштайсызбы?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Экинчи көрүнбөсүн"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Баарын тазалап салуу"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Башкаруу"</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,4 +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>
+    <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-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml
index 499341c..90fc652 100644
--- a/packages/SystemUI/res/values-land-television/dimens.xml
+++ b/packages/SystemUI/res/values-land-television/dimens.xml
@@ -17,5 +17,8 @@
 <resources>
   <!-- Width of volume bar -->
   <dimen name="volume_dialog_row_width">252dp</dimen>
-  <dimen name="volume_dialog_tap_target_size">36dp</dimen>
+  <dimen name="tv_volume_dialog_bubble_size">36dp</dimen>
+  <dimen name="tv_volume_dialog_corner_radius">40dp</dimen>
+  <dimen name="tv_volume_dialog_row_padding">5dp</dimen>
+  <dimen name="tv_volume_number_text_size">16dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 2fe3f80..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>
@@ -1084,4 +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>
+    <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-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 4489b7d..4e9b802 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -508,7 +508,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Akumuliatoriaus tausojimo priemonė įjungta"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Sumažinamas našumas ir foninių duomenų naudojimas"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Išjungti Akumuliatoriaus tausojimo priemonę"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"„<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Šią funkcija teikianti paslauga galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Pradėti įrašyti ar perduoti turinį?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Pradėti įrašyti ar perduoti turinį naudojant „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“?"</string>
@@ -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>
@@ -1096,4 +1097,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Jei norite peržiūrėti naujus valdiklius, laikykite paspaudę maitinimo mygtuką"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Pridėti valdiklių"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Redaguoti valdiklius"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Vienos rankos režimo naudojimas"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Jei norite išeiti, perbraukite aukštyn nuo ekrano apačios arba palieskite bet kur virš programos"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index d4826e4..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>
@@ -1090,4 +1091,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Nospiediet barošanas pogu un turiet to, lai skatītu jaunas vadīklas"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Pievienot vadīklas"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Rediģēt vadīklas"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Vienas rokas režīma izmantošana"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Lai izietu, velciet augšup no ekrāna apakšdaļas vai pieskarieties jebkurā vietā virs lietotnes"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 4836ecf..7e623ad 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -502,8 +502,8 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Штедачот на батерија е вклучен"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Ја намалува изведбата и податоците во заднина"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Исклучете го штедачот на батерија"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата што ја обезбедува функцијава ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе има пристап до сите податоци што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува податоци како лозинки, детали за плаќање, фотографии, пораки, аудио што го пуштате итн."</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата што ја обезбедува функцијава ќе има пристап до сите податоци што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како лозинки, детали за плаќање, фотографии, пораки, аудио што го пуштате итн."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Да почне снимање или емитување?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Да почне снимање или емитување со <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Не покажувај повторно"</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,4 +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>
+    <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-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 863ce11..35e6427 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"ബാറ്ററി ലാഭിക്കൽ ഓണാണ്"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"പ്രവർത്തനവും പശ്ചാത്തല ഡാറ്റയും കുറയ്‌ക്കുന്നു"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ബാറ്ററി ലാഭിക്കൽ ഓഫാക്കുക"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്‌റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ആക്‌സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഒഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, പാസ്‌വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്‌റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ന് ആക്‌സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഒഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, പാസ്‌വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്‌റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും ഈ ഫംഗ്‌ഷൻ ലഭ്യമാക്കുന്ന സേവനത്തിന് ആക്‌സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഓഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, പാസ്‌വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"റെക്കോർഡ് ചെയ്യൽ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യൽ ആരംഭിക്കണോ?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ഉപയോഗിച്ച് റെക്കോർഡ് ചെയ്യൽ അല്ലെങ്കിൽ കാസ്‌റ്റ് ചെയ്യൽ ആരംഭിക്കണോ?"</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,4 +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>
+    <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-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 107c310..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>
@@ -1084,4 +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>
+    <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-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index bd2c3a5..4ea965a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -504,8 +504,8 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"बॅटरी सेव्हर बंद करा"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"तुमच्या स्क्रीनवर दृश्यमान असलेल्या किंवा रेकॉर्ड किंवा कास्ट करताना तुमच्या डिव्हाइसमधून प्ले केलेल्या सर्व माहितीचा अ‍ॅक्सेस <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला असेल. यामध्ये पासवर्ड, पेमेंट तपशील, फोटो, मेसेज आणि तुम्ही प्ले केलेला ऑडिओ यासारख्या माहितीचा समावेश असतो."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"हे कार्य पुरवठा करणाऱ्या सेवेस तुमच्या स्क्रीनवर दृश्यमान असलेल्या किंवा रेकॉर्ड किंवा कास्ट करताना तुमच्या डिव्हाइसमधून प्ले केलेल्या सर्व माहितीचा अ‍ॅक्सेस असेल. यामध्ये पासवर्ड, पेमेंट तपशील, फोटो, मेसेज आणि तुम्ही प्ले केलेला ऑडिओ यासारख्या माहितीचा समावेश असतो."</string>
-    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकॉर्ड किंवा कास्ट करणे सुरू करायचे आहे का ?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ने रेकॉर्ड करणे किंवा कास्ट करणे सुरू करा?"</string>
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकॉर्ड करणे किंवा कास्ट करणे सुरू करायचे का ?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ने रेकॉर्ड करणे किंवा कास्ट करणे सुरू करायचे का?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"पुन्हा दर्शवू नका"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"सर्व साफ करा"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थापित करा"</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,4 +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>
+    <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-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 029571b..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Tahan butang Kuasa untuk melihat kawalan baharu"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Tambah kawalan"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit kawalan"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Menggunakan mod sebelah tangan"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Untuk keluar, leret ke atas daripada bahagian bawah skrin atau ketik pada mana-mana di bahagian atas apl"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 6e632af..150ed94e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -502,8 +502,8 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"ဘက်ထရီ အားထိန်းကို ဖွင့်ထားခြင်း"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"လုပ်ကိုင်မှုကို လျှော့ချလျက် နောက်ခံ ဒေတာကို ကန့်သတ်သည်"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ဘက်ထရီ အားထိန်းကို ပိတ်ရန်"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်မျက်နှာပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"ဤလုပ်ရပ်အတွက် ဝန်ဆောင်မှုသည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်မျက်နှာပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်ဖန်သားပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်နိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"ဤလုပ်ရပ်အတွက် ဝန်ဆောင်မှုသည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်ဖန်သားပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပြီး သင့်စက်မှ ဖွင့်နိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ဖမ်းယူခြင်း သို့မဟုတ် ကာစ်လုပ်ခြင်း စတင်မလား။"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> နှင့် ဖမ်းယူခြင်း သို့မဟုတ် ကာစ်လုပ်ခြင်း စတင်မလား။"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"နောက်ထပ် မပြပါနှင့်"</string>
@@ -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>
@@ -1084,4 +1084,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_description" msgid="7674850272340517174">"ထွက်ရန် ဖန်သားပြင်၏အောက်ခြေမှ အပေါ်သို့ပွတ်ဆွဲပါ သို့မဟုတ် အက်ပ်အပေါ်ဘက် မည်သည့်နေရာတွင်မဆို တို့ပါ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5e29ca4..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold inne av/på-knappen for å se kontroller"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Legg til kontroller"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Endre kontroller"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Bruk av enhåndsmodus"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"For å avslutte, sveip opp fra bunnen av skjermen eller trykk hvor som helst over appen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 098e0b1..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,4 +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>
+    <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 e501218..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Houd de aan/uit-knop ingedrukt om nieuwe bedieningselementen te bekijken"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Bedieningselementen toevoegen"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Bedieningselementen bewerken"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Bediening met één hand gebruiken"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index bfd4bd7..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,4 +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>
+    <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 1df9192..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,4 +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>
+    <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 56fcbb2..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>
@@ -1096,4 +1097,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Przytrzymaj przycisk zasilania, by zobaczyć nowe elementy sterujące"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj elementy sterujące"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edytuj elementy sterujące"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korzystanie z trybu jednej ręki"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Aby zamknąć, przesuń palcem z dołu ekranu w górę lub kliknij dowolne miejsce nad aplikacją"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 88f6032..715b0e4 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Economia de bateria ativada"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduz o desempenho e os dados em segundo plano"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e o áudio que você tocar."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudio."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha o botão liga/desliga pressionado para ver os novos controles"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Como usar o modo para uma mão"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 5fdb285..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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha premido o botão ligar/desligar para ver os novos controlos."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controlos"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controlos"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utilizar o modo para uma mão"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para sair, deslize rapidamente para cima a partir da parte inferior do ecrã ou toque em qualquer ponto acima da app."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 88f6032..715b0e4 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Economia de bateria ativada"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduz o desempenho e os dados em segundo plano"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e o áudio que você tocar."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudio."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
@@ -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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha o botão liga/desliga pressionado para ver os novos controles"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Como usar o modo para uma mão"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d5eecc7..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>
@@ -1090,4 +1091,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Apăsați butonul de alimentare pentru a vedea noile comenzi"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adăugați comenzi"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editați comenzile"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Folosirea modului cu o mână"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Pentru a ieși, glisați în sus din partea de jos a ecranului sau atingeți oriunde deasupra ferestrei aplicației"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 5085022..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>
@@ -1096,4 +1097,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_description" msgid="7674850272340517174">"Чтобы выйти, проведите по экрану снизу вверх или нажмите в любой области над значком приложения."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index ff7e816..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>
@@ -1084,4 +1084,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_description" msgid="7674850272340517174">"පිටවීමට, තිරයේ පහළ සිට ඉහළට ස්වයිප් කරන්න හෝ යෙදුමට ඉහළින් ඕනෑම තැනක තට්ටු කරන්න"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index a86da96..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>
@@ -1096,4 +1097,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Pridržaním vypínača zobrazíte nové ovládacie prvky"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Pridať ovládače"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Upraviť ovládače"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Používanie režimu jednej ruky"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ukončíte potiahnutím z dolnej časti obrazovky nahor alebo klepnutím kdekoľvek nad aplikáciu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index b9039d8..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>
@@ -1096,4 +1097,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Za ogled novih kontrolnikov pridržite gumb za vklop"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrolnike"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrolnike"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Uporaba enoročnega načina"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Za izhod povlecite z dna zaslona navzgor ali se dotaknite na poljubnem mestu nad aplikacijo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 2972c53..70245b8 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -502,8 +502,8 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"\"Kursyesi i baterisë\" është i aktivizuar"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Pakëson veprimtarinë dhe të dhënat në sfond"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Çaktivizo \"Kursyesin e baterisë\""</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione, si p.sh.: fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione, si p.sh.: fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Do të fillosh regjistrimin ose transmetimin?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Fillo regjistrimin ose transmetimin me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Mos e shfaq sërish"</string>
@@ -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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Mbaj shtypur butonin e energjisë për të parë kontrollet e reja"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Shto kontrollet"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifiko kontrollet"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Po përdor modalitetin e përdorimit me një dorë"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Për të dalë, rrëshqit lart nga fundi i ekranit ose trokit diku mbi aplikacion"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 77cbb96b..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>
@@ -1090,4 +1091,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_description" msgid="7674850272340517174">"Да бисте изашли, превуците нагоре од дна екрана или додирните било где изнад апликације"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c7eda45..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"De nya snabbkontrollerna visas om du håller strömbrytaren nedtryckt"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Lägg till snabbkontroller"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Redigera snabbkontroller"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Använda enhandsläge"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Avsluta genom att svepa uppåt från skärmens nederkant eller trycka ovanför appen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 2804f2f..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Shikilia kitufe cha kuwasha/kuzima ili uone vidhibiti vipya"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Weka vidhibiti"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Badilisha vidhibiti"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Kutumia hali ya kutumia kwa mkono mmoja"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ili ufunge, telezesha kidole juu kutoka sehemu ya chini ya skrini au uguse mahali popote juu ya programu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index ee176fa..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>
@@ -1084,4 +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>
+    <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-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index dc5ea1f..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>
@@ -1084,4 +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>
+    <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-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 1696aab..7b1479a 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -43,4 +43,7 @@
         <item>com.android.systemui.toast.ToastUI</item>
         <item>com.android.systemui.onehanded.OneHandedUI</item>
     </string-array>
+
+    <!-- Show a separate icon for low and high volume on the volume dialog -->
+    <bool name="config_showLowMediaVolumeIcon">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index b01c5d8..4cf7034a 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -22,4 +22,8 @@
         <item name="android:windowEnterAnimation">@null</item>
         <item name="android:windowExitAnimation">@null</item>
     </style>
+
+    <style name="volume_dialog_theme" parent="qs_theme">
+        <item name="android:colorAccent">@color/tv_volume_dialog_accent</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index ba9fd29..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>
@@ -1084,4 +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>
+    <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-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 069438f..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Pindutin nang matagal ang Power button para makita ang mga bagong kontrol"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Magdagdag ng mga kontrol"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Mag-edit ng mga kontrol"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Gamit ang one-hand mode"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para lumabas, mag-swipe pataas mula sa ibaba ng screen o mag-tap kahit saan sa itaas ng app"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 649b9af..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Yeni kontrolleri görmek için Güç düğmesini basılı tutun"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Denetim ekle"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Denetimleri düzenle"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Tek el modunu kullanma"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Çıkmak için ekranın alt kısmından yukarı kaydırın veya uygulamanın üzerinde herhangi bir yere dokunun"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 6cb3ac8..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>
@@ -1096,4 +1097,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_description" msgid="7674850272340517174">"Щоб вийти, проведіть пальцем вверх від низу екрана або торкніться екрана над додатком"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 804c334..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>
@@ -1084,4 +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>
+    <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-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 6193d33..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>
@@ -505,7 +505,7 @@
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ekranda chiqqan yoki yozib olish va translatsiya vaqtida ijro etilgan barcha axborotlarga ruxsat oladi. Bu axborotlar parollar, toʻlov tafsilotlari, rasmlar, xabarlar va ijro etilgan audiolardan iborat boʻlishi mumkin."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Bu funksiyani taʼminlovchi xizmat ekranda chiqqan yoki yozib olish va translatsiya vaqtida ijro etilgan barcha axborotlarga ruxsat oladi. Bu axborotlar parollar, toʻlov tafsilotlari, rasmlar, xabarlar va ijro etilgan audiolardan iborat boʻlishi mumkin."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Yozib olish yoki translatsiya boshlansinmi?"</string>
-    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bilan yozib olinsin yoki translatsiya qilinsinmi?"</string>
+    <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> orqali yozib olish yoki translatsiya boshlansinmi?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Boshqa ko‘rsatilmasin"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string>
     <string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</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>
@@ -1084,4 +1084,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Yangi boshqaruv elementlari bilan tanishish uchun quvvat tugmasini bosib turing"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Element kiritish"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Elementlarni tahrirlash"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Ixcham rejimdan foydalaning"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Chiqish uchun ekran pastidan tepaga suring yoki ilovaning tepasidagi istalgan joyga bosing."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 97ffbc6..a12a08d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -502,8 +502,8 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"Trình tiết kiệm pin đang bật"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Giảm hiệu suất và dữ liệu nền"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Tắt trình tiết kiệm pin"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát từ thiết bị trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Dịch vụ cung cấp chức năng này có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát từ thiết bị trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát trên thiết bị của bạn trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Dịch vụ cung cấp chức năng này có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát trên thiết bị của bạn trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Bắt đầu ghi âm/ghi hình hoặc truyền?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Bắt đầu ghi âm/ghi hình hoặc truyền bằng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Không hiển thị lại"</string>
@@ -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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Giữ nút Nguồn để xem các tùy chọn điều khiển mới"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Thêm các tùy chọn điều khiển"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Chỉnh sửa tùy chọn điều khiển"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Cách dùng chế độ một tay"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Để thoát, hãy vuốt lên từ cuối màn hình hoặc nhấn vào vị trí bất kỳ phía trên ứng dụng"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index b9b146c..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>
@@ -1084,4 +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>
+    <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-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f2cb185..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>
@@ -1084,4 +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>
+    <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-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 69b5294..e62c164 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -502,7 +502,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"省電模式已開啟"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"降低效能並限制背景數據傳輸"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"關閉省電模式"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"在錄製或投放內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"在錄製或投放內容時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"在錄製或投放內容時,提供這項功能的服務可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"要開始錄製或投放內容嗎?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」開始錄製或投放內容嗎?"</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,4 +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>
+    <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-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 5b899f9..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>
@@ -1084,4 +1085,6 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"Bamba Inkinobho yamandla ukuze ubone izilawuli ezintsha"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Engeza Izilawuli"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Hlela izilawuli"</string>
+    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Ukusebenzisa imodi yesandla esisodwa"</string>
+    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ukuze uphume, swayipha ngaphezulu kusuka ngezansi kwesikrini noma thepha noma kuphi ngenhla kohlelo lokusebenza"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 53cd971..cb49918 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -24,7 +24,11 @@
 
     <!-- Background color for audio recording indicator (G800) -->
     <color name="tv_audio_recording_indicator_background">#FF3C4043</color>
-    <color name="tv_audio_recording_indicator_pulse">#4DFFFFFF</color>
+    <color name="tv_audio_recording_indicator_icon_background">#CC000000</color>
+    <color name="tv_audio_recording_indicator_stroke">#33FFFFFF</color>
 
     <color name="red">#FFCC0000</color>
+    <color name="tv_volume_dialog_background">#E61F232B</color>
+    <color name="tv_volume_dialog_circle">#08FFFFFF</color>
+    <color name="tv_volume_dialog_accent">#FFDADCE0</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fa620df..17ba7c9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -515,13 +515,11 @@
     <!-- Whether or not to add a "people" notifications section -->
     <bool name="config_usePeopleFiltering">false</bool>
 
-    <!-- Defines the blacklist for system icons.  That is to say, the icons in the status bar that
-         are part of the blacklist are never displayed. Each item in the blacklist must be a string
-         defined in core/res/res/config.xml to properly blacklist the icon.
-
-         TODO: See if we can rename this config variable.
+    <!-- Defines system icons to be excluded from the display. That is to say, the icons in the
+         status bar that are part of this list are never displayed. Each item in the list must be a
+         string defined in core/res/res/config.xml to properly exclude the icon.
      -->
-    <string-array name="config_statusBarIconBlackList" translatable="false">
+    <string-array name="config_statusBarIconsToExclude" translatable="false">
         <item>@*android:string/status_bar_rotate</item>
         <item>@*android:string/status_bar_headset</item>
     </string-array>
@@ -576,4 +574,6 @@
     <!-- Animation duration for translating of one handed when trigger / dismiss. -->
     <integer name="config_one_handed_translate_animation_duration">150</integer>
 
+    <!-- Show a separate icon for low and high volume on the volume dialog -->
+    <bool name="config_showLowMediaVolumeIcon">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 67293c5..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">eeeMMMd</item>
+    <item type="string" name="system_ui_aod_date_pattern" translatable="false">EEEMMMd</item>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 77d3f45..823c1ff 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2799,7 +2799,7 @@
     <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] -->
     <string name="controls_media_close_session">Hide the current session.</string>
     <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
-    <string name="controls_media_dismiss_button">Hide</string>
+    <string name="controls_media_dismiss_button">Dismiss</string>
     <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
     <string name="controls_media_resume">Resume</string>
     <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9e5b94e..ee07e61 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -387,6 +387,9 @@
         <item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
     </style>
 
+    <!-- Overridden by values-television/styles.xml with tv-specific settings -->
+    <style name="volume_dialog_theme" parent="qs_theme"/>
+
     <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
         <item name="android:colorAccent">@color/remote_input_accent</item>
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java
index 4d968f1..73dc60d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java
@@ -26,6 +26,17 @@
 
     private static final String TAG = "WallpaperEngineCompat";
 
+    /**
+     * Returns true if {@link IWallpaperEngine#scalePreview(Rect)} is available.
+     */
+    public static boolean supportsScalePreview() {
+        try {
+            return IWallpaperEngine.class.getMethod("scalePreview", Rect.class) != null;
+        } catch (NoSuchMethodException | SecurityException e) {
+            return false;
+        }
+    }
+
     private final IWallpaperEngine mWrappedEngine;
 
     public WallpaperEngineCompat(IWallpaperEngine wrappedEngine) {
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/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 4df6660..6512624 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -281,9 +281,11 @@
      * @return {@code true} iff the app-op for should be shown to the user
      */
     private boolean isUserVisible(int appOpCode, int uid, String packageName) {
-        // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
-        // which may be user senstive, so for now always show it to the user.
-        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
+        // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION
+        // does not correspond to a platform permission
+        // which may be user sensitive, so for now always show it to the user.
+        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
+                || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
             return true;
         }
 
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/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 95bbea1..d4e6506b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.os.UserHandle;
 import android.text.InputType;
@@ -27,7 +28,9 @@
 import android.widget.TextView;
 
 import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.systemui.R;
 
 /**
@@ -104,18 +107,21 @@
                 return;
             }
 
+            // Request LockSettingsService to return the Gatekeeper Password in the
+            // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
+            // Gatekeeper Password and operationId.
             mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils,
-                    password, mOperationId, mEffectiveUserId, this::onCredentialVerified);
+                    password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW,
+                    this::onCredentialVerified);
         }
     }
 
     @Override
-    protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
-        super.onCredentialVerified(attestation, timeoutMs);
+    protected void onCredentialVerified(@NonNull VerifyCredentialResponse response,
+            int timeoutMs) {
+        super.onCredentialVerified(response, timeoutMs);
 
-        final boolean matched = attestation != null;
-
-        if (matched) {
+        if (response.isMatched()) {
             mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);
         } else {
             mPasswordField.setText("");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index 6d16f43..ad89ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.util.AttributeSet;
 
@@ -23,6 +24,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
 import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.systemui.R;
 
 import java.util.List;
@@ -61,22 +63,25 @@
 
             if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
                 // Pattern size is less than the minimum, do not count it as a failed attempt.
-                onPatternVerified(null /* attestation */, 0 /* timeoutMs */);
+                onPatternVerified(VerifyCredentialResponse.ERROR, 0 /* timeoutMs */);
                 return;
             }
 
             try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
+                // Request LockSettingsService to return the Gatekeeper Password in the
+                // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
+                // Gatekeeper Password and operationId.
                 mPendingLockCheck = LockPatternChecker.verifyCredential(
                         mLockPatternUtils,
                         credential,
-                        mOperationId,
                         mEffectiveUserId,
+                        LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW,
                         this::onPatternVerified);
             }
         }
 
-        private void onPatternVerified(byte[] attestation, int timeoutMs) {
-            AuthCredentialPatternView.this.onCredentialVerified(attestation, timeoutMs);
+        private void onPatternVerified(@NonNull VerifyCredentialResponse response, int timeoutMs) {
+            AuthCredentialPatternView.this.onCredentialVerified(response, timeoutMs);
             if (timeoutMs > 0) {
                 mLockPatternView.setEnabled(false);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 2695c69..a8f6f85 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.biometrics;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
@@ -38,12 +41,10 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
@@ -283,14 +284,18 @@
 
     protected void onErrorTimeoutFinish() {}
 
-    protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
-
-        final boolean matched = attestation != null;
-
-        if (matched) {
+    protected void onCredentialVerified(@NonNull VerifyCredentialResponse response, int timeoutMs) {
+        if (response.isMatched()) {
             mClearErrorRunnable.run();
             mLockPatternUtils.userPresent(mEffectiveUserId);
-            mCallback.onCredentialMatched(attestation);
+
+            // The response passed into this method contains the Gatekeeper Password. We still
+            // have to request Gatekeeper to create a Hardware Auth Token with the
+            // Gatekeeper Password and Challenge (keystore operationId in this case)
+            final VerifyCredentialResponse gkResponse = mLockPatternUtils.verifyGatekeeperPassword(
+                    response.getGatekeeperPw(), mOperationId, mEffectiveUserId);
+
+            mCallback.onCredentialMatched(gkResponse.getGatekeeperHAT());
         } else {
             if (timeoutMs > 0) {
                 mHandler.removeCallbacks(mClearErrorRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index d0f6181..fa0d2ba 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -310,7 +310,6 @@
         // Set ActivityView's alpha value as zero, since there is no view content to be shown.
         setContentVisibility(false);
 
-        mActivityViewContainer.setBackgroundColor(Color.WHITE);
         mActivityViewContainer.setOutlineProvider(new ViewOutlineProvider() {
             @Override
             public void getOutline(View view, Outline outline) {
@@ -439,9 +438,11 @@
     }
 
     void applyThemeAttrs() {
-        final TypedArray ta = mContext.obtainStyledAttributes(
-                new int[] {android.R.attr.dialogCornerRadius});
+        final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+                android.R.attr.dialogCornerRadius,
+                android.R.attr.colorBackgroundFloating});
         mCornerRadius = ta.getDimensionPixelSize(0, 0);
+        mActivityViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
         ta.recycle();
 
         if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
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/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index de53168..7f610d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -3,7 +3,6 @@
 import android.content.Context
 import android.content.Intent
 import android.content.res.Configuration
-import android.graphics.Color
 import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
 import android.util.Log
 import android.util.MathUtils
@@ -151,7 +150,7 @@
         pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator)
         mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator,
                 executor, mediaManager::onSwipeToDismiss, this::updatePageIndicatorLocation,
-                falsingManager)
+                this::closeGuts, falsingManager)
         isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
         inflateSettingsButton()
         mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
@@ -470,6 +469,12 @@
         }
     }
 
+    fun closeGuts() {
+        mediaPlayers.values.forEach {
+            it.closeGuts(true)
+        }
+    }
+
     /**
      * Update the size of the carousel, remeasuring it if necessary.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index 3096908..77cac50 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -56,6 +56,7 @@
     private val mainExecutor: DelayableExecutor,
     private val dismissCallback: () -> Unit,
     private var translationChangedListener: () -> Unit,
+    private val closeGuts: () -> Unit,
     private val falsingManager: FalsingManager
 ) {
     /**
@@ -452,6 +453,7 @@
         val nowScrolledIn = scrollIntoCurrentMedia != 0
         if (newIndex != activeMediaIndex || wasScrolledIn != nowScrolledIn) {
             activeMediaIndex = newIndex
+            closeGuts()
             updatePlayerVisibilities()
         }
         val relativeLocation = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 3fc162e..e55678dc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.media;
 
+import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -45,6 +47,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.util.animation.TransitionLayout;
 
 import java.util.List;
@@ -52,6 +55,8 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * A view controller used for Media Playback.
  */
@@ -59,6 +64,8 @@
     private static final String TAG = "MediaControlPanel";
     private static final float DISABLED_ALPHA = 0.38f;
 
+    private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
+
     // Button IDs for QS controls
     static final int[] ACTION_IDS = {
             R.id.action0,
@@ -78,6 +85,8 @@
     private MediaViewController mMediaViewController;
     private MediaSession.Token mToken;
     private MediaController mController;
+    private KeyguardDismissUtil mKeyguardDismissUtil;
+    private Lazy<MediaDataManager> mMediaDataManagerLazy;
     private int mBackgroundColor;
     private int mAlbumArtSize;
     private int mAlbumArtRadius;
@@ -93,12 +102,15 @@
     @Inject
     public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
             ActivityStarter activityStarter, MediaViewController mediaViewController,
-            SeekBarViewModel seekBarViewModel) {
+            SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
+            KeyguardDismissUtil keyguardDismissUtil) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
         mActivityStarter = activityStarter;
         mSeekBarViewModel = seekBarViewModel;
         mMediaViewController = mediaViewController;
+        mMediaDataManagerLazy = lazyMediaDataManager;
+        mKeyguardDismissUtil = keyguardDismissUtil;
         loadDimens();
 
         mViewOutlineProvider = new ViewOutlineProvider() {
@@ -174,6 +186,21 @@
         mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
         mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
         mMediaViewController.attach(player);
+
+        mViewHolder.getPlayer().setOnLongClickListener(v -> {
+            if (!mMediaViewController.isGutsVisible()) {
+                mMediaViewController.openGuts();
+                return true;
+            } else {
+                return false;
+            }
+        });
+        mViewHolder.getCancel().setOnClickListener(v -> {
+            closeGuts();
+        });
+        mViewHolder.getSettings().setOnClickListener(v -> {
+            mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
+        });
     }
 
     /**
@@ -205,6 +232,7 @@
         PendingIntent clickIntent = data.getClickIntent();
         if (clickIntent != null) {
             mViewHolder.getPlayer().setOnClickListener(v -> {
+                if (mMediaViewController.isGutsVisible()) return;
                 mActivityStarter.postStartActivityDismissingKeyguard(clickIntent);
             });
         }
@@ -329,14 +357,38 @@
         final MediaController controller = getController();
         mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
 
-        // Set up long press menu
-        // TODO: b/156036025 bring back media guts
+        // Dismiss
+        mViewHolder.getDismiss().setOnClickListener(v -> {
+            if (data.getNotificationKey() != null) {
+                closeGuts();
+                mKeyguardDismissUtil.executeWhenUnlocked(() -> {
+                    mMediaDataManagerLazy.get().dismissMediaData(data.getNotificationKey(),
+                            MediaViewController.GUTS_ANIMATION_DURATION + 100);
+                    return true;
+                }, /* requiresShadeOpen */ true);
+            } else {
+                Log.w(TAG, "Dismiss media with null notification. Token uid="
+                        + data.getToken().getUid());
+            }
+        });
 
         // TODO: We don't need to refresh this state constantly, only if the state actually changed
         // to something which might impact the measurement
         mMediaViewController.refreshState();
     }
 
+    /**
+     * Close the guts for this player.
+     * @param immediate {@code true} if it should be closed without animation
+     */
+    public void closeGuts(boolean immediate) {
+        mMediaViewController.closeGuts(immediate);
+    }
+
+    private void closeGuts() {
+        closeGuts(false);
+    }
+
     @UiThread
     private Drawable scaleDrawable(Icon icon) {
         if (icon == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index d82150f2..8a51c85 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
 import com.android.systemui.util.Utils
+import com.android.systemui.util.concurrency.DelayableExecutor
 import java.io.FileDescriptor
 import java.io.IOException
 import java.io.PrintWriter
@@ -90,7 +91,7 @@
 class MediaDataManager(
     private val context: Context,
     @Background private val backgroundExecutor: Executor,
-    @Main private val foregroundExecutor: Executor,
+    @Main private val foregroundExecutor: DelayableExecutor,
     private val mediaControllerFactory: MediaControllerFactory,
     private val broadcastDispatcher: BroadcastDispatcher,
     dumpManager: DumpManager,
@@ -107,7 +108,7 @@
     constructor(
         context: Context,
         @Background backgroundExecutor: Executor,
-        @Main foregroundExecutor: Executor,
+        @Main foregroundExecutor: DelayableExecutor,
         mediaControllerFactory: MediaControllerFactory,
         dumpManager: DumpManager,
         broadcastDispatcher: BroadcastDispatcher,
@@ -183,10 +184,7 @@
         val listenersCopy = listeners.toSet()
         val toRemove = mediaEntries.filter { it.value.packageName == packageName }
         toRemove.forEach {
-            mediaEntries.remove(it.key)
-            listenersCopy.forEach { listener ->
-                listener.onMediaDataRemoved(it.key)
-            }
+            removeEntry(it.key, listenersCopy)
         }
     }
 
@@ -269,6 +267,18 @@
         }
     }
 
+    private fun removeEntry(key: String, listenersCopy: Set<Listener>) {
+        mediaEntries.remove(key)
+        listenersCopy.forEach {
+            it.onMediaDataRemoved(key)
+        }
+    }
+
+    fun dismissMediaData(key: String, delay: Long) {
+        val listenersCopy = listeners.toSet()
+        foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay)
+    }
+
     private fun loadMediaDataInBgForResumption(
         userId: Int,
         desc: MediaDescription,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index fc33391..70f01d5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -293,6 +293,13 @@
         return viewHost
     }
 
+    /**
+     * Close the guts in all players in [MediaCarouselController].
+     */
+    fun closeGuts() {
+        mediaCarouselController.closeGuts()
+    }
+
     private fun createUniqueObjectHost(): UniqueObjectHostView {
         val viewHost = UniqueObjectHostView(context)
         viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 38817d7..92eeed4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -37,6 +37,11 @@
     private val mediaHostStatesManager: MediaHostStatesManager
 ) {
 
+    companion object {
+        @JvmField
+        val GUTS_ANIMATION_DURATION = 500L
+    }
+
     /**
      * A listener when the current dimensions of the player change
      */
@@ -169,6 +174,12 @@
      */
     val expandedLayout = ConstraintSet()
 
+    /**
+     * Whether the guts are visible for the associated player.
+     */
+    var isGutsVisible = false
+        private set
+
     init {
         collapsedLayout.load(context, R.xml.media_collapsed)
         expandedLayout.load(context, R.xml.media_expanded)
@@ -189,6 +200,37 @@
         configurationController.removeCallback(configurationListener)
     }
 
+    /**
+     * Show guts with an animated transition.
+     */
+    fun openGuts() {
+        if (isGutsVisible) return
+        isGutsVisible = true
+        animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L)
+        setCurrentState(currentStartLocation,
+                currentEndLocation,
+                currentTransitionProgress,
+                applyImmediately = false)
+    }
+
+    /**
+     * Close the guts for the associated player.
+     *
+     * @param immediate if `false`, it will animate the transition.
+     */
+    @JvmOverloads
+    fun closeGuts(immediate: Boolean = false) {
+        if (!isGutsVisible) return
+        isGutsVisible = false
+        if (!immediate) {
+            animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L)
+        }
+        setCurrentState(currentStartLocation,
+                currentEndLocation,
+                currentTransitionProgress,
+                applyImmediately = immediate)
+    }
+
     private fun ensureAllMeasurements() {
         val mediaStates = mediaHostStatesManager.mediaHostStates
         for (entry in mediaStates) {
@@ -203,6 +245,24 @@
             if (expansion > 0) expandedLayout else collapsedLayout
 
     /**
+     * Set the views to be showing/hidden based on the [isGutsVisible] for a given
+     * [TransitionViewState].
+     */
+    private fun setGutsViewState(viewState: TransitionViewState) {
+        PlayerViewHolder.controlsIds.forEach { id ->
+            viewState.widgetStates.get(id)?.let { state ->
+                // Make sure to use the unmodified state if guts are not visible
+                state.alpha = if (isGutsVisible) 0f else state.alpha
+                state.gone = if (isGutsVisible) true else state.gone
+            }
+        }
+        PlayerViewHolder.gutsIds.forEach { id ->
+            viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f
+            viewState.widgetStates.get(id)?.gone = !isGutsVisible
+        }
+    }
+
+    /**
      * Obtain a new viewState for a given media state. This usually returns a cached state, but if
      * it's not available, it will recreate one by measuring, which may be expensive.
      */
@@ -211,7 +271,7 @@
             return null
         }
         // Only a subset of the state is relevant to get a valid viewState. Let's get the cachekey
-        var cacheKey = getKey(state, tmpKey)
+        var cacheKey = getKey(state, isGutsVisible, tmpKey)
         val viewState = viewStates[cacheKey]
         if (viewState != null) {
             // we already have cached this measurement, let's continue
@@ -228,6 +288,7 @@
                         constraintSetForExpansion(state.expansion),
                         TransitionViewState())
 
+                setGutsViewState(result)
                 // We don't want to cache interpolated or null states as this could quickly fill up
                 // our cache. We only cache the start and the end states since the interpolation
                 // is cheap
@@ -252,11 +313,12 @@
         return result
     }
 
-    private fun getKey(state: MediaHostState, result: CacheKey): CacheKey {
+    private fun getKey(state: MediaHostState, guts: Boolean, result: CacheKey): CacheKey {
         result.apply {
             heightMeasureSpec = state.measurementInput?.heightMeasureSpec ?: 0
             widthMeasureSpec = state.measurementInput?.widthMeasureSpec ?: 0
             expansion = state.expansion
+            gutsVisible = guts
         }
         return result
     }
@@ -432,5 +494,6 @@
 private data class CacheKey(
     var widthMeasureSpec: Int = -1,
     var heightMeasureSpec: Int = -1,
-    var expansion: Float = 0.0f
+    var expansion: Float = 0.0f,
+    var gutsVisible: Boolean = false
 )
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 600fdc2..11551ac 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -23,6 +23,7 @@
 import android.widget.ImageView
 import android.widget.SeekBar
 import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintSet
 import com.android.systemui.R
 import com.android.systemui.util.animation.TransitionLayout
 
@@ -59,6 +60,11 @@
     val action3 = itemView.requireViewById<ImageButton>(R.id.action3)
     val action4 = itemView.requireViewById<ImageButton>(R.id.action4)
 
+    // Settings screen
+    val cancel = itemView.requireViewById<View>(R.id.cancel)
+    val dismiss = itemView.requireViewById<View>(R.id.dismiss)
+    val settings = itemView.requireViewById<View>(R.id.settings)
+
     init {
         (player.background as IlluminationDrawable).let {
             it.registerLightSource(seamless)
@@ -67,6 +73,9 @@
             it.registerLightSource(action2)
             it.registerLightSource(action3)
             it.registerLightSource(action4)
+            it.registerLightSource(cancel)
+            it.registerLightSource(dismiss)
+            it.registerLightSource(settings)
         }
     }
 
@@ -83,9 +92,6 @@
         }
     }
 
-    // Settings screen
-    val options = itemView.requireViewById<View>(R.id.qs_media_controls_options)
-
     companion object {
         /**
          * Creates a PlayerViewHolder.
@@ -105,5 +111,29 @@
                 progressTimes.layoutDirection = View.LAYOUT_DIRECTION_LTR
             }
         }
+
+        val controlsIds = setOf(
+                R.id.icon,
+                R.id.app_name,
+                R.id.album_art,
+                R.id.header_title,
+                R.id.header_artist,
+                R.id.media_seamless,
+                R.id.notification_media_progress_time,
+                R.id.media_progress_bar,
+                R.id.action0,
+                R.id.action1,
+                R.id.action2,
+                R.id.action3,
+                R.id.action4,
+                R.id.icon
+        )
+        val gutsIds = setOf(
+                R.id.media_text,
+                R.id.remove_text,
+                R.id.cancel,
+                R.id.dismiss,
+                R.id.settings
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
index ded3861..563684a 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
@@ -159,7 +159,7 @@
                     } else {
                         float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
                                 mLastPos.y - mDownPos.y);
-                        if (distance > mDragDistThreshold && mPassedSlop) {
+                        if (distance > mDragDistThreshold) {
                             mGestureEventCallback.onStop();
                         }
                     }
@@ -273,13 +273,13 @@
      */
     public interface OneHandedGestureEventCallback {
         /**
-         * Handle the start event event, and return whether the event was consumed.
+         * Handles the start gesture.
          */
-        boolean onStart();
+        void onStart();
 
         /**
-         * Handle the exit event event, and return whether the event was consumed.
+         * Handles the exit gesture.
          */
-        boolean onStop();
+        void onStop();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index 51e5875..a3921ee 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.KeyEvent;
 
 import androidx.annotation.NonNull;
 
@@ -33,6 +34,7 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 
@@ -50,9 +52,11 @@
     private static final String TAG = "OneHandedManager";
 
     private boolean mIsOneHandedEnabled;
+    private boolean mIsSwipeToNotificationEnabled;
     private boolean mTaskChangeToExit;
     private float mOffSetFraction;
 
+    private final CommandQueue mCommandQueue;
     private final DisplayController mDisplayController;
     private final OneHandedGestureHandler mGestureHandler;
     private final OneHandedTimeoutHandler mTimeoutHandler;
@@ -60,11 +64,7 @@
     private final OneHandedTutorialHandler mTutorialHandler;
     private final SysUiState mSysUiFlagContainer;
 
-    private Context mContext;
     private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
-    private OneHandedGestureHandler.OneHandedGestureEventCallback mGestureEventCallback;
-    private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback;
-    private OneHandedTransitionCallback mTransitionCallback;
 
     /**
      * Handler for system task stack changes, exit when user lunch new task or bring task to front
@@ -105,13 +105,14 @@
      */
     @Inject
     public OneHandedManagerImpl(Context context,
+            CommandQueue commandQueue,
             DisplayController displayController,
             OneHandedDisplayAreaOrganizer displayAreaOrganizer,
             OneHandedTouchHandler touchHandler,
             OneHandedTutorialHandler tutorialHandler,
             OneHandedGestureHandler gestureHandler,
             SysUiState sysUiState) {
-        mContext = context;
+        mCommandQueue = commandQueue;
         mDisplayAreaOrganizer = displayAreaOrganizer;
         mDisplayController = displayController;
         mDisplayController.addDisplayChangingController(mRotationController);
@@ -120,6 +121,8 @@
                 context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
         mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                 context.getContentResolver());
+        mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                context.getContentResolver());
         mTimeoutHandler = OneHandedTimeoutHandler.get();
         mTouchHandler = touchHandler;
         mTutorialHandler = tutorialHandler;
@@ -148,11 +151,19 @@
     }
 
     /**
-     * Start one handed mode
+     * Sets whether to enable swipe bottom to notification gesture when user update settings.
+     */
+    public void setSwipeToNotificationEnabled(boolean enabled) {
+        mIsSwipeToNotificationEnabled = enabled;
+        updateOneHandedEnabled();
+    }
+
+    /**
+     * Enters one handed mode.
      */
     @Override
     public void startOneHanded() {
-        if (!mDisplayAreaOrganizer.isInOneHanded() && mIsOneHandedEnabled) {
+        if (!mDisplayAreaOrganizer.isInOneHanded()) {
             final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
             mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
             mTimeoutHandler.resetTimer();
@@ -160,7 +171,7 @@
     }
 
     /**
-     * Stop one handed mode
+     * Exits one handed mode.
      */
     @Override
     public void stopOneHanded() {
@@ -171,53 +182,45 @@
     }
 
     private void setupGestures() {
-        mTouchEventCallback = new OneHandedTouchHandler.OneHandedTouchEventCallback() {
-            @Override
-            public boolean onStart() {
-                boolean result = false;
-                if (!mDisplayAreaOrganizer.isInOneHanded()) {
-                    startOneHanded();
-                    result = true;
-                }
-                return result;
-            }
+        mTouchHandler.registerTouchEventListener(
+                new OneHandedTouchHandler.OneHandedTouchEventCallback() {
+                    @Override
+                    public void onStart() {
+                        if (mIsOneHandedEnabled) {
+                            startOneHanded();
+                        }
+                    }
 
-            @Override
-            public boolean onStop() {
-                boolean result = false;
-                if (mDisplayAreaOrganizer.isInOneHanded()) {
-                    stopOneHanded();
-                    result = true;
-                }
-                return result;
-            }
-        };
-        mTouchHandler.registerTouchEventListener(mTouchEventCallback);
+                    @Override
+                    public void onStop() {
+                        if (mIsOneHandedEnabled) {
+                            stopOneHanded();
+                        }
+                    }
+                });
 
-        mGestureEventCallback = new OneHandedGestureHandler.OneHandedGestureEventCallback() {
-            @Override
-            public boolean onStart() {
-                boolean result = false;
-                if (!mDisplayAreaOrganizer.isInOneHanded()) {
-                    startOneHanded();
-                    result = true;
-                }
-                return result;
-            }
+        mGestureHandler.setGestureEventListener(
+                new OneHandedGestureHandler.OneHandedGestureEventCallback() {
+                    @Override
+                    public void onStart() {
+                        if (mIsOneHandedEnabled) {
+                            startOneHanded();
+                        } else if (mIsSwipeToNotificationEnabled) {
+                            mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
+                        }
+                    }
 
-            @Override
-            public boolean onStop() {
-                boolean result = false;
-                if (mDisplayAreaOrganizer.isInOneHanded()) {
-                    stopOneHanded();
-                    result = true;
-                }
-                return result;
-            }
-        };
-        mGestureHandler.setGestureEventListener(mGestureEventCallback);
+                    @Override
+                    public void onStop() {
+                        if (mIsOneHandedEnabled) {
+                            stopOneHanded();
+                        } else if (mIsSwipeToNotificationEnabled) {
+                            mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
+                        }
+                    }
+                });
 
-        mTransitionCallback = new OneHandedTransitionCallback() {
+        mDisplayAreaOrganizer.registerTransitionCallback(new OneHandedTransitionCallback() {
             @Override
             public void onStartFinished(Rect bounds) {
                 mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
@@ -229,8 +232,8 @@
                 mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
                         false).commitUpdate(DEFAULT_DISPLAY);
             }
-        };
-        mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallback);
+        });
+
         mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
         mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
         mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
@@ -264,7 +267,7 @@
             ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         }
         mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
-        mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled);
+        mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java
index 9b232cd..1b6ec04 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java
@@ -132,6 +132,14 @@
                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
     }
 
+    /**
+     * Returns whether swipe bottom to notification gesture enabled or not.
+     */
+    public static boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) {
+        return Settings.Secure.getInt(resolver,
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0) == 1;
+    }
+
     protected static void dump(PrintWriter pw, String prefix, ContentResolver resolver) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
index d616a3a..1446e5a 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
@@ -175,13 +175,13 @@
      */
     public interface OneHandedTouchEventCallback {
         /**
-         * Handle the start event event, and return whether the event was consumed.
+         * Handle the start event.
          */
-        boolean onStart();
+        void onStart();
 
         /**
-         * Handle the exit event event, and return whether the event was consumed.
+         * Handle the exit event.
          */
-        boolean onStop();
+        void onStop();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
index 8a67da5..b28730d0 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
@@ -16,11 +16,13 @@
 
 package com.android.systemui.onehanded;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.provider.Settings;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -48,12 +50,15 @@
 @Singleton
 public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Dumpable {
     private static final String TAG = "OneHandedTutorialHandler";
+    private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
     private final Rect mLastUpdatedBounds = new Rect();
     private final WindowManager mWindowManager;
 
     private View mTutorialView;
     private Point mDisplaySize = new Point();
     private Handler mUpdateHandler;
+    private ContentResolver mContentResolver;
+    private boolean mCanShowTutorial;
 
     /**
      * Container of the tutorial panel showing at outside region when one handed starting
@@ -71,6 +76,7 @@
     @Inject
     public OneHandedTutorialHandler(Context context) {
         context.getDisplay().getRealSize(mDisplaySize);
+        mContentResolver = context.getContentResolver();
         mUpdateHandler = new Handler();
         mWindowManager = context.getSystemService(WindowManager.class);
         mTargetViewContainer = new FrameLayout(context);
@@ -79,12 +85,20 @@
                 R.fraction.config_one_handed_offset, 1, 1));
         mTutorialView = LayoutInflater.from(context).inflate(R.xml.one_handed_tutorial, null);
         mTargetViewContainer.addView(mTutorialView);
-        createOrUpdateTutorialTarget();
+        mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
+                ? false : true;
+        if (mCanShowTutorial) {
+            createOrUpdateTutorialTarget();
+        }
     }
 
     @Override
     public void onStartFinished(Rect bounds) {
-        mUpdateHandler.post(() -> updateFinished(View.VISIBLE, 0f));
+        mUpdateHandler.post(() -> {
+            updateFinished(View.VISIBLE, 0f);
+            updateTutorialCount();
+        });
     }
 
     @Override
@@ -94,10 +108,23 @@
     }
 
     private void updateFinished(int visible, float finalPosition) {
+        if (!canShowTutorial()) {
+            return;
+        }
+
         mTargetViewContainer.setVisibility(visible);
         mTargetViewContainer.setTranslationY(finalPosition);
     }
 
+    private void updateTutorialCount() {
+        int showCount = Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0);
+        showCount = Math.min(MAX_TUTORIAL_SHOW_COUNT, showCount + 1);
+        mCanShowTutorial = showCount < MAX_TUTORIAL_SHOW_COUNT;
+        Settings.Secure.putInt(mContentResolver,
+                Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount);
+    }
+
     /**
      * Adds the tutorial target view to the WindowManager and update its layout, so it's ready
      * to be animated in.
@@ -153,7 +180,19 @@
         pw.println(mLastUpdatedBounds);
     }
 
+    private boolean canShowTutorial() {
+        if (!mCanShowTutorial) {
+            mTargetViewContainer.setVisibility(View.GONE);
+            return false;
+        }
+
+        return true;
+    }
+
     private void onAnimationUpdate(float value) {
+        if (!canShowTutorial()) {
+            return;
+        }
         mTargetViewContainer.setVisibility(View.VISIBLE);
         mTargetViewContainer.setTransitionGroup(true);
         mTargetViewContainer.setTranslationY(value - mTargetViewContainer.getHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
index 9239435..0903c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
@@ -40,7 +40,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.SystemUI;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.statusbar.CommandQueue;
 
@@ -80,7 +79,10 @@
                 mOneHandedManager.setOneHandedEnabled(enabled);
             }
 
-            setEnabledGesturalOverlay(enabled);
+            // Also checks swipe to notification settings since they all need gesture overlay.
+            setEnabledGesturalOverlay(
+                    enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                            mContext.getContentResolver()));
         }
     };
 
@@ -130,11 +132,28 @@
         }
     };
 
+    private final ContentObserver mSwipeToNotificationEnabledObserver =
+            new ContentObserver(mMainHandler) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    final boolean enabled =
+                            OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                                    mContext.getContentResolver());
+                    if (mOneHandedManager != null) {
+                        mOneHandedManager.setSwipeToNotificationEnabled(enabled);
+                    }
+
+                    // Also checks one handed mode settings since they all need gesture overlay.
+                    setEnabledGesturalOverlay(
+                            enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+                                    mContext.getContentResolver()));
+                }
+            };
+
     @Inject
     public OneHandedUI(Context context,
             CommandQueue commandQueue,
             OneHandedManagerImpl oneHandedManager,
-            DumpManager dumpManager,
             OneHandedSettingsUtil settingsUtil,
             ScreenLifecycle screenLifecycle) {
         super(context);
@@ -239,6 +258,9 @@
                 mContext.getContentResolver(), mTimeoutObserver);
         mSettingUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT,
                 mContext.getContentResolver(), mTaskChangeExitObserver);
+        mSettingUtil.registerSettingsKeyObserver(
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+                mContext.getContentResolver(), mSwipeToNotificationEnabledObserver);
     }
 
     private void updateSettings() {
@@ -248,6 +270,8 @@
                 mSettingUtil.getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
         mOneHandedManager.setTaskChangeToExit(
                 mSettingUtil.getSettingsTapsAppToExit(mContext.getContentResolver()));
+        mOneHandedManager.setSwipeToNotificationEnabled(
+                mSettingUtil.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 118e0bd..025341c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -99,6 +99,7 @@
     private final Handler mUpdateHandler;
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipAnimationController mPipAnimationController;
+    private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
     private final Rect mLastReportedBounds = new Rect();
     private final int mEnterExitAnimationDuration;
@@ -209,7 +210,8 @@
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
             @Nullable Divider divider,
             @NonNull DisplayController displayController,
-            @NonNull PipAnimationController pipAnimationController) {
+            @NonNull PipAnimationController pipAnimationController,
+            @NonNull PipUiEventLogger pipUiEventLogger) {
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
         mPipBoundsHandler = boundsHandler;
@@ -217,6 +219,7 @@
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
         mSurfaceTransactionHelper = surfaceTransactionHelper;
         mPipAnimationController = pipAnimationController;
+        mPipUiEventLoggerLogger = pipUiEventLogger;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
         mSplitDivider = divider;
         displayController.addDisplayWindowListener(this);
@@ -279,6 +282,8 @@
             return;
         }
 
+        mPipUiEventLoggerLogger.log(
+                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
         final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
         final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
                 != mPipBoundsHandler.getDisplayRotation();
@@ -381,6 +386,9 @@
         mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
 
+        mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
+        mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
+
         if (mShouldDeferEnteringPip) {
             if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
             // if deferred, hide the surface till fixed rotation is completed
@@ -514,6 +522,7 @@
         mPictureInPictureParams = null;
         mInPip = false;
         mExitingPip = false;
+        mPipUiEventLoggerLogger.setTaskInfo(null);
     }
 
     @Override
@@ -629,7 +638,7 @@
      * {@link PictureInPictureParams} would affect the bounds.
      */
     private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
-        final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals(
+        final boolean changed = (mPictureInPictureParams == null) || !Objects.equals(
                 mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational());
         if (changed) {
             mPictureInPictureParams = params;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
new file mode 100644
index 0000000..5e2cd9c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
@@ -0,0 +1,97 @@
+/*
+ * 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.pip;
+
+import android.app.TaskInfo;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+
+/**
+ * Helper class that ends PiP log to UiEvent, see also go/uievent
+ */
+@Singleton
+public class PipUiEventLogger {
+
+    private final UiEventLogger mUiEventLogger;
+
+    private TaskInfo mTaskInfo;
+
+    @Inject
+    public PipUiEventLogger(UiEventLogger uiEventLogger) {
+        mUiEventLogger = uiEventLogger;
+    }
+
+    public void setTaskInfo(TaskInfo taskInfo) {
+        mTaskInfo = taskInfo;
+    }
+
+    /**
+     * Sends log via UiEvent, reference go/uievent for how to debug locally
+     */
+    public void log(PipUiEventEnum event) {
+        if (mTaskInfo == null) {
+            return;
+        }
+        mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName());
+    }
+
+    /**
+     * Enums for logging the PiP events to UiEvent
+     */
+    public enum PipUiEventEnum implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Activity enters picture-in-picture mode")
+        PICTURE_IN_PICTURE_ENTER(603),
+
+        @UiEvent(doc = "Expands from picture-in-picture to fullscreen")
+        PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
+
+        @UiEvent(doc = "Removes picture-in-picture by tap close button")
+        PICTURE_IN_PICTURE_TAP_TO_REMOVE(605),
+
+        @UiEvent(doc = "Removes picture-in-picture by drag to dismiss area")
+        PICTURE_IN_PICTURE_DRAG_TO_REMOVE(606),
+
+        @UiEvent(doc = "Shows picture-in-picture menu")
+        PICTURE_IN_PICTURE_SHOW_MENU(607),
+
+        @UiEvent(doc = "Hides picture-in-picture menu")
+        PICTURE_IN_PICTURE_HIDE_MENU(608),
+
+        @UiEvent(doc = "Changes the aspect ratio of picture-in-picture window. This is inherited"
+                + " from previous Tron-based logging and currently not in use.")
+        PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609),
+
+        @UiEvent(doc = "User resize of the picture-in-picture window")
+        PICTURE_IN_PICTURE_RESIZE(610);
+
+        private final int mId;
+
+        PipUiEventEnum(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 7c5054c..9dfa864 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -47,6 +47,7 @@
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -259,7 +260,8 @@
             PipSnapAlgorithm pipSnapAlgorithm,
             PipTaskOrganizer pipTaskOrganizer,
             SysUiState sysUiState,
-            ConfigurationController configController) {
+            ConfigurationController configController,
+            PipUiEventLogger pipUiEventLogger) {
         mContext = context;
         mActivityManager = ActivityManager.getService();
 
@@ -280,7 +282,8 @@
                 mMediaController, mInputConsumerController);
         mTouchHandler = new PipTouchHandler(context, mActivityManager,
                 mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
-                floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState);
+                floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState,
+                pipUiEventLogger);
         mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
                 mTouchHandler.getMotionHelper());
         displayController.addDisplayChangingController(mRotationController);
@@ -394,12 +397,7 @@
      * Update the bounds used to save the re-entry size and snap fraction when exiting PIP.
      */
     public void updateReentryBounds(Rect bounds) {
-        // On phones, the expansion animation that happens on pip tap before restoring
-        // to fullscreen makes it so that the last reported bounds are the expanded
-        // bounds. We want to restore to the unexpanded bounds when re-entering pip,
-        // so we use the bounds before expansion (normal) instead of the reported
-        // bounds.
-        Rect reentryBounds = mTouchHandler.getNormalBounds();
+        final Rect reentryBounds = mTouchHandler.getUserResizeBounds();
         float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
         mPipBoundsHandler.applySnapFraction(reentryBounds, snapFraction);
         mReentryBounds.set(reentryBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index d884fa9..9c42f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -52,6 +52,7 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.wm.shell.R;
 
@@ -88,6 +89,7 @@
     private final Point mMaxSize = new Point();
     private final Point mMinSize = new Point();
     private final Rect mLastResizeBounds = new Rect();
+    private final Rect mUserResizeBounds = new Rect();
     private final Rect mLastDownBounds = new Rect();
     private final Rect mDragCornerSize = new Rect();
     private final Rect mTmpTopLeftCorner = new Rect();
@@ -109,13 +111,15 @@
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
     private PipTaskOrganizer mPipTaskOrganizer;
+    private PipUiEventLogger mPipUiEventLogger;
 
     private int mCtrlType;
 
     public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
             PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
             PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
-            Runnable updateMovementBoundsRunnable, SysUiState sysUiState) {
+            Runnable updateMovementBoundsRunnable, SysUiState sysUiState,
+            PipUiEventLogger pipUiEventLogger) {
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
@@ -125,6 +129,7 @@
         mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mSysUiState = sysUiState;
+        mPipUiEventLogger = pipUiEventLogger;
 
         context.getDisplay().getRealSize(mMaxSize);
         reloadResources();
@@ -181,6 +186,7 @@
 
     void onActivityUnpinned() {
         mIsAttached = false;
+        mUserResizeBounds.setEmpty();
         updateIsEnabled();
     }
 
@@ -329,6 +335,7 @@
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
                     if (!mLastResizeBounds.isEmpty()) {
+                        mUserResizeBounds.set(mLastResizeBounds);
                         mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
                                 (Rect bounds) -> {
                                     new Handler(Looper.getMainLooper()).post(() -> {
@@ -337,6 +344,8 @@
                                         resetState();
                                     });
                                 });
+                        mPipUiEventLogger.log(
+                                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
                     } else {
                         resetState();
                     }
@@ -351,6 +360,14 @@
         mThresholdCrossed = false;
     }
 
+    void setUserResizeBounds(Rect bounds) {
+        mUserResizeBounds.set(bounds);
+    }
+
+    Rect getUserResizeBounds() {
+        return mUserResizeBounds;
+    }
+
     void updateMaxSize(int maxX, int maxY) {
         mMaxSize.set(maxX, maxY);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 7fc2823..b20ea4e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -34,7 +34,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Size;
 import android.view.Gravity;
 import android.view.IPinnedStackController;
@@ -55,13 +54,13 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.systemui.R;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.DismissCircleView;
@@ -94,6 +93,8 @@
     private final WindowManager mWindowManager;
     private final IActivityManager mActivityManager;
     private final PipBoundsHandler mPipBoundsHandler;
+    private final PipUiEventLogger mPipUiEventLogger;
+
     private PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
 
@@ -132,9 +133,6 @@
 
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
-    // The current resized bounds, changed by user resize.
-    // This is used during expand/un-expand to save/restore the user's resized size.
-    @VisibleForTesting Rect mResizedBounds = new Rect();
 
     // The reference inset bounds, used to determine the dismiss fraction
     private Rect mInsetBounds = new Rect();
@@ -198,11 +196,7 @@
 
         @Override
         public void onPipDismiss() {
-            Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
-                    mActivityManager);
-            if (topPipActivity.first != null) {
-                MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity);
-            }
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
             mTouchState.removeDoubleTapTimeoutCallback();
             mMotionHelper.dismissPip();
         }
@@ -223,7 +217,8 @@
             FloatingContentCoordinator floatingContentCoordinator,
             DeviceConfigProxy deviceConfig,
             PipSnapAlgorithm pipSnapAlgorithm,
-            SysUiState sysUiState) {
+            SysUiState sysUiState,
+            PipUiEventLogger pipUiEventLogger) {
         // Initialize the Pip input consumer
         mContext = context;
         mActivityManager = activityManager;
@@ -238,7 +233,7 @@
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         deviceConfig, pipTaskOrganizer, this::getMovementBounds,
-                        this::updateMovementBounds, sysUiState);
+                        this::updateMovementBounds, sysUiState, pipUiEventLogger);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
@@ -259,6 +254,8 @@
                 pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu,
                 this::updateMovementBounds, mHandler);
 
+        mPipUiEventLogger = pipUiEventLogger;
+
         mTargetView = new DismissCircleView(context);
         mTargetViewContainer = new FrameLayout(context);
         mTargetViewContainer.setBackgroundDrawable(
@@ -307,11 +304,8 @@
                     hideDismissTarget();
                 });
 
-                Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
-                        mActivityManager);
-                if (topPipActivity.first != null) {
-                    MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, topPipActivity);
-                }
+                mPipUiEventLogger.log(
+                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
             }
         });
 
@@ -383,7 +377,6 @@
 
             mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         }
-        mResizedBounds.setEmpty();
         mPipResizeGestureHandler.onActivityUnpinned();
     }
 
@@ -393,9 +386,8 @@
         mMotionHelper.synchronizePinnedStackBounds();
         updateMovementBounds();
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
-            // updates mResizedBounds only if it's an entering PiP animation
-            // mResized should be otherwise updated in setMenuState.
-            mResizedBounds.set(mMotionHelper.getBounds());
+            // Set the initial bounds as the user resize bounds.
+            mPipResizeGestureHandler.setUserResizeBounds(mMotionHelper.getBounds());
         }
 
         if (mShowPipMenuOnAnimationEnd) {
@@ -808,9 +800,7 @@
             // Save the current snap fraction and if we do not drag or move the PiP, then
             // we store back to this snap fraction.  Otherwise, we'll reset the snap
             // fraction and snap to the closest edge.
-            // Also save the current resized bounds so when the menu disappears, we can restore it.
             if (resize) {
-                mResizedBounds.set(mMotionHelper.getBounds());
                 Rect expandedBounds = new Rect(mExpandedBounds);
                 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
                         mMovementBounds, mExpandedMovementBounds, callback);
@@ -839,7 +829,7 @@
                 }
 
                 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
-                    Rect restoreBounds = new Rect(mResizedBounds);
+                    Rect restoreBounds = new Rect(getUserResizeBounds());
                     Rect restoredMovementBounds = new Rect();
                     mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds,
                             restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
@@ -856,8 +846,10 @@
         // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
         // as well, or it can't handle a11y focus and pip menu can't perform any action.
         onRegistrationChanged(menuState == MENU_STATE_NONE);
-        if (menuState != MENU_STATE_CLOSE) {
-            MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
+        if (menuState == MENU_STATE_NONE) {
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_HIDE_MENU);
+        } else if (menuState == MENU_STATE_FULL) {
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_MENU);
         }
     }
 
@@ -890,6 +882,10 @@
         return mNormalBounds;
     }
 
+    Rect getUserResizeBounds() {
+        return mPipResizeGestureHandler.getUserResizeBounds();
+    }
+
     /**
      * Gesture controlling normal movement of the PIP.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index d5a14f7..affc5ee 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -45,7 +45,6 @@
 
 @Singleton
 class PrivacyItemController @Inject constructor(
-    context: Context,
     private val appOpsController: AppOpsController,
     @Main uiExecutor: DelayableExecutor,
     @Background private val bgExecutor: Executor,
@@ -57,16 +56,21 @@
 
     @VisibleForTesting
     internal companion object {
-        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO,
+        val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
+                AppOpsManager.OP_RECORD_AUDIO)
+        val OPS_LOCATION = intArrayOf(
                 AppOpsManager.OP_COARSE_LOCATION,
                 AppOpsManager.OP_FINE_LOCATION)
+        val OPS = OPS_MIC_CAMERA + OPS_LOCATION
         val intentFilter = IntentFilter().apply {
             addAction(Intent.ACTION_USER_SWITCHED)
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
         }
         const val TAG = "PrivacyItemController"
+        private const val ALL_INDICATORS =
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
     }
 
     @VisibleForTesting
@@ -74,9 +78,14 @@
         @Synchronized get() = field.toList() // Returns a shallow copy of the list
         @Synchronized set
 
-    private fun isPermissionsHubEnabled(): Boolean {
+    private fun isAllIndicatorsEnabled(): Boolean {
         return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+                ALL_INDICATORS, false)
+    }
+
+    private fun isMicCameraEnabled(): Boolean {
+        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                MIC_CAMERA, false)
     }
 
     private var currentUserIds = emptyList<Int>()
@@ -94,23 +103,28 @@
         uiExecutor.execute(notifyChanges)
     }
 
-    var indicatorsAvailable = isPermissionsHubEnabled()
+    var allIndicatorsAvailable = isAllIndicatorsEnabled()
         private set
-    @VisibleForTesting
-    internal val devicePropertiesChangedListener =
+    var micCameraAvailable = isMicCameraEnabled()
+        private set
+
+    private val devicePropertiesChangedListener =
             object : DeviceConfig.OnPropertiesChangedListener {
         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
             if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
-                    properties.getKeyset().contains(
-                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
-                val flag = properties.getBoolean(
-                        SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
-                if (indicatorsAvailable != flag) {
-                    // This is happening already in the UI executor, so we can iterate in the
-                    indicatorsAvailable = flag
-                    callbacks.forEach { it.get()?.onFlagChanged(flag) }
+                    (properties.keyset.contains(ALL_INDICATORS) ||
+                    properties.keyset.contains(MIC_CAMERA))) {
+
+                // Running on the ui executor so can iterate on callbacks
+                if (properties.keyset.contains(ALL_INDICATORS)) {
+                    allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false)
+                    callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
                 }
 
+                if (properties.keyset.contains(MIC_CAMERA)) {
+                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, false)
+                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
+                }
                 internalUiExecutor.updateListeningState()
             }
         }
@@ -123,6 +137,10 @@
             packageName: String,
             active: Boolean
         ) {
+            // Check if we care about this code right now
+            if (!allIndicatorsAvailable && code in OPS_LOCATION) {
+                return
+            }
             val userId = UserHandle.getUserId(uid)
             if (userId in currentUserIds) {
                 update(false)
@@ -166,13 +184,16 @@
     }
 
     /**
-     * Updates listening status based on whether there are callbacks and the indicators are enabled
+     * Updates listening status based on whether there are callbacks and the indicators are enabled.
+     *
+     * Always listen to all OPS so we don't have to figure out what we should be listening to. We
+     * still have to filter anyway. Updates are filtered in the callback.
      *
      * This is only called from private (add/remove)Callback and from the config listener, all in
      * main thread.
      */
     private fun setListeningState() {
-        val listen = !callbacks.isEmpty() and indicatorsAvailable
+        val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable)
         if (listening == listen) return
         listening = listen
         if (listening) {
@@ -233,14 +254,19 @@
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
+        if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null
         val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
         return PrivacyItem(type, app)
     }
 
     interface Callback {
         fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
+
         @JvmDefault
-        fun onFlagChanged(flag: Boolean) {}
+        fun onFlagAllChanged(flag: Boolean) {}
+
+        @JvmDefault
+        fun onFlagMicCameraChanged(flag: Boolean) {}
     }
 
     internal inner class Receiver : BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index f8655cc..52a2cec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -3,7 +3,9 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -44,6 +46,24 @@
     private int mPosition = -1;
     private boolean mAnimating;
 
+    private final Animatable2.AnimationCallback mAnimationCallback =
+            new Animatable2.AnimationCallback() {
+
+                @Override
+                public void onAnimationEnd(Drawable drawable) {
+                    super.onAnimationEnd(drawable);
+                    if (DEBUG) Log.d(TAG, "onAnimationEnd - queued: " + mQueuedPositions.size());
+                    if (drawable instanceof AnimatedVectorDrawable) {
+                        ((AnimatedVectorDrawable) drawable).unregisterAnimationCallback(
+                                mAnimationCallback);
+                    }
+                    mAnimating = false;
+                    if (mQueuedPositions.size() != 0) {
+                        setPosition(mQueuedPositions.remove(0));
+                    }
+                }
+            };
+
     public PageIndicator(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -197,10 +217,8 @@
         final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getContext().getDrawable(res);
         imageView.setImageDrawable(avd);
         avd.forceAnimationOnUI();
+        avd.registerAnimationCallback(mAnimationCallback);
         avd.start();
-        // TODO: Figure out how to user an AVD animation callback instead, which doesn't
-        // seem to be working right now...
-        postDelayed(mAnimationDone, ANIMATION_DURATION);
     }
 
     private int getTransition(boolean fromB, boolean isMajorAState, boolean isMajor) {
@@ -264,15 +282,4 @@
             getChildAt(i).layout(left, 0, mPageIndicatorWidth + left, mPageIndicatorHeight);
         }
     }
-
-    private final Runnable mAnimationDone = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG) Log.d(TAG, "onAnimationEnd - queued: " + mQueuedPositions.size());
-            mAnimating = false;
-            if (mQueuedPositions.size() != 0) {
-                setPosition(mQueuedPositions.remove(0));
-            }
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2dc82dd..2e258d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -151,7 +151,8 @@
     private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
     private RingerModeTracker mRingerModeTracker;
-    private boolean mPermissionsHubEnabled;
+    private boolean mAllIndicatorsEnabled;
+    private boolean mMicCameraIndicatorsEnabled;
 
     private PrivacyItemController mPrivacyItemController;
     private final UiEventLogger mUiEventLogger;
@@ -178,13 +179,26 @@
         }
 
         @Override
-        public void onFlagChanged(boolean flag) {
-            if (mPermissionsHubEnabled != flag) {
-                StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
-                iconContainer.setIgnoredSlots(getIgnoredIconSlots());
-                setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+        public void onFlagAllChanged(boolean flag) {
+            if (mAllIndicatorsEnabled != flag) {
+                mAllIndicatorsEnabled = flag;
+                update();
             }
         }
+
+        @Override
+        public void onFlagMicCameraChanged(boolean flag) {
+            if (mMicCameraIndicatorsEnabled != flag) {
+                mMicCameraIndicatorsEnabled = flag;
+                update();
+            }
+        }
+
+        private void update() {
+            StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
+            iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+            setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+        }
     };
 
     @Inject
@@ -267,7 +281,8 @@
         mRingerModeTextView.setSelected(true);
         mNextAlarmTextView.setSelected(true);
 
-        mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+        mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+        mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
     }
 
     public QuickQSPanel getHeaderQsPanel() {
@@ -276,13 +291,15 @@
 
     private List<String> getIgnoredIconSlots() {
         ArrayList<String> ignored = new ArrayList<>();
-        ignored.add(mContext.getResources().getString(
-                com.android.internal.R.string.status_bar_camera));
-        ignored.add(mContext.getResources().getString(
-                com.android.internal.R.string.status_bar_microphone));
-        if (mPermissionsHubEnabled) {
+        if (getChipEnabled()) {
             ignored.add(mContext.getResources().getString(
-                    com.android.internal.R.string.status_bar_location));
+                    com.android.internal.R.string.status_bar_camera));
+            ignored.add(mContext.getResources().getString(
+                    com.android.internal.R.string.status_bar_microphone));
+            if (mAllIndicatorsEnabled) {
+                ignored.add(mContext.getResources().getString(
+                        com.android.internal.R.string.status_bar_location));
+            }
         }
 
         return ignored;
@@ -300,7 +317,7 @@
     }
 
     private void setChipVisibility(boolean chipVisible) {
-        if (chipVisible && mPermissionsHubEnabled) {
+        if (chipVisible && getChipEnabled()) {
             mPrivacyChip.setVisibility(View.VISIBLE);
             // Makes sure that the chip is logged as viewed at most once each time QS is opened
             // mListening makes sure that the callback didn't return after the user closed QS
@@ -607,7 +624,8 @@
             mAlarmController.addCallback(this);
             mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
             // Get the most up to date info
-            mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+            mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+            mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
             mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenController.removeCallback(this);
@@ -747,4 +765,8 @@
             updateHeaderTextContainerAlphaAnimator();
         }
     }
+
+    private boolean getChipEnabled() {
+        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
+    }
 }
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 8c485a6..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() {
@@ -451,15 +453,24 @@
         if (listening) {
             if (mListeners.add(listener) && mListeners.size() == 1) {
                 if (DEBUG) Log.d(TAG, "handleSetListening true");
-                mLifecycle.setCurrentState(RESUMED);
                 handleSetListening(listening);
-                refreshState(); // Ensure we get at least one refresh after 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.
+                });
             }
         } else {
             if (mListeners.remove(listener) && mListeners.size() == 0) {
                 if (DEBUG) Log.d(TAG, "handleSetListening false");
-                mLifecycle.setCurrentState(STARTED);
                 handleSetListening(listening);
+                mUiHandler.post(() -> {
+                    // This tile has been destroyed, the state should not change anymore.
+                    if (mLifecycle.getCurrentState().equals(DESTROYED)) return;
+                    mLifecycle.setCurrentState(STARTED);
+                });
             }
         }
         updateIsFullQs();
@@ -486,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/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 3264429..d8548de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -67,7 +67,7 @@
     private static final String PATTERN_HOUR_MINUTE = "h:mm a";
     private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm";
 
-    private final ColorDisplayManager mManager;
+    private ColorDisplayManager mManager;
     private final LocationController mLocationController;
     private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
     private NightDisplayListener mListener;
@@ -126,6 +126,8 @@
             mListener.setCallback(null);
         }
 
+        mManager = getHost().getUserContext().getSystemService(ColorDisplayManager.class);
+
         // Make a new controller for the new user.
         mListener = mNightDisplayListenerBuilder.setUser(newUserId).build();
         if (mIsListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 07b841f..78975a47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -58,10 +58,9 @@
     public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
     private final Icon mIcon = ResourceIcon.get(
             com.android.internal.R.drawable.ic_qs_ui_mode_night);
-    private final UiModeManager mUiModeManager;
+    private UiModeManager mUiModeManager;
     private final BatteryController mBatteryController;
     private final LocationController mLocationController;
-
     @Inject
     public UiModeNightTile(
             QSHost host,
@@ -78,7 +77,7 @@
         super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
                 activityStarter, qsLogger);
         mBatteryController = batteryController;
-        mUiModeManager = mContext.getSystemService(UiModeManager.class);
+        mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
         mLocationController = locationController;
         configurationController.observe(getLifecycle(), this);
         batteryController.observe(getLifecycle(), this);
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/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 71e7883..1bea72a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -385,7 +385,7 @@
         if (stopTracking) {
             // TODO(brightnessfloat): change to use float value instead.
             MetricsLogger.action(mContext, metric,
-                    BrightnessSynchronizer.brightnessFloatToInt(mContext, valFloat));
+                    BrightnessSynchronizer.brightnessFloatToInt(valFloat));
 
         }
         setBrightness(valFloat);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 4007abb..d40b666 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -78,6 +78,22 @@
                             onUndockingTask();
                         }
                     }
+
+                    @Override
+                    public void onActivityForcedResizable(String packageName, int taskId,
+                            int reason) {
+                        mDividerController.onActivityForcedResizable(packageName, taskId, reason);
+                    }
+
+                    @Override
+                    public void onActivityDismissingDockedStack() {
+                        mDividerController.onActivityDismissingSplitScreen();
+                    }
+
+                    @Override
+                    public void onActivityLaunchOnSecondaryDisplayFailed() {
+                        mDividerController.onActivityLaunchOnSecondaryDisplayFailed();
+                    }
                 }
         );
     }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
index 81649f6..1ee8abb 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
@@ -50,8 +50,7 @@
 /**
  * Controls the docked stack divider.
  */
-public class DividerController implements DividerView.DividerCallbacks,
-        DisplayController.OnDisplaysChangedListener {
+public class DividerController implements DisplayController.OnDisplaysChangedListener {
     static final boolean DEBUG = false;
     private static final String TAG = "Divider";
 
@@ -257,8 +256,8 @@
         mView = (DividerView)
                 LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
         DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
-        mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
-                mImePositionProcessor, mWindowManagerProxy);
+        mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits,
+                mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
         mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
         mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
         final int size = dctx.getResources().getDimensionPixelSize(
@@ -397,7 +396,22 @@
         }
     }
 
-    /** Called when there's a task undocking.  */
+    /** Called when there's an activity forced resizable. */
+    public void onActivityForcedResizable(String packageName, int taskId, int reason) {
+        mForcedResizableController.activityForcedResizable(packageName, taskId, reason);
+    }
+
+    /** Called when there's an activity dismissing split screen. */
+    public void onActivityDismissingSplitScreen() {
+        mForcedResizableController.activityDismissingSplitScreen();
+    }
+
+    /** Called when there's an activity launch on secondary display failed. */
+    public void onActivityLaunchOnSecondaryDisplayFailed() {
+        mForcedResizableController.activityLaunchOnSecondaryDisplayFailed();
+    }
+
+    /** Called when there's a task undocking. */
     public void onUndockingTask() {
         if (mView != null) {
             mView.onUndockingTask();
@@ -426,17 +440,7 @@
         mForcedResizableController.onAppTransitionFinished();
     }
 
-    @Override
-    public void onDraggingStart() {
-        mForcedResizableController.onDraggingStart();
-    }
-
-    @Override
-    public void onDraggingEnd() {
-        mForcedResizableController.onDraggingEnd();
-    }
-
-    /** Dumps current status of Divider.*/
+    /** Dumps current status of Split Screen. */
     public void dump(PrintWriter pw) {
         pw.print("  mVisible="); pw.println(mVisible);
         pw.print("  mMinimized="); pw.println(mMinimized);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index f412cc0..ff8bab0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -28,15 +28,13 @@
 import android.widget.Toast;
 
 import com.android.systemui.R;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.function.Consumer;
 
 /**
  * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
  */
-public class ForcedResizableInfoActivityController {
+final class ForcedResizableInfoActivityController implements DividerView.DividerCallbacks {
 
     private static final String SELF_PACKAGE_NAME = "com.android.systemui";
 
@@ -47,12 +45,7 @@
     private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
     private boolean mDividerDragging;
 
-    private final Runnable mTimeoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            showPending();
-        }
-    };
+    private final Runnable mTimeoutRunnable = this::showPending;
 
     private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
         if (!exists) {
@@ -78,44 +71,28 @@
     public ForcedResizableInfoActivityController(Context context,
             DividerController dividerController) {
         mContext = context;
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(
-                new TaskStackChangeListener() {
-                    @Override
-                    public void onActivityForcedResizable(String packageName, int taskId,
-                            int reason) {
-                        activityForcedResizable(packageName, taskId, reason);
-                    }
-
-                    @Override
-                    public void onActivityDismissingDockedStack() {
-                        activityDismissingDockedStack();
-                    }
-
-                    @Override
-                    public void onActivityLaunchOnSecondaryDisplayFailed() {
-                        activityLaunchOnSecondaryDisplayFailed();
-                    }
-                });
         dividerController.registerInSplitScreenListener(mDockedStackExistsListener);
     }
 
-    public void onAppTransitionFinished() {
+    @Override
+    public void onDraggingStart() {
+        mDividerDragging = true;
+        mHandler.removeCallbacks(mTimeoutRunnable);
+    }
+
+    @Override
+    public void onDraggingEnd() {
+        mDividerDragging = false;
+        showPending();
+    }
+
+    void onAppTransitionFinished() {
         if (!mDividerDragging) {
             showPending();
         }
     }
 
-    void onDraggingStart() {
-        mDividerDragging = true;
-        mHandler.removeCallbacks(mTimeoutRunnable);
-    }
-
-    void onDraggingEnd() {
-        mDividerDragging = false;
-        showPending();
-    }
-
-    private void activityForcedResizable(String packageName, int taskId, int reason) {
+    void activityForcedResizable(String packageName, int taskId, int reason) {
         if (debounce(packageName)) {
             return;
         }
@@ -123,12 +100,12 @@
         postTimeout();
     }
 
-    private void activityDismissingDockedStack() {
+    void activityDismissingSplitScreen() {
         Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
                 Toast.LENGTH_SHORT).show();
     }
 
-    private void activityLaunchOnSecondaryDisplayFailed() {
+    void activityLaunchOnSecondaryDisplayFailed() {
         Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text,
                 Toast.LENGTH_SHORT).show();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d798692..36f8bcd 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;
@@ -50,12 +46,8 @@
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 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,7 +68,6 @@
     private int mIconAppearTopPadding;
     private float mHiddenShelfIconSize;
     private int mStatusBarHeight;
-    private int mStatusBarPaddingStart;
     private AmbientState mAmbientState;
     private NotificationStackScrollLayout mHostLayout;
     private int mMaxLayoutHeight;
@@ -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,19 +114,6 @@
         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) {
         mAmbientState = ambientState;
         mHostLayout = hostLayout;
@@ -150,7 +123,6 @@
         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();
@@ -315,9 +287,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;
                     }
@@ -999,10 +969,6 @@
         return mInteractive;
     }
 
-    public void setMaxShelfEnd(float maxShelfEnd) {
-        mMaxShelfEnd = maxShelfEnd;
-    }
-
     public void setAnimationsEnabled(boolean enabled) {
         mAnimationsEnabled = enabled;
         if (!enabled) {
@@ -1044,6 +1010,10 @@
         updateBackgroundColors();
     }
 
+    public void setController(NotificationShelfController notificationShelfController) {
+        mController = notificationShelfController;
+    }
+
     private class ShelfState extends ExpandableViewState {
         private float openedAmount;
         private boolean hasItemsInStableShelf;
@@ -1056,7 +1026,6 @@
             }
 
             super.applyToView(view);
-            setMaxShelfEnd(maxShelfEnd);
             setOpenedAmount(openedAmount);
             updateAppearance();
             setHasItemsInStableShelf(hasItemsInStableShelf);
@@ -1070,7 +1039,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..9f8f844
--- /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.NotificationStackScrollLayout;
+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,
+            NotificationStackScrollLayout notificationStackScrollLayout) {
+        mView.bind(ambientState, notificationStackScrollLayout);
+        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/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/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..57c1a16 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
@@ -113,6 +113,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;
@@ -5419,28 +5420,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, this);
         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;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
index 53b369c..eb47645 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
@@ -34,6 +34,7 @@
     private ContextButtonListener mListener;
     private ContextualButtonGroup mGroup;
 
+    protected final Context mLightContext;
     protected final @DrawableRes int mIconResId;
 
     /**
@@ -42,8 +43,10 @@
       * @param buttonResId the button view from xml layout
       * @param iconResId icon resource to be used
       */
-    public ContextualButton(@IdRes int buttonResId, @DrawableRes int iconResId) {
+    public ContextualButton(@IdRes int buttonResId, Context lightContext,
+            @DrawableRes int iconResId) {
         super(buttonResId);
+        mLightContext = lightContext;
         mIconResId = iconResId;
     }
 
@@ -117,17 +120,8 @@
     }
 
     protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
-        return KeyButtonDrawable.create(getContext().getApplicationContext(), lightIconColor,
-                darkIconColor, mIconResId, false /* shadow */, null /* ovalBackground */);
-    }
-
-    /**
-     * This context is from the view that could be stale after rotation or config change. To get
-     * correct resources use getApplicationContext() as well.
-     * @return current view context
-     */
-    protected Context getContext() {
-        return getCurrentView().getContext();
+        return KeyButtonDrawable.create(mLightContext, lightIconColor, darkIconColor, mIconResId,
+                false /* shadow */, null /* ovalBackground */);
     }
 
     public interface ContextButtonListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index 687f5f1..8a85f7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -30,6 +30,8 @@
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 
+import java.util.function.Consumer;
+
 /** Containing logic for the rotation button on the physical left bottom corner of the screen. */
 public class FloatingRotationButton implements RotationButton {
 
@@ -45,6 +47,7 @@
     private boolean mCanShow = true;
 
     private RotationButtonController mRotationButtonController;
+    private Consumer<Boolean> mVisibilityChangedCallback;
 
     FloatingRotationButton(Context context) {
         mContext = context;
@@ -67,6 +70,11 @@
     }
 
     @Override
+    public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
+        mVisibilityChangedCallback = visibilityChangedCallback;
+    }
+
+    @Override
     public View getCurrentView() {
         return mKeyButtonView;
     }
@@ -105,6 +113,16 @@
             mKeyButtonDrawable.resetAnimation();
             mKeyButtonDrawable.startAnimation();
         }
+        mKeyButtonView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5,
+                    int i6, int i7) {
+                if (mIsShowing && mVisibilityChangedCallback != null) {
+                    mVisibilityChangedCallback.accept(true);
+                }
+                mKeyButtonView.removeOnLayoutChangeListener(this);
+            }
+        });
         return true;
     }
 
@@ -115,6 +133,9 @@
         }
         mWindowManager.removeViewImmediate(mKeyButtonView);
         mIsShowing = false;
+        if (mVisibilityChangedCallback != null) {
+            mVisibilityChangedCallback.accept(false);
+        }
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 62970525..fd653b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -120,7 +120,6 @@
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
-import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -132,7 +131,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
@@ -194,6 +192,7 @@
     private int mLayoutDirection;
 
     private boolean mForceNavBarHandleOpaque;
+    private boolean mIsCurrentUserSetup;
 
     /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
     private @Appearance int mAppearance;
@@ -313,6 +312,10 @@
 
         @Override
         public void onNavBarButtonAlphaChanged(float alpha, boolean animate) {
+            if (!mIsCurrentUserSetup) {
+                // If the current user is not yet setup, then don't update any button alphas
+                return;
+            }
             ButtonDispatcher buttonDispatcher = null;
             boolean forceVisible = false;
             if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
@@ -351,15 +354,6 @@
                 }
             };
 
-    private final ContextButtonListener mRotationButtonListener = (button, visible) -> {
-        if (visible) {
-            // If the button will actually become visible and the navbar is about to hide,
-            // tell the statusbar to keep it around for longer
-            mAutoHideController.touchAutoHide();
-            mNavigationBarView.notifyActiveTouchRegions();
-        }
-    };
-
     private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
 
     private final ContentObserver mAssistContentObserver = new ContentObserver(
@@ -375,6 +369,34 @@
         }
     };
 
+    private static class NavBarViewAttachedListener implements View.OnAttachStateChangeListener {
+        private NavigationBarFragment mFragment;
+        private FragmentListener mListener;
+
+        NavBarViewAttachedListener(NavigationBarFragment fragment, FragmentListener listener) {
+            mFragment = fragment;
+            mListener = listener;
+        }
+
+        @Override
+        public void onViewAttachedToWindow(View v) {
+            final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+            fragmentHost.getFragmentManager().beginTransaction()
+                    .replace(R.id.navigation_bar_frame, mFragment, TAG)
+                    .commit();
+            fragmentHost.addTagListener(TAG, mListener);
+            mFragment = null;
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+            fragmentHost.removeTagListener(TAG, mListener);
+            FragmentHostManager.removeAndDestroy(v);
+            v.removeOnAttachStateChangeListener(this);
+        }
+    }
+
     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
             new DeviceConfig.OnPropertiesChangedListener() {
         @Override
@@ -386,6 +408,14 @@
         }
     };
 
+    private final DeviceProvisionedController.DeviceProvisionedListener mUserSetupListener =
+            new DeviceProvisionedController.DeviceProvisionedListener() {
+        @Override
+        public void onUserSetupChanged() {
+            mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+        }
+    };
+
     @Inject
     public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
             DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
@@ -453,6 +483,9 @@
                 /* defaultValue = */ true);
         DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
+
+        mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+        mDeviceProvisionedController.addCallback(mUserSetupListener);
     }
 
     @Override
@@ -461,6 +494,7 @@
         mNavigationModeController.removeListener(this);
         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
+        mDeviceProvisionedController.removeCallback(mUserSetupListener);
 
         DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
     }
@@ -507,8 +541,6 @@
 
         // Currently there is no accelerometer sensor on non-default display.
         if (mIsOnDefaultDisplay) {
-            mNavigationBarView.getRotateSuggestionButton().setListener(mRotationButtonListener);
-
             final RotationButtonController rotationButtonController =
                     mNavigationBarView.getRotationButtonController();
             rotationButtonController.addRotationCallback(mRotationWatcher);
@@ -1312,6 +1344,7 @@
         if (mAutoHideController != null) {
             mAutoHideController.setNavigationBar(mAutoHideUiElement);
         }
+        mNavigationBarView.setAutoHideController(autoHideController);
     }
 
     private boolean isTransientShown() {
@@ -1470,26 +1503,10 @@
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
         if (navigationBarView == null) return null;
 
-        navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-            @Override
-            public void onViewAttachedToWindow(View v) {
-                final NavigationBarFragment fragment =
-                        FragmentHostManager.get(v).create(NavigationBarFragment.class);
-                final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
-                fragmentHost.getFragmentManager().beginTransaction()
-                        .replace(R.id.navigation_bar_frame, fragment, TAG)
-                        .commit();
-                fragmentHost.addTagListener(TAG, listener);
-            }
-
-            @Override
-            public void onViewDetachedFromWindow(View v) {
-                final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
-                fragmentHost.removeTagListener(TAG, listener);
-                FragmentHostManager.removeAndDestroy(v);
-                navigationBarView.removeOnAttachStateChangeListener(this);
-            }
-        });
+        NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
+                .create(NavigationBarFragment.class);
+        navigationBarView.addOnAttachStateChangeListener(new NavBarViewAttachedListener(fragment,
+                listener));
         context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
         return navigationBarView;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 84512ac..e3cb105 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -131,6 +131,7 @@
     private boolean mDeadZoneConsuming = false;
     private final NavigationBarTransitions mBarTransitions;
     private final OverviewProxyService mOverviewProxyService;
+    private AutoHideController mAutoHideController;
 
     // performs manual animation in sync with layout transitions
     private final NavTransitionListener mTransitionListener = new NavTransitionListener();
@@ -276,6 +277,15 @@
         info.touchableRegion.setEmpty();
     };
 
+    private final Consumer<Boolean> mRotationButtonListener = (visible) -> {
+        if (visible) {
+            // If the button will actually become visible and the navbar is about to hide,
+            // tell the statusbar to keep it around for longer
+            mAutoHideController.touchAutoHide();
+        }
+        notifyActiveTouchRegions();
+    };
+
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -295,11 +305,12 @@
         // Set up the context group of buttons
         mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
         final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
-                R.drawable.ic_ime_switcher_default);
+                mLightContext, R.drawable.ic_ime_switcher_default);
         final RotationContextButton rotateSuggestionButton = new RotationContextButton(
-                R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button_ccw_start_0);
+                R.id.rotate_suggestion, mLightContext,
+                R.drawable.ic_sysbar_rotate_button_ccw_start_0);
         final ContextualButton accessibilityButton =
-                new ContextualButton(R.id.accessibility_button,
+                new ContextualButton(R.id.accessibility_button, mLightContext,
                         R.drawable.ic_sysbar_accessibility_button);
         mContextualButtonGroup.addButton(imeSwitcherButton);
         if (!isGesturalMode) {
@@ -312,7 +323,8 @@
         mFloatingRotationButton = new FloatingRotationButton(context);
         mRotationButtonController = new RotationButtonController(mLightContext,
                 mLightIconColor, mDarkIconColor,
-                isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton);
+                isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton,
+                mRotationButtonListener);
 
         mConfiguration = new Configuration();
         mTmpLastConfiguration = new Configuration();
@@ -360,6 +372,10 @@
                 });
     }
 
+    public void setAutoHideController(AutoHideController autoHideController) {
+        mAutoHideController = autoHideController;
+    }
+
     public NavigationBarTransitions getBarTransitions() {
         return mBarTransitions;
     }
@@ -938,7 +954,15 @@
         updateButtonLocation(getBackButton());
         updateButtonLocation(getHomeButton());
         updateButtonLocation(getRecentsButton());
-        updateButtonLocation(getRotateSuggestionButton());
+        updateButtonLocation(getImeSwitchButton());
+        updateButtonLocation(getAccessibilityButton());
+        if (mFloatingRotationButton.isVisible()) {
+            View floatingRotationView = mFloatingRotationButton.getCurrentView();
+            floatingRotationView.getBoundsOnScreen(mTmpBounds);
+            mActiveRegion.op(mTmpBounds, Op.UNION);
+        } else {
+            updateButtonLocation(getRotateSuggestionButton());
+        }
         mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
     }
 
@@ -1210,6 +1234,7 @@
         dumpButton(pw, "rcnt", getRecentsButton());
         dumpButton(pw, "rota", getRotateSuggestionButton());
         dumpButton(pw, "a11y", getAccessibilityButton());
+        dumpButton(pw, "ime", getImeSwitchButton());
 
         pw.println("    }");
         pw.println("    mScreenOn: " + mScreenOn);
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 99cb476..6946346 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;
@@ -454,6 +455,7 @@
 
     private boolean mAnimatingQS;
     private int mOldLayoutDirection;
+    private NotificationShelfController mNotificationShelfController;
 
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
         @Override
@@ -858,7 +860,7 @@
         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
@@ -2620,6 +2622,7 @@
         super.onClosingFinished();
         resetHorizontalPanelPosition();
         setClosingWithAlphaFadeout(false);
+        mMediaHierarchyManager.closeGuts();
     }
 
     private void setClosingWithAlphaFadeout(boolean closing) {
@@ -3052,7 +3055,7 @@
     }
 
     public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
-            NotificationShelf notificationShelf,
+            NotificationShelfController notificationShelfController,
             NotificationIconAreaController notificationIconAreaController,
             ScrimController scrimController) {
         setStatusBar(statusBar);
@@ -3061,9 +3064,10 @@
         mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
         mNotificationStackScroller.setStatusBar(statusBar);
         mNotificationStackScroller.setGroupManager(groupManager);
-        mNotificationStackScroller.setShelf(notificationShelf);
+        mNotificationStackScroller.setShelfController(notificationShelfController);
         mNotificationStackScroller.setScrimController(scrimController);
         updateShowEmptyShadeView();
+        mNotificationShelfController = notificationShelfController;
     }
 
     public void showTransientIndication(int id) {
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 b2cfcea..8cb54ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -662,16 +662,18 @@
 
         mIconController.setIconVisibility(mSlotCamera, showCamera);
         mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        mIconController.setIconVisibility(mSlotLocation, showLocation);
+        if (mPrivacyItemController.getAllIndicatorsAvailable()) {
+            mIconController.setIconVisibility(mSlotLocation, showLocation);
+        }
     }
 
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation();
+        if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
     }
 
     // Updates the status view based on the current state of location requests.
-    private void updateLocation() {
+    private void updateLocationFromController() {
         if (mLocationController.isLocationActive()) {
             mIconController.setIconVisibility(mSlotLocation, true);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
index 687efd3..74d4eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
@@ -20,9 +20,12 @@
 
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
+import java.util.function.Consumer;
+
 /** Interface of a rotation button that interacts {@link RotationButtonController}. */
 interface RotationButton {
     void setRotationButtonController(RotationButtonController rotationButtonController);
+    void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback);
     View getCurrentView();
     boolean show();
     boolean hide();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
index f83cdd4..2f2e1f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
@@ -124,7 +124,8 @@
     }
 
     RotationButtonController(Context context, @ColorInt int lightIconColor,
-            @ColorInt int darkIconColor, RotationButton rotationButton) {
+            @ColorInt int darkIconColor, RotationButton rotationButton,
+            Consumer<Boolean> visibilityChangedCallback) {
         mContext = context;
         mLightIconColor = lightIconColor;
         mDarkIconColor = darkIconColor;
@@ -139,6 +140,7 @@
         mTaskStackListener = new TaskStackListenerImpl();
         mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
         mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
+        mRotationButton.setVisibilityChangedCallback(visibilityChangedCallback);
     }
 
     void registerListeners() {
@@ -283,7 +285,6 @@
             return;
         }
 
-        // TODO: Remove styles?
         // Prepare to show the navbar icon by updating the icon style to change anim params
         mLastRotationSuggestion = rotation; // Remember rotation for click
         final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index d7e95e4..d63d445 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -18,18 +18,25 @@
 
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
+import android.content.Context;
 import android.view.View;
 
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
+import java.util.function.Consumer;
+
 /** Containing logic for the rotation button in nav bar. */
 public class RotationContextButton extends ContextualButton implements RotationButton {
     public static final boolean DEBUG_ROTATION = false;
 
     private RotationButtonController mRotationButtonController;
 
-    public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId) {
-        super(buttonResId, iconResId);
+    /**
+     * @param lightContext the context to use to load the icon resource
+     */
+    public RotationContextButton(@IdRes int buttonResId, Context lightContext,
+            @DrawableRes int iconResId) {
+        super(buttonResId, lightContext, iconResId);
     }
 
     @Override
@@ -38,6 +45,18 @@
     }
 
     @Override
+    public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
+        setListener(new ContextButtonListener() {
+            @Override
+            public void onVisibilityChanged(ContextualButton button, boolean visible) {
+                if (visibilityChangedCallback != null) {
+                    visibilityChangedCallback.accept(visible);
+                }
+            }
+        });
+    }
+
+    @Override
     public void setVisibility(int visibility) {
         super.setVisibility(visibility);
 
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..c8fb4e3 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;
@@ -1029,7 +1028,7 @@
                         mStatusBarStateController);
         mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController);
         inflateShelf();
-        mNotificationIconAreaController.setupShelf(mNotificationShelf);
+        mNotificationIconAreaController.setupShelf(mNotificationShelfController);
         mNotificationPanelViewController.setOnReinflationListener(
                 mNotificationIconAreaController::initAodIcons);
         mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
@@ -1147,7 +1146,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 +1310,7 @@
                 this /* statusBar */, mShadeController, mCommandQueue, mInitController,
                 mNotificationInterruptStateProvider);
 
-        mNotificationShelf.setOnActivatedListener(mPresenter);
+        mNotificationShelfController.setOnActivatedListener(mPresenter);
         mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
 
         mNotificationActivityStarter =
@@ -1386,8 +1386,9 @@
     }
 
     private void inflateShelf() {
-        mNotificationShelf = mSuperStatusBarViewFactory.getNotificationShelf(mStackScroller);
-        mNotificationShelf.setOnClickListener(mGoToLockedShadeListener);
+        mNotificationShelfController = mSuperStatusBarViewFactory
+                .getNotificationShelfController(mStackScroller);
+        mNotificationShelfController.setOnClickListener(mGoToLockedShadeListener);
     }
 
     @Override
@@ -4085,8 +4086,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/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index b89cb21..8ff7a41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -86,7 +86,7 @@
     static ArraySet<String> getIconHideList(Context context, String hideListStr) {
         ArraySet<String> ret = new ArraySet<>();
         String[] hideList = hideListStr == null
-            ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList)
+            ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
             : hideListStr.split(",");
         for (String slot : hideList) {
             if (!TextUtils.isEmpty(slot)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java
index 87b3956..bbab625 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java
@@ -40,5 +40,9 @@
         mListener = listener;
     }
 
+    abstract void start();
+
+    abstract void stop();
+
     abstract Set<String> getActivePackages();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index 8e4e123..e3eed35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -22,12 +22,12 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.annotation.IntDef;
 import android.annotation.UiThread;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -65,11 +65,13 @@
     // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest
     private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
 
+    private static final String ENABLE_FLAG = "sysui_mic_disclosure_enable";
     private static final String EXEMPT_PACKAGES_LIST = "sysui_mic_disclosure_exempt";
     private static final String FORCED_PACKAGES_LIST = "sysui_mic_disclosure_forced";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"STATE_"}, value = {
+            STATE_STOPPED,
             STATE_NOT_SHOWN,
             STATE_APPEARING,
             STATE_SHOWN,
@@ -80,6 +82,7 @@
     })
     public @interface State {}
 
+    private static final int STATE_STOPPED = -1;
     private static final int STATE_NOT_SHOWN = 0;
     private static final int STATE_APPEARING = 1;
     private static final int STATE_SHOWN = 2;
@@ -90,10 +93,9 @@
 
     private static final int ANIMATION_DURATION = 600;
     private static final int MAXIMIZED_DURATION = 3000;
-    private static final int PULSE_BIT_DURATION = 1000;
-    private static final float PULSE_SCALE = 1.25f;
 
     private final Context mContext;
+    private boolean mIsEnabledInSettings;
 
     private View mIndicatorView;
     private View mIconTextsContainer;
@@ -104,13 +106,13 @@
     private TextView mTextView;
     private boolean mIsLtr;
 
-    @State private int mState = STATE_NOT_SHOWN;
+    @State private int mState = STATE_STOPPED;
 
     /**
      * Array of the observers that monitor different aspects of the system, such as AppOps and
      * microphone foreground services
      */
-    private final AudioActivityObserver[] mAudioActivityObservers;
+    private AudioActivityObserver[] mAudioActivityObservers;
     /**
      * Whether the indicator should expand and show the recording application's label.
      * If disabled ({@code false}) the "minimized" ({@link #STATE_MINIMIZED}) indicator would appear
@@ -144,6 +146,7 @@
     public AudioRecordingDisclosureBar(Context context) {
         mContext = context;
 
+        // Loading configs
         mRevealRecordingPackages = mContext.getResources().getBoolean(
                 R.bool.audio_recording_disclosure_reveal_packages);
         mExemptPackages = new ArraySet<>(
@@ -152,10 +155,52 @@
         mExemptPackages.addAll(Arrays.asList(getGlobalStringArray(EXEMPT_PACKAGES_LIST)));
         mExemptPackages.removeAll(Arrays.asList(getGlobalStringArray(FORCED_PACKAGES_LIST)));
 
-        mAudioActivityObservers = new AudioActivityObserver[]{
-                new RecordAudioAppOpObserver(mContext, this),
-                new MicrophoneForegroundServicesObserver(mContext, this),
-        };
+        // Check setting, and start if enabled
+        mIsEnabledInSettings = checkIfEnabledInSettings();
+        registerSettingsObserver();
+        if (mIsEnabledInSettings) {
+            start();
+        }
+    }
+
+    @UiThread
+    private void start() {
+        if (mState != STATE_STOPPED) {
+            return;
+        }
+        mState = STATE_NOT_SHOWN;
+
+        if (mAudioActivityObservers == null) {
+            mAudioActivityObservers = new AudioActivityObserver[]{
+                    new RecordAudioAppOpObserver(mContext, this),
+                    new MicrophoneForegroundServicesObserver(mContext, this),
+            };
+        }
+
+        for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) {
+            mAudioActivityObservers[i].start();
+        }
+    }
+
+    @UiThread
+    private void stop() {
+        if (mState == STATE_STOPPED) {
+            return;
+        }
+        mState = STATE_STOPPED;
+
+        for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) {
+            mAudioActivityObservers[i].stop();
+        }
+
+        // Remove the view if shown.
+        if (mState != STATE_NOT_SHOWN) {
+            removeIndicatorView();
+        }
+
+        // Clean up the state.
+        mSessionNotifiedPackages.clear();
+        mPendingNotificationPackages.clear();
     }
 
     @UiThread
@@ -213,7 +258,6 @@
 
     @UiThread
     private void hideIndicatorIfNeeded() {
-        if (DEBUG) Log.d(TAG, "hideIndicatorIfNeeded");
         // If not MINIMIZED, will check whether the indicator should be hidden when the indicator
         // comes to the STATE_MINIMIZED eventually.
         if (mState != STATE_MINIMIZED) return;
@@ -222,7 +266,6 @@
         for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) {
             for (String activePackage : mAudioActivityObservers[index].getActivePackages()) {
                 if (mExemptPackages.contains(activePackage)) continue;
-                if (DEBUG) Log.d(TAG, "   - there are still ongoing activities");
                 return;
             }
         }
@@ -235,7 +278,7 @@
     @UiThread
     private void show(String packageName) {
         if (DEBUG) {
-            Log.d(TAG, "Showing indicator for " + packageName);
+            Log.d(TAG, "Showing indicator");
         }
 
         mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
@@ -286,6 +329,10 @@
                         new ViewTreeObserver.OnGlobalLayoutListener() {
                             @Override
                             public void onGlobalLayout() {
+                                if (mState == STATE_STOPPED) {
+                                    return;
+                                }
+
                                 // Remove the observer
                                 mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
                                         this);
@@ -306,13 +353,16 @@
                                             @Override
                                             public void onAnimationStart(Animator animation,
                                                     boolean isReverse) {
+                                                if (mState == STATE_STOPPED) {
+                                                    return;
+                                                }
+
                                                 // Indicator is INVISIBLE at the moment, change it.
                                                 mIndicatorView.setVisibility(View.VISIBLE);
                                             }
 
                                             @Override
                                             public void onAnimationEnd(Animator animation) {
-                                                startPulsatingAnimation();
                                                 if (mRevealRecordingPackages) {
                                                     onExpanded();
                                                 } else {
@@ -345,9 +395,6 @@
         assertRevealingRecordingPackages();
 
         final String label = getApplicationLabel(packageName);
-        if (DEBUG) {
-            Log.d(TAG, "Expanding for " + packageName + " (" + label + ")...");
-        }
         mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
 
         final AnimatorSet set = new AnimatorSet();
@@ -373,7 +420,6 @@
     private void minimize() {
         assertRevealingRecordingPackages();
 
-        if (DEBUG) Log.d(TAG, "Minimizing...");
         final int targetOffset = (mIsLtr ? 1 : -1) * mTextsContainers.getWidth();
         final AnimatorSet set = new AnimatorSet();
         set.playTogether(
@@ -396,7 +442,9 @@
 
     @UiThread
     private void hide() {
-        if (DEBUG) Log.d(TAG, "Hiding...");
+        if (DEBUG) {
+            Log.d(TAG, "Hide indicator");
+        }
         final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth()
                 - (int) mIconTextsContainer.getTranslationX());
         final AnimatorSet set = new AnimatorSet();
@@ -418,9 +466,12 @@
 
     @UiThread
     private void onExpanded() {
+        if (mState == STATE_STOPPED) {
+            return;
+        }
+
         assertRevealingRecordingPackages();
 
-        if (DEBUG) Log.d(TAG, "Expanded");
         mState = STATE_SHOWN;
 
         mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION);
@@ -428,7 +479,10 @@
 
     @UiThread
     private void onMinimized() {
-        if (DEBUG) Log.d(TAG, "Minimized");
+        if (mState == STATE_STOPPED) {
+            return;
+        }
+
         mState = STATE_MINIMIZED;
 
         if (mRevealRecordingPackages) {
@@ -443,8 +497,21 @@
 
     @UiThread
     private void onHidden() {
-        if (DEBUG) Log.d(TAG, "Hidden");
+        if (mState == STATE_STOPPED) {
+            return;
+        }
 
+        removeIndicatorView();
+        mState = STATE_NOT_SHOWN;
+
+        // Check if anybody started recording while we were in STATE_DISAPPEARING
+        if (!mPendingNotificationPackages.isEmpty()) {
+            // There is a new application that started recording, tell the user about it.
+            show(mPendingNotificationPackages.poll());
+        }
+    }
+
+    private void removeIndicatorView() {
         final WindowManager windowManager = (WindowManager) mContext.getSystemService(
                 Context.WINDOW_SERVICE);
         windowManager.removeView(mIndicatorView);
@@ -456,28 +523,6 @@
         mTextsContainers = null;
         mTextView = null;
         mBgEnd = null;
-
-        mState = STATE_NOT_SHOWN;
-
-        // Check if anybody started recording while we were in STATE_DISAPPEARING
-        if (!mPendingNotificationPackages.isEmpty()) {
-            // There is a new application that started recording, tell the user about it.
-            show(mPendingNotificationPackages.poll());
-        }
-    }
-
-    @UiThread
-    private void startPulsatingAnimation() {
-        final View pulsatingView = mIconTextsContainer.findViewById(R.id.pulsating_circle);
-        final ObjectAnimator animator =
-                ObjectAnimator.ofPropertyValuesHolder(
-                        pulsatingView,
-                        PropertyValuesHolder.ofFloat(View.SCALE_X, PULSE_SCALE),
-                        PropertyValuesHolder.ofFloat(View.SCALE_Y, PULSE_SCALE));
-        animator.setDuration(PULSE_BIT_DURATION);
-        animator.setRepeatCount(ObjectAnimator.INFINITE);
-        animator.setRepeatMode(ObjectAnimator.REVERSE);
-        animator.start();
     }
 
     private String[] getGlobalStringArray(String setting) {
@@ -504,4 +549,33 @@
                     DEBUG ? new RuntimeException("Should not be called") : null);
         }
     }
+
+    private boolean checkIfEnabledInSettings() {
+        // 0 = disabled, everything else = enabled. Enabled by default.
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                ENABLE_FLAG, 1) != 0;
+    }
+
+    private void registerSettingsObserver() {
+        final ContentObserver contentObserver = new ContentObserver(
+                mContext.getMainThreadHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                if (mIsEnabledInSettings == checkIfEnabledInSettings()) {
+                    // Nothing changed as we know it - ignore.
+                    return;
+                }
+
+                // Things changed: flip the flag.
+                mIsEnabledInSettings = !mIsEnabledInSettings;
+                if (mIsEnabledInSettings) {
+                    start();
+                } else {
+                    stop();
+                }
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(ENABLE_FLAG), false, contentObserver);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java
index 1ede88a..8caf95f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java
@@ -30,7 +30,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -41,9 +40,8 @@
  */
 class MicrophoneForegroundServicesObserver extends AudioActivityObserver {
     private static final String TAG = "MicrophoneForegroundServicesObserver";
-    private static final boolean ENABLED = true;
 
-    private final IActivityManager mActivityManager;
+    private IActivityManager mActivityManager;
     /**
      * A dictionary that maps PIDs to the package names. We only keep track of the PIDs that are
      * "active" (those that are running FGS with FOREGROUND_SERVICE_TYPE_MICROPHONE flag).
@@ -60,7 +58,10 @@
     MicrophoneForegroundServicesObserver(Context context,
             OnAudioActivityStateChangeListener listener) {
         super(context, listener);
+    }
 
+    @Override
+    void start() {
         mActivityManager = ActivityManager.getService();
         try {
             mActivityManager.registerProcessObserver(mProcessObserver);
@@ -70,8 +71,19 @@
     }
 
     @Override
+    void stop() {
+        try {
+            mActivityManager.unregisterProcessObserver(mProcessObserver);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't unregister process observer", e);
+        }
+        mActivityManager = null;
+        mPackageToProcessCount.clear();
+    }
+
+    @Override
     Set<String> getActivePackages() {
-        return ENABLED ? mPackageToProcessCount.keySet() : Collections.emptySet();
+        return mPackageToProcessCount.keySet();
     }
 
     @UiThread
@@ -141,13 +153,12 @@
 
     @UiThread
     private void notifyPackageStateChanged(String packageName, boolean active) {
-        if (active) {
-            if (DEBUG) Log.d(TAG, "New microphone fgs detected, package=" + packageName);
-        } else {
-            if (DEBUG) Log.d(TAG, "Microphone fgs is gone, package=" + packageName);
+        if (DEBUG) {
+            Log.d(TAG, (active ? "New microphone fgs detected" : "Microphone fgs is gone")
+                    + ", package=" + packageName);
         }
 
-        if (ENABLED) mListener.onAudioActivityStateChange(active, packageName);
+        mListener.onAudioActivityStateChange(active, packageName);
     }
 
     @UiThread
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java
index b5b1c2b..9a2b4a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java
@@ -42,14 +42,33 @@
 
     RecordAudioAppOpObserver(Context context, OnAudioActivityStateChangeListener listener) {
         super(context, listener);
+    }
+
+    @Override
+    void start() {
+        if (DEBUG) {
+            Log.d(TAG, "Start");
+        }
 
         // Register AppOpsManager callback
-        final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(
-                Context.APP_OPS_SERVICE);
-        appOpsManager.startWatchingActive(
-                new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
-                mContext.getMainExecutor(),
-                this);
+        mContext.getSystemService(AppOpsManager.class)
+                .startWatchingActive(
+                        new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
+                        mContext.getMainExecutor(),
+                        this);
+    }
+
+    @Override
+    void stop() {
+        if (DEBUG) {
+            Log.d(TAG, "Stop");
+        }
+
+        // Unregister AppOpsManager callback
+        mContext.getSystemService(AppOpsManager.class).stopWatchingActive(this);
+
+        // Clean up state
+        mActiveAudioRecordingPackages.clear();
     }
 
     @UiThread
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index b5f98ad..89297fd 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -54,7 +54,7 @@
         String uriString = intent.getStringExtra("uri");
         mUri = (uriString == null ? null : Uri.parse(uriString));
 
-        // sanity check before displaying dialog
+        // Exception check before displaying dialog
         if (mUri == null) {
             Log.e(TAG, "could not parse Uri " + uriString);
             finish();
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/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 3455ff4..4b119dd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -150,6 +150,8 @@
     private boolean mShowing;
     private boolean mShowA11yStream;
 
+    private final boolean mShowLowMediaVolumeIcon;
+
     private int mActiveStream;
     private int mPrevActiveStream;
     private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
@@ -166,7 +168,7 @@
 
     public VolumeDialogImpl(Context context) {
         mContext =
-                new ContextThemeWrapper(context, R.style.qs_theme);
+                new ContextThemeWrapper(context, R.style.volume_dialog_theme);
         mController = Dependency.get(VolumeDialogController.class);
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -175,6 +177,8 @@
         mShowActiveStreamOnly = showActiveStreamOnly();
         mHasSeenODICaptionsTooltip =
                 Prefs.getBoolean(context, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
+        mShowLowMediaVolumeIcon =
+            mContext.getResources().getBoolean(R.bool.config_showLowMediaVolumeIcon);
     }
 
     @Override
@@ -421,6 +425,7 @@
         row.dndIcon = row.view.findViewById(R.id.dnd_icon);
         row.slider = row.view.findViewById(R.id.volume_row_slider);
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
+        row.number = row.view.findViewById(R.id.volume_number);
 
         row.anim = null;
 
@@ -1024,19 +1029,28 @@
         final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
         row.icon.setEnabled(iconEnabled);
         row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
-        final int iconRes =
-                isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
-                        : isRingSilent || zenMuted ? row.iconMuteRes
-                                : ss.routedToBluetooth
-                                        ? isStreamMuted(ss) ? R.drawable.ic_volume_media_bt_mute
-                                                : R.drawable.ic_volume_media_bt
-                                        : isStreamMuted(ss) ? row.iconMuteRes : row.iconRes;
+        final int iconRes;
+        if (isRingVibrate) {
+            iconRes = R.drawable.ic_volume_ringer_vibrate;
+        } else if (isRingSilent || zenMuted) {
+            iconRes = row.iconMuteRes;
+        } else if (ss.routedToBluetooth) {
+            iconRes = isStreamMuted(ss) ? R.drawable.ic_volume_media_bt_mute
+                                        : R.drawable.ic_volume_media_bt;
+        } else if (isStreamMuted(ss)) {
+            iconRes = row.iconMuteRes;
+        } else {
+            iconRes = mShowLowMediaVolumeIcon && ss.level * 2 < (ss.levelMax + ss.levelMin)
+                      ? R.drawable.ic_volume_media_low : row.iconRes;
+        }
+
         row.icon.setImageResource(iconRes);
         row.iconState =
                 iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
                 : (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
                         ? Events.ICON_STATE_MUTE
-                : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes)
+                : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes
+                        || iconRes == R.drawable.ic_volume_media_low)
                         ? Events.ICON_STATE_UNMUTE
                 : Events.ICON_STATE_UNKNOWN;
         if (iconEnabled) {
@@ -1090,6 +1104,7 @@
         final int vlevel = row.ss.muted && (!isRingStream && !zenMuted) ? 0
                 : row.ss.level;
         updateVolumeRowSliderH(row, enableSlider, vlevel);
+        if (row.number != null) row.number.setText(Integer.toString(vlevel));
     }
 
     private boolean isStreamMuted(final StreamState streamState) {
@@ -1115,6 +1130,10 @@
         row.icon.setImageTintList(tint);
         row.icon.setImageAlpha(alpha);
         row.cachedTint = tint;
+        if (row.number != null) {
+            row.number.setTextColor(tint);
+            row.number.setAlpha(alpha);
+        }
     }
 
     private void updateVolumeRowSliderH(VolumeRow row, boolean enable, int vlevel) {
@@ -1346,7 +1365,7 @@
 
     private final class CustomDialog extends Dialog implements DialogInterface {
         public CustomDialog(Context context) {
-            super(context, R.style.qs_theme);
+            super(context, R.style.volume_dialog_theme);
         }
 
         @Override
@@ -1458,6 +1477,7 @@
         private TextView header;
         private ImageButton icon;
         private SeekBar slider;
+        private TextView number;
         private int stream;
         private StreamState ss;
         private long userAttempt;  // last user-driven slider change
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/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/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index ac8c671..4c54954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -88,7 +88,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class GlobalActionsDialogTest extends SysuiTestCase {
     private GlobalActionsDialog mGlobalActionsDialog;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index c63781c..8a30b00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media
 
+import android.content.Intent
 import android.content.res.ColorStateList
 import android.graphics.Color
 import android.graphics.drawable.GradientDrawable
@@ -23,6 +24,7 @@
 import android.media.MediaMetadata
 import android.media.session.MediaSession
 import android.media.session.PlaybackState
+import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -35,24 +37,31 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.LiveData
 import androidx.test.filters.SmallTest
-import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private const val KEY = "TEST_KEY"
 private const val APP = "APP"
@@ -81,6 +90,8 @@
     @Mock private lateinit var seekBarViewModel: SeekBarViewModel
     @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
     @Mock private lateinit var mediaViewController: MediaViewController
+    @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
+    @Mock private lateinit var mediaDataManager: MediaDataManager
     @Mock private lateinit var expandedSet: ConstraintSet
     @Mock private lateinit var collapsedSet: ConstraintSet
     private lateinit var appIcon: ImageView
@@ -100,6 +111,9 @@
     private lateinit var action2: ImageButton
     private lateinit var action3: ImageButton
     private lateinit var action4: ImageButton
+    private lateinit var settings: View
+    private lateinit var cancel: View
+    private lateinit var dismiss: View
 
     private lateinit var session: MediaSession
     private val device = MediaDeviceData(true, null, DEVICE_NAME)
@@ -114,7 +128,7 @@
         whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
 
         player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
-                seekBarViewModel)
+                seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil)
         whenever(seekBarViewModel.progress).thenReturn(seekBarData)
 
         // Mock out a view holder for the player to attach to.
@@ -156,6 +170,12 @@
         whenever(holder.action3).thenReturn(action3)
         action4 = ImageButton(context)
         whenever(holder.action4).thenReturn(action4)
+        settings = View(context)
+        whenever(holder.settings).thenReturn(settings)
+        cancel = View(context)
+        whenever(holder.cancel).thenReturn(cancel)
+        dismiss = View(context)
+        whenever(holder.dismiss).thenReturn(dismiss)
 
         // Create media session
         val metadataBuilder = MediaMetadata.Builder().apply {
@@ -254,4 +274,79 @@
         assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
         assertThat(seamless.isEnabled()).isFalse()
     }
+
+    @Test
+    fun longClick_gutsClosed() {
+        player.attach(holder)
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(holder.player).setOnLongClickListener(captor.capture())
+
+        captor.value.onLongClick(holder.player)
+        verify(mediaViewController).openGuts()
+    }
+
+    @Test
+    fun longClick_gutsOpen() {
+        player.attach(holder)
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(holder.player).setOnLongClickListener(captor.capture())
+
+        captor.value.onLongClick(holder.player)
+        verify(mediaViewController, never()).openGuts()
+    }
+
+    @Test
+    fun cancelButtonClick_animation() {
+        player.attach(holder)
+
+        cancel.callOnClick()
+
+        verify(mediaViewController).closeGuts(false)
+    }
+
+    @Test
+    fun settingsButtonClick() {
+        player.attach(holder)
+
+        settings.callOnClick()
+
+        val captor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(activityStarter).startActivity(captor.capture(), eq(true))
+
+        assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS)
+    }
+
+    @Test
+    fun dismissButtonClick() {
+        player.attach(holder)
+        val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+                emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
+                notificationKey = KEY)
+        player.bind(state)
+
+        dismiss.callOnClick()
+        val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
+        verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean())
+
+        captor.value.onDismiss()
+        verify(mediaDataManager).dismissMediaData(eq(KEY), anyLong())
+    }
+
+    @Test
+    fun dismissButtonClick_nullNotificationKey() {
+        player.attach(holder)
+        val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+                emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
+        player.bind(state)
+
+        verify(keyguardDismissUtil, never())
+                .executeWhenUnlocked(
+                        any(ActivityStarter.OnDismissAction::class.java),
+                        ArgumentMatchers.anyBoolean()
+                )
+    }
 }
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 59c2d0e..3789e6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -217,6 +217,20 @@
         assertThat(data.actions).hasSize(1)
     }
 
+    @Test
+    fun testDismissMedia_listenerCalled() {
+        val listener = mock(MediaDataManager.Listener::class.java)
+        mediaDataManager.addListener(listener)
+        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+        mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
+        mediaDataManager.dismissMediaData(KEY, 0L)
+
+        foregroundExecutor.advanceClockToLast()
+        foregroundExecutor.runAllReady()
+
+        verify(listener).onMediaDataRemoved(eq(KEY))
+    }
+
     /**
      * Simple implementation of [MediaDataManager.Listener] for the test.
      *
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 91c5ff8..d86dfa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -142,4 +142,11 @@
         verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                 any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
     }
+
+    @Test
+    fun testCloseGutsRelayToCarousel() {
+        mediaHiearchyManager.closeGuts()
+
+        verify(mediaCarouselController).closeGuts()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
index 694f51b..f4c0700 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
@@ -24,14 +24,13 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.app.Instrumentation;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.wm.shell.common.DisplayController;
 
@@ -46,12 +45,13 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedGestureHandlerTest extends OneHandedTestCase {
-    Instrumentation mInstrumentation;
     OneHandedTouchHandler mTouchHandler;
     OneHandedTutorialHandler mTutorialHandler;
     OneHandedGestureHandler mGestureHandler;
     OneHandedManagerImpl mOneHandedManagerImpl;
     @Mock
+    CommandQueue mCommandQueue;
+    @Mock
     DisplayController mMockDisplayController;
     @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@@ -62,12 +62,13 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mTouchHandler = new OneHandedTouchHandler();
         mTutorialHandler = new OneHandedTutorialHandler(mContext);
         mGestureHandler = Mockito.spy(new OneHandedGestureHandler(
                 mContext, mMockDisplayController, mMockNavigationModeController));
-        mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(),
+        mOneHandedManagerImpl = new OneHandedManagerImpl(
+                getContext(),
+                mCommandQueue,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
                 mTouchHandler,
@@ -100,6 +101,7 @@
     @Test
     public void testOneHandedDisabled_shouldDisposeInputChannel() {
         mOneHandedManagerImpl.setOneHandedEnabled(false);
+        mOneHandedManagerImpl.setSwipeToNotificationEnabled(false);
 
         assertThat(mGestureHandler.mInputMonitor).isNull();
         assertThat(mGestureHandler.mInputEventReceiver).isNull();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index 3418ebf..763f6e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -33,6 +33,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.wm.shell.common.DisplayController;
 
 import org.junit.Before;
@@ -51,6 +52,8 @@
     OneHandedTimeoutHandler mTimeoutHandler;
 
     @Mock
+    CommandQueue mCommandQueue;
+    @Mock
     DisplayController mMockDisplayController;
     @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@@ -67,7 +70,9 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mDisplay = mContext.getDisplay();
-        mOneHandedManagerImpl = new OneHandedManagerImpl(getContext(),
+        mOneHandedManagerImpl = new OneHandedManagerImpl(
+                getContext(),
+                mCommandQueue,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
                 mMockTouchHandler,
@@ -94,14 +99,14 @@
 
     @Test
     public void testRegisterOrganizer() {
-        verify(mMockDisplayAreaOrganizer, times(1)).registerOrganizer(anyInt());
+        verify(mMockDisplayAreaOrganizer).registerOrganizer(anyInt());
     }
 
     @Test
     public void testStartOneHanded() {
         mOneHandedManagerImpl.startOneHanded();
 
-        verify(mMockDisplayAreaOrganizer, times(1)).scheduleOffset(anyInt(), anyInt());
+        verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt());
     }
 
     @Test
@@ -121,7 +126,7 @@
     public void testStopOneHanded_shouldRemoveTimer() {
         mOneHandedManagerImpl.stopOneHanded();
 
-        verify(mTimeoutHandler, times(1)).removeTimer();
+        verify(mTimeoutHandler).removeTimer();
     }
 
     @Test
@@ -129,7 +134,14 @@
         final boolean enabled = true;
         mOneHandedManagerImpl.setOneHandedEnabled(enabled);
 
-        verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled);
+        verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled);
     }
 
+    @Test
+    public void testUpdateSwipeToNotificationIsEnabled() {
+        final boolean enabled = true;
+        mOneHandedManagerImpl.setSwipeToNotificationEnabled(enabled);
+
+        verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java
index c157ae6..f81d047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java
@@ -104,4 +104,10 @@
                 ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS,
                 ONE_HANDED_TIMEOUT_LONG_IN_SECONDS);
     }
+
+    @Test
+    public void testGetSettingsSwipeToNotificationEnabled() {
+        assertThat(mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                mContentResolver)).isAnyOf(true, false);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java
index befa42a..04ebf25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java
@@ -32,6 +32,7 @@
     static boolean sOrigEnabled;
     static boolean sOrigTapsAppToExitEnabled;
     static int sOrigTimeout;
+    static boolean sOrigSwipeToNotification;
 
     @Before
     public void setupSettings() {
@@ -41,12 +42,16 @@
                 getContext().getContentResolver());
         sOrigTapsAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
                 getContext().getContentResolver());
+        sOrigSwipeToNotification = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                getContext().getContentResolver());
         Settings.Secure.putInt(getContext().getContentResolver(),
                 Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
         Settings.Secure.putInt(getContext().getContentResolver(),
                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
         Settings.Secure.putInt(getContext().getContentResolver(),
                 Settings.Secure.TAPS_APP_TO_EXIT, 1);
+        Settings.Secure.putInt(getContext().getContentResolver(),
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
     }
 
     @After
@@ -57,6 +62,9 @@
                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT, sOrigTimeout);
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.TAPS_APP_TO_EXIT, sOrigTapsAppToExitEnabled ? 1 : 0);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+                sOrigSwipeToNotification ? 1 : 0);
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
index fdb28d3..15881a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
@@ -22,14 +22,13 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.app.Instrumentation;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.wm.shell.common.DisplayController;
 
@@ -44,12 +43,13 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedTouchHandlerTest extends OneHandedTestCase {
-    Instrumentation mInstrumentation;
     OneHandedTouchHandler mTouchHandler;
     OneHandedTutorialHandler mTutorialHandler;
     OneHandedGestureHandler mGestureHandler;
     OneHandedManagerImpl mOneHandedManagerImpl;
     @Mock
+    CommandQueue mCommandQueue;
+    @Mock
     DisplayController mMockDisplayController;
     @Mock
     NavigationModeController mMockNavigationModeController;
@@ -61,11 +61,12 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mTouchHandler = Mockito.spy(new OneHandedTouchHandler());
         mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
                 mMockNavigationModeController);
-        mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(),
+        mOneHandedManagerImpl = new OneHandedManagerImpl(
+                getContext(),
+                mCommandQueue,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
                 mTouchHandler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
index f4aa00e..f2b77a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
@@ -19,14 +19,13 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.app.Instrumentation;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.wm.shell.common.DisplayController;
 
@@ -41,12 +40,13 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
-    Instrumentation mInstrumentation;
     OneHandedTouchHandler mTouchHandler;
     OneHandedTutorialHandler mTutorialHandler;
     OneHandedGestureHandler mGestureHandler;
     OneHandedManagerImpl mOneHandedManagerImpl;
     @Mock
+    CommandQueue mCommandQueue;
+    @Mock
     DisplayController mMockDisplayController;
     @Mock
     NavigationModeController mMockNavigationModeController;
@@ -58,12 +58,13 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mTouchHandler = new OneHandedTouchHandler();
         mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext));
         mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
                 mMockNavigationModeController);
-        mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(),
+        mOneHandedManagerImpl = new OneHandedManagerImpl(
+                getContext(),
+                mCommandQueue,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
                 mTouchHandler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
index ffedb07..6db2679 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
@@ -28,7 +28,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.statusbar.CommandQueue;
 
@@ -53,8 +52,6 @@
     @Mock
     OneHandedManagerImpl mMockOneHandedManagerImpl;
     @Mock
-    DumpManager mMockDumpManager;
-    @Mock
     OneHandedSettingsUtil mMockSettingsUtil;
     @Mock
     OneHandedTimeoutHandler mMockTimeoutHandler;
@@ -68,7 +65,6 @@
         mOneHandedUI = new OneHandedUI(mContext,
                 mCommandQueue,
                 mMockOneHandedManagerImpl,
-                mMockDumpManager,
                 mMockSettingsUtil,
                 mScreenLifecycle);
         mOneHandedUI.start();
@@ -168,6 +164,18 @@
                 OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
     }
 
+    @Test
+    public void tesSettingsObserver_updateSwipeToNotification() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
+
+        verify(mMockOneHandedManagerImpl).setSwipeToNotificationEnabled(true);
+    }
+
     @Ignore("Clarifying do not receive callback")
     @Test
     public void testKeyguardBouncerShowing_shouldStopOneHanded() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
index 96bb521..9f67722 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -37,6 +37,7 @@
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -85,6 +86,9 @@
     @Mock
     private SysUiState mSysUiState;
 
+    @Mock
+    private PipUiEventLogger mPipUiEventLogger;
+
     private PipSnapAlgorithm mPipSnapAlgorithm;
     private PipMotionHelper mMotionHelper;
     private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -104,7 +108,7 @@
         mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager,
                 mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
                 mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy,
-                mPipSnapAlgorithm, mSysUiState);
+                mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger);
         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
         mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
         mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 548da8e..3562032 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -384,7 +384,7 @@
         mPowerUI.mSevereWarningShownThisChargeCycle = false;
         BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
 
-        // sanity check to make sure we can show for a valid config
+        // readiness check to make sure we can show for a valid config
         state.mBatteryLevel = 10;
         state.mTimeRemainingMillis = Duration.ofHours(2).toMillis();
         boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
@@ -449,7 +449,7 @@
         mPowerUI.mSevereWarningShownThisChargeCycle = false;
         BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
 
-        // sanity check to make sure we can show for a valid config
+        // readiness check to make sure we can show for a valid config
         state.mBatteryLevel = 1;
         state.mTimeRemainingMillis = Duration.ofMinutes(1).toMillis();
         boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
@@ -572,7 +572,7 @@
         state.mIsHybrid = false;
         BatteryStateSnapshot lastState = state.get();
 
-        // sanity check to make sure we can show for a valid config
+        // readiness check to make sure we can show for a valid config
         state.mBatteryLevel = 10;
         state.mBucket = -1;
         boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
new file mode 100644
index 0000000..4ba29e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.privacy
+
+import android.os.UserManager
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+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.Mockito.anyBoolean
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PrivacyItemControllerFlagsTest : SysuiTestCase() {
+    companion object {
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+        fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+        fun <T> any(): T = Mockito.any<T>()
+
+        private const val ALL_INDICATORS =
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+    }
+
+    @Mock
+    private lateinit var appOpsController: AppOpsController
+    @Mock
+    private lateinit var callback: PrivacyItemController.Callback
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock
+    private lateinit var dumpManager: DumpManager
+
+    private lateinit var privacyItemController: PrivacyItemController
+    private lateinit var executor: FakeExecutor
+    private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+    fun PrivacyItemController(): PrivacyItemController {
+        return PrivacyItemController(
+                appOpsController,
+                executor,
+                executor,
+                broadcastDispatcher,
+                deviceConfigProxy,
+                userManager,
+                dumpManager
+        )
+    }
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        executor = FakeExecutor(FakeSystemClock())
+        deviceConfigProxy = DeviceConfigProxyFake()
+
+        privacyItemController = PrivacyItemController()
+        privacyItemController.addCallback(callback)
+
+        executor.runAllReady()
+    }
+
+    @Test
+    fun testNotListeningByDefault() {
+        assertFalse(privacyItemController.allIndicatorsAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
+
+        verify(appOpsController, never()).addCallback(any(), any())
+    }
+
+    @Test
+    fun testMicCameraChanged() {
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        verify(callback).onFlagMicCameraChanged(true)
+        verify(callback, never()).onFlagAllChanged(anyBoolean())
+
+        assertTrue(privacyItemController.micCameraAvailable)
+        assertFalse(privacyItemController.allIndicatorsAvailable)
+    }
+
+    @Test
+    fun testAllChanged() {
+        changeAll(true)
+        executor.runAllReady()
+
+        verify(callback).onFlagAllChanged(true)
+        verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
+
+        assertTrue(privacyItemController.allIndicatorsAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
+    }
+
+    @Test
+    fun testBothChanged() {
+        changeAll(true)
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        verify(callback, atLeastOnce()).onFlagAllChanged(true)
+        verify(callback, atLeastOnce()).onFlagMicCameraChanged(true)
+
+        assertTrue(privacyItemController.allIndicatorsAvailable)
+        assertTrue(privacyItemController.micCameraAvailable)
+    }
+
+    @Test
+    fun testAll_listeningToAll() {
+        changeAll(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
+    fun testMicCamera_listening() {
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
+    fun testAll_listening() {
+        changeAll(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
+    fun testAllFalse_notListening() {
+        changeAll(true)
+        executor.runAllReady()
+        changeAll(false)
+        executor.runAllReady()
+
+        verify(appOpsController).removeCallback(any(), any())
+    }
+
+    @Test
+    fun testSomeListening_stillListening() {
+        changeAll(true)
+        changeMicCamera(true)
+        executor.runAllReady()
+        changeAll(false)
+        executor.runAllReady()
+
+        verify(appOpsController, never()).removeCallback(any(), any())
+    }
+
+    @Test
+    fun testAllDeleted_stopListening() {
+        changeAll(true)
+        executor.runAllReady()
+        changeAll(null)
+        executor.runAllReady()
+
+        verify(appOpsController).removeCallback(any(), any())
+    }
+
+    @Test
+    fun testMicDeleted_stopListening() {
+        changeMicCamera(true)
+        executor.runAllReady()
+        changeMicCamera(null)
+        executor.runAllReady()
+
+        verify(appOpsController).removeCallback(any(), any())
+    }
+
+    private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+    private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+
+    private fun changeProperty(name: String, value: Boolean?) {
+        deviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                name,
+                value?.toString(),
+                false
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index dddc350..fb42baa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager
 import android.app.AppOpsManager
-import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.os.UserHandle
@@ -69,10 +68,11 @@
     companion object {
         val CURRENT_USER_ID = ActivityManager.getCurrentUser()
         val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
-        const val SYSTEM_UID = 1000
         const val TEST_PACKAGE_NAME = "test"
-        const val DEVICE_SERVICES_STRING = "Device services"
-        const val TAG = "PrivacyItemControllerTest"
+
+        private const val ALL_INDICATORS =
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
         fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
         fun <T> eq(value: T): T = Mockito.eq(value) ?: value
         fun <T> any(): T = Mockito.any<T>()
@@ -97,9 +97,8 @@
     private lateinit var executor: FakeExecutor
     private lateinit var deviceConfigProxy: DeviceConfigProxy
 
-    fun PrivacyItemController(context: Context): PrivacyItemController {
+    fun PrivacyItemController(): PrivacyItemController {
         return PrivacyItemController(
-                context,
                 appOpsController,
                 executor,
                 executor,
@@ -116,11 +115,8 @@
         executor = FakeExecutor(FakeSystemClock())
         deviceConfigProxy = DeviceConfigProxyFake()
 
-        appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
-
-        deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                "true", false)
+        // Listen to everything by default
+        changeAll(true)
 
         doReturn(listOf(object : UserInfo() {
             init {
@@ -128,7 +124,7 @@
             }
         })).`when`(userManager).getProfiles(anyInt())
 
-        privacyItemController = PrivacyItemController(mContext)
+        privacyItemController = PrivacyItemController()
     }
 
     @Test
@@ -276,15 +272,59 @@
 
     @Test
     fun testNotListeningWhenIndicatorsDisabled() {
-        deviceConfigProxy.setProperty(
-                DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                "false",
-                false
-        )
+        changeAll(false)
         privacyItemController.addCallback(callback)
         executor.runAllReady()
         verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
                 any())
     }
+
+    @Test
+    fun testNotSendingLocationWhenOnlyMicCamera() {
+        changeAll(false)
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+                AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+
+        verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+
+        assertEquals(1, argCaptor.value.size)
+        assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType)
+    }
+
+    @Test
+    fun testNotUpdated_LocationChangeWhenOnlyMicCamera() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        changeAll(false)
+        changeMicCamera(true)
+        executor.runAllReady()
+        reset(callback) // Clean callback
+
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+        argCaptorCallback.value.onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+
+        verify(callback, never()).onPrivacyItemsChanged(any())
+    }
+
+    private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+    private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+
+    private fun changeProperty(name: String, value: Boolean?) {
+        deviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                name,
+                value?.toString(),
+                false
+        )
+    }
 }
\ No newline at end of file
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 103e558..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
@@ -74,7 +74,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class QSTileImplTest extends SysuiTestCase {
 
@@ -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/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 2d276bb..6b54791 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -47,7 +47,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ScreenRecordTileTest 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..85701c2 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
@@ -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/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index ae87eef..781f875 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -41,7 +41,7 @@
 import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper()
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
index b5060ee..1fb28f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
@@ -65,9 +65,9 @@
         mDependency.injectMockDependency(AssistManager.class);
 
         mGroup = new ContextualButtonGroup(GROUP_ID);
-        mBtn0 = new ContextualButton(BUTTON_0_ID, ICON_RES_ID);
-        mBtn1 = new ContextualButton(BUTTON_1_ID, ICON_RES_ID);
-        mBtn2 = new ContextualButton(BUTTON_2_ID, ICON_RES_ID);
+        mBtn0 = new ContextualButton(BUTTON_0_ID, mContext, ICON_RES_ID);
+        mBtn1 = new ContextualButton(BUTTON_1_ID, mContext, ICON_RES_ID);
+        mBtn2 = new ContextualButton(BUTTON_2_ID, mContext, ICON_RES_ID);
 
         // Order of adding buttons to group determines the priority, ascending priority order
         mGroup.addButton(mBtn0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
index f21235c..f665241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
@@ -60,7 +60,7 @@
         final View view = new View(mContext);
         mRotationButton = mock(RotationButton.class);
         mRotationButtonController = spy(new RotationButtonController(mContext, 0, 0,
-                mRotationButton));
+                mRotationButton, (visibility) -> {}));
         final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
         doReturn(view).when(mRotationButton).getCurrentView();
         doReturn(true).when(mRotationButton).acceptRotationProposal();
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..bf4ccd2 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;
@@ -115,7 +115,7 @@
     @Mock
     private HeadsUpManagerPhone mHeadsUpManager;
     @Mock
-    private NotificationShelf mNotificationShelf;
+    private NotificationShelfController mNotificationShelfController;
     @Mock
     private NotificationGroupManager mGroupManager;
     @Mock
@@ -246,7 +246,7 @@
                 mBiometricUnlockController, mStatusBarKeyguardViewManager,
                 () -> mKeyguardClockSwitchController);
         mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager,
-                mNotificationShelf, mNotificationAreaController, mScrimController);
+                mNotificationShelfController, mNotificationAreaController, mScrimController);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
         mNotificationPanelViewController.setBar(mPanelBar);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
index a16fb5e..86dacc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
@@ -37,7 +37,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 public class CallbackControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
index 0e1c560..b2f57d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
@@ -39,7 +39,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 public class LifecycleFragmentTest extends SysuiBaseFragmentTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
index 486939d..4f509ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
@@ -49,7 +49,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 public class SysuiLifecycleTest extends SysuiTestCase {
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 9b9dcde..5f8d299 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -73,6 +73,9 @@
     <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
     <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
 
+    <!-- Use legacy wifi p2p dedicated address instead of randomize address. -->
+    <bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool>
+
     <!-- Dhcp range (min, max) to use for tethering purposes -->
     <string-array translatable="false" name="config_tether_dhcp_range">
     </string-array>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index 6a33d55..0ee7a99 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -30,6 +30,7 @@
             -->
             <item type="bool" name="config_tether_enable_bpf_offload"/>
             <item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
+            <item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/>
             <item type="integer" name="config_tether_offload_poll_interval"/>
             <item type="array" name="config_tether_upstream_types"/>
             <item type="bool" name="config_tether_upstream_automatic"/>
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index aa58a4b..fd9e360 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -15,6 +15,8 @@
  */
 package com.android.networkstack.tethering;
 
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+
 import static java.util.Arrays.asList;
 
 import android.content.Context;
@@ -58,6 +60,7 @@
     private static final int BYTE_MASK = 0xff;
     // reserved for bluetooth tethering.
     private static final int BLUETOOTH_RESERVED = 44;
+    private static final int WIFI_P2P_RESERVED = 49;
     private static final byte DEFAULT_ID = (byte) 42;
 
     // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
@@ -71,15 +74,18 @@
     // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
     // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
     private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
+    private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
     private final IpPrefix mTetheringPrefix;
     private final ConnectivityManager mConnectivityMgr;
+    private final TetheringConfiguration mConfig;
 
-    public PrivateAddressCoordinator(Context context) {
+    public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
         mDownstreams = new ArraySet<>();
         mUpstreamPrefixMap = new ArrayMap<>();
         mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
         mConnectivityMgr = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
+        mConfig = config;
     }
 
     /**
@@ -141,12 +147,21 @@
         mUpstreamPrefixMap.removeAll(toBeRemoved);
     }
 
+    private boolean isReservedSubnet(final int subnet) {
+        return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED;
+    }
+
     /**
      * Pick a random available address and mark its prefix as in use for the provided IpServer,
      * returns null if there is no available address.
      */
     @Nullable
     public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
+        if (mConfig.shouldEnableWifiP2pDedicatedIp()
+                && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
+            return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
+        }
+
         // Address would be 192.168.[subAddress]/24.
         final byte[] bytes = mTetheringPrefix.getRawAddress();
         final int subAddress = getRandomSubAddr();
@@ -154,7 +169,7 @@
         bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
         for (int i = 0; i < MAX_UBYTE; i++) {
             final int newSubNet = (subNet + i) & BYTE_MASK;
-            if (newSubNet == BLUETOOTH_RESERVED) continue;
+            if (isReservedSubnet(newSubNet)) continue;
 
             bytes[2] = (byte) newSubNet;
             final InetAddress addr;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index cfc6575..7dd5290 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -320,10 +320,13 @@
         mExecutor = new TetheringThreadExecutor(mHandler);
         mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
         mNetdCallback = new NetdCallback();
-        mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext);
 
         // Load tethering configuration.
         updateConfiguration();
+        // It is OK for the configuration to be passed to the PrivateAddressCoordinator at
+        // construction time because the only part of the configuration it uses is
+        // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
+        mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig);
 
         // Must be initialized after tethering configuration is loaded because BpfCoordinator
         // constructor needs to use the configuration.
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index e1771a5..5783805 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -84,6 +84,9 @@
     public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
             "tether_enable_legacy_dhcp_server";
 
+    public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP =
+            "use_legacy_wifi_p2p_dedicated_ip";
+
     /**
      * Default value that used to periodic polls tether offload stats from tethering offload HAL
      * to make the data warnings work.
@@ -113,6 +116,7 @@
     private final int mOffloadPollInterval;
     // TODO: Add to TetheringConfigurationParcel if required.
     private final boolean mEnableBpfOffload;
+    private final boolean mEnableWifiP2pDedicatedIp;
 
     public TetheringConfiguration(Context ctx, SharedLog log, int id) {
         final SharedLog configLog = log.forSubComponent("config");
@@ -156,6 +160,10 @@
                 R.integer.config_tether_offload_poll_interval,
                 DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
 
+        mEnableWifiP2pDedicatedIp = getResourceBoolean(res,
+                R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
+                false /* defaultValue */);
+
         configLog.log(toString());
     }
 
@@ -199,6 +207,11 @@
         return !TextUtils.isEmpty(provisioningAppNoUi);
     }
 
+    /** Check whether dedicated wifi p2p address is enabled. */
+    public boolean shouldEnableWifiP2pDedicatedIp() {
+        return mEnableWifiP2pDedicatedIp;
+    }
+
     /** Does the dumping.*/
     public void dump(PrintWriter pw) {
         pw.print("activeDataSubId: ");
@@ -233,6 +246,9 @@
 
         pw.print("enableLegacyDhcpServer: ");
         pw.println(enableLegacyDhcpServer);
+
+        pw.print("enableWifiP2pDedicatedIp: ");
+        pw.println(mEnableWifiP2pDedicatedIp);
     }
 
     /** Returns the string representation of this object.*/
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 2c0df6f..8e93c2e 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -15,6 +15,11 @@
  */
 package com.android.networkstack.tethering;
 
+import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.Mockito.never;
@@ -54,22 +59,34 @@
     @Mock private IpServer mHotspotIpServer;
     @Mock private IpServer mUsbIpServer;
     @Mock private IpServer mEthernetIpServer;
+    @Mock private IpServer mWifiP2pIpServer;
     @Mock private Context mContext;
     @Mock private ConnectivityManager mConnectivityMgr;
+    @Mock private TetheringConfiguration mConfig;
 
     private PrivateAddressCoordinator mPrivateAddressCoordinator;
     private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
+    private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24");
     private final Network mWifiNetwork = new Network(1);
     private final Network mMobileNetwork = new Network(2);
     private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
 
+    private void setUpIpServers() throws Exception {
+        when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
+        when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET);
+        when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
+        when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
         when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
-        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext));
+        when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
+        setUpIpServers();
+        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
     }
 
     @Test
@@ -256,4 +273,38 @@
         final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
         assertEquals(predefinedPrefix, ethPrefix);
     }
+
+    private int getSubAddress(final byte... ipv4Address) {
+        assertEquals(4, ipv4Address.length);
+
+        int subnet = Byte.toUnsignedInt(ipv4Address[2]);
+        return (subnet << 8) + ipv4Address[3];
+    }
+
+    private void assertReseveredWifiP2pPrefix() throws Exception {
+        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+        final IpPrefix legacyWifiP2pPrefix = PrefixUtils.asIpPrefix(mLegacyWifiP2pAddress);
+        assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+    }
+
+    @Test
+    public void testEnableLegacyWifiP2PAddress() throws Exception {
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+                getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
+        // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix
+        // is resevered.
+        assertReseveredWifiP2pPrefix();
+
+        when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true);
+        assertReseveredWifiP2pPrefix();
+
+        // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
+        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mWifiP2pIpServer);
+        assertEquals(mLegacyWifiP2pAddress, address);
+        mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
+    }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index a9ac4e2..dc0940c 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -128,6 +128,8 @@
                 .thenReturn(new String[0]);
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 false);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
+                .thenReturn(false);
         initializeBpfOffloadConfiguration(true, null /* unset */);
 
         mHasTelephonyManager = true;
@@ -413,4 +415,17 @@
                 R.string.config_mobile_hotspot_provision_response)).thenReturn(
                 PROVISIONING_APP_RESPONSE);
     }
+
+    @Test
+    public void testEnableLegacyWifiP2PAddress() throws Exception {
+        final TetheringConfiguration defaultCfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp());
+
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
+                .thenReturn(true);
+        final TetheringConfiguration testCfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
+    }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 4b6bbac..75c819b 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -18,33 +18,40 @@
 
 import android.app.Notification
 import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
 import android.content.Context
+import android.content.Intent
 import android.content.pm.ActivityInfo
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.content.res.Resources
 import android.net.ConnectivityManager.TETHERING_WIFI
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
 import android.os.Handler
 import android.os.HandlerThread
 import android.os.Looper
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
 import android.os.UserHandle
+import android.provider.Settings
 import android.telephony.TelephonyManager
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
 import com.android.internal.util.test.BroadcastInterceptingContext
+import com.android.networkstack.tethering.TetheringNotificationUpdater.ACTION_DISABLE_TETHERING
 import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
 import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
 import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
 import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
 import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID
 import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.getSettingsPackageName
 import com.android.testutils.waitForIdle
 import org.junit.After
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
 import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Test
@@ -87,12 +94,17 @@
     // every test but should always be initialized before use (or the test should crash).
     private lateinit var context: TestContext
     private lateinit var notificationUpdater: TetheringNotificationUpdater
+
+    // Initializing the following members depends on initializing some of the mocks and
+    // is more logically done in setup().
     private lateinit var fakeTetheringThread: HandlerThread
 
     private val ROAMING_CAPABILITIES = NetworkCapabilities()
     private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING)
     private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general
     private val TIMEOUT_MS = 500L
+    private val ACTIVITY_PENDING_INTENT = 0
+    private val BROADCAST_PENDING_INTENT = 1
 
     private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
         override fun createContextAsUser(user: UserHandle, flags: Int) =
@@ -146,10 +158,43 @@
         fakeTetheringThread.quitSafely()
     }
 
+    private fun verifyActivityPendingIntent(intent: Intent, flags: Int) {
+        // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
+        // the flag in creating arguments). If the described PendingIntent does not already exist,
+        // getActivity() will return null instead of PendingIntent object.
+        val pi = PendingIntent.getActivity(
+                context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                intent,
+                flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE,
+                null /* options */)
+        assertNotNull("Activity PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
+    }
+
+    private fun verifyBroadcastPendingIntent(intent: Intent, flags: Int) {
+        // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
+        // the flag in creating arguments). If the described PendingIntent does not already exist,
+        // getBroadcast() will return null instead of PendingIntent object.
+        val pi = PendingIntent.getBroadcast(
+                context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                intent,
+                flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE)
+        assertNotNull("Broadcast PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
+    }
+
     private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
     private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
 
-    private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) {
+    private fun verifyNotification(
+        iconId: Int,
+        title: String,
+        text: String,
+        id: Int,
+        intentSenderType: Int,
+        intent: Intent,
+        flags: Int
+    ) {
         verify(notificationManager, never()).cancel(any(), eq(id))
 
         val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
@@ -161,6 +206,11 @@
         assertEquals(title, notification.title())
         assertEquals(text, notification.text())
 
+        when (intentSenderType) {
+            ACTIVITY_PENDING_INTENT -> verifyActivityPendingIntent(intent, flags)
+            BROADCAST_PENDING_INTENT -> verifyBroadcastPendingIntent(intent, flags)
+        }
+
         reset(notificationManager)
     }
 
@@ -176,6 +226,10 @@
 
     @Test
     fun testRestrictedNotification() {
+        val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
+                .setPackage(getSettingsPackageName(context.packageManager))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
         // Set test sub id.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
         verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -183,7 +237,7 @@
         // User restrictions on. Show restricted notification.
         notificationUpdater.notifyTetheringDisabledByRestriction()
         verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
-                RESTRICTED_NOTIFICATION_ID)
+                RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
 
         // User restrictions off. Clear notification.
         notificationUpdater.tetheringRestrictionLifted()
@@ -196,7 +250,7 @@
         // User restrictions on again. Show restricted notification.
         notificationUpdater.notifyTetheringDisabledByRestriction()
         verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
-                RESTRICTED_NOTIFICATION_ID)
+                RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
     }
 
     val MAX_BACKOFF_MS = 200L
@@ -234,6 +288,8 @@
 
     @Test
     fun testNoUpstreamNotification() {
+        val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)
+
         // Set test sub id.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
         verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -246,7 +302,8 @@
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // Same capabilities changed. Nothing happened.
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
@@ -260,7 +317,8 @@
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // No downstream.
         notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
@@ -305,6 +363,11 @@
 
     @Test
     fun testRoamingNotification() {
+        val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)
+        val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
+                .setPackage(getSettingsPackageName(context.packageManager))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
         // Set test sub id.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
         verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -316,7 +379,7 @@
         // Upstream capabilities changed to roaming state. Show roaming notification.
         notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
-                ROAMING_NOTIFICATION_ID)
+                ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
 
         // Same capabilities change. Nothing happened.
         notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
@@ -329,14 +392,15 @@
         // Upstream capabilities changed to roaming state again. Show roaming notification.
         notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
-                ROAMING_NOTIFICATION_ID)
+                ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
 
         // No upstream. Clear roaming notification and show no upstream notification.
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // No downstream.
         notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
@@ -347,7 +411,8 @@
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // Set R.bool.config_upstream_roaming_notification to false and change upstream
         // network to roaming state again. No roaming notification.
@@ -363,8 +428,7 @@
         val testSettingsPackageName = "com.android.test.settings"
         val pm = mock(PackageManager::class.java)
         doReturn(null).`when`(pm).resolveActivity(any(), anyInt())
-        assertEquals(defaultSettingsPackageName,
-                TetheringNotificationUpdater.getSettingsPackageName(pm))
+        assertEquals(defaultSettingsPackageName, getSettingsPackageName(pm))
 
         val resolveInfo = ResolveInfo().apply {
             activityInfo = ActivityInfo().apply {
@@ -375,7 +439,6 @@
             }
         }
         doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt())
-        assertEquals(testSettingsPackageName,
-                TetheringNotificationUpdater.getSettingsPackageName(pm))
+        assertEquals(testSettingsPackageName, getSettingsPackageName(pm))
     }
 }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index b0e401b..3f712dd 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -3706,33 +3706,40 @@
     // OS: O
     BACKUP_SETTINGS = 818;
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // ACTION: Picture-in-picture was explicitly entered for an activity
     // VALUE: true if it was entered while hiding as a result of moving to
     // another task, false otherwise
-    ACTION_PICTURE_IN_PICTURE_ENTERED = 819;
+    ACTION_PICTURE_IN_PICTURE_ENTERED = 819 [deprecated=true];
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // ACTION: The activity currently in picture-in-picture was expanded back to fullscreen
     // PACKAGE: The package name of the activity that was expanded back to fullscreen
-    ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820;
+    ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820 [deprecated=true];
 
+    // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent.
     // ACTION: The activity currently in picture-in-picture was minimized
     // VALUE: True if the PiP was minimized, false otherwise
-    ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821;
+    ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821 [deprecated=true];
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // ACTION: Picture-in-picture was dismissed via the dismiss button
     // VALUE: 0 if dismissed by tap, 1 if dismissed by drag
-    ACTION_PICTURE_IN_PICTURE_DISMISSED = 822;
+    ACTION_PICTURE_IN_PICTURE_DISMISSED = 822 [deprecated=true];
 
-    // ACTION: The visibility of the picture-in-picture meny
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
+    // ACTION: The visibility of the picture-in-picture menu
     // VALUE: Whether or not the menu is visible
-    ACTION_PICTURE_IN_PICTURE_MENU = 823;
+    ACTION_PICTURE_IN_PICTURE_MENU = 823 [deprecated=true];
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // Enclosing category for group of PICTURE_IN_PICTURE_ASPECT_RATIO_FOO events,
     // logged when the aspect ratio changes
-    ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824;
+    ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824 [deprecated=true];
 
+    // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent.
     // The current aspect ratio of the PiP, logged when it changes.
-    PICTURE_IN_PICTURE_ASPECT_RATIO = 825;
+    PICTURE_IN_PICTURE_ASPECT_RATIO = 825 [deprecated=true];
 
     // FIELD - length in dp of ACTION_LS_* gestures, or zero if not applicable
     // CATEGORY: GLOBAL_SYSTEM_UI
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 9ad808a..a167ab1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -148,6 +148,8 @@
 
     private boolean mRequestMultiFingerGestures;
 
+    private boolean mRequestTwoFingerPassthrough;
+
     boolean mRequestFilterKeyEvents;
 
     boolean mRetrieveInteractiveWindows;
@@ -325,8 +327,10 @@
                 & AccessibilityServiceInfo.FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0;
         mRequestMultiFingerGestures = (info.flags
                 & AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0;
-        mRequestFilterKeyEvents = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+        mRequestTwoFingerPassthrough =
+                (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0;
+        mRequestFilterKeyEvents =
+                (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
         mRetrieveInteractiveWindows = (info.flags
                 & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
         mCaptureFingerprintGestures = (info.flags
@@ -1772,6 +1776,10 @@
         return mRequestMultiFingerGestures;
     }
 
+    public boolean isTwoFingerPassthroughEnabled() {
+        return mRequestTwoFingerPassthrough;
+    }
+
     @Override
     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
         mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 8b40f61..cd9ab8d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -118,6 +118,13 @@
      */
     static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100;
 
+    /**
+     * Flag for enabling multi-finger gestures.
+     *
+     * @see #setUserAndEnabledFeatures(int, int)
+     */
+    static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200;
+
     static final int FEATURES_AFFECTING_MOTION_EVENTS =
             FLAG_FEATURE_INJECT_MOTION_EVENTS
                     | FLAG_FEATURE_AUTOCLICK
@@ -125,7 +132,8 @@
                     | FLAG_FEATURE_SCREEN_MAGNIFIER
                     | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
                     | FLAG_SERVICE_HANDLES_DOUBLE_TAP
-                    | FLAG_REQUEST_MULTI_FINGER_GESTURES;
+                    | FLAG_REQUEST_MULTI_FINGER_GESTURES
+                    | FLAG_REQUEST_2_FINGER_PASSTHROUGH;
 
     private final Context mContext;
 
@@ -421,6 +429,9 @@
                 if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
                     explorer.setMultiFingerGesturesEnabled(true);
                 }
+                if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
+                    explorer.setTwoFingerPassthroughEnabled(true);
+                }
                 addFirstEventHandler(displayId, explorer);
                 mTouchExplorer.put(displayId, explorer);
             }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2cd4c69..833aeec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -443,7 +443,11 @@
                     if (reboundAService || configurationChanged) {
                         onUserStateChangedLocked(userState);
                     }
-                    migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName);
+                    // Passing 0 for restoreFromSdkInt to have this migration check execute each
+                    // time. It can make sure a11y button settings are correctly if there's an a11y
+                    // service updated and modifies the a11y button configuration.
+                    migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName,
+                            /* restoreFromSdkInt = */0);
                 }
             }
 
@@ -554,7 +558,9 @@
                         synchronized (mLock) {
                             restoreEnabledAccessibilityServicesLocked(
                                     intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE),
-                                    intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE));
+                                    intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE),
+                                    intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT,
+                                            0));
                         }
                     } else if (ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED.equals(which)) {
                         synchronized (mLock) {
@@ -563,6 +569,12 @@
                                     intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT,
                                             0));
                         }
+                    } else if (Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS.equals(which)) {
+                        synchronized (mLock) {
+                            restoreAccessibilityButtonTargetsLocked(
+                                    intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE),
+                                    intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE));
+                        }
                     }
                 }
             }
@@ -588,7 +600,7 @@
         final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
         final Set<String> targetsFromSetting = new ArraySet<>();
         readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                userState.mUserId, targetsFromSetting, str -> str);
+                userState.mUserId, str -> str, targetsFromSetting);
         final boolean targetsContainMagnification = targetsFromSetting.contains(
                 MAGNIFICATION_CONTROLLER_NAME);
         if (targetsContainMagnification == displayMagnificationNavBarEnabled) {
@@ -1189,7 +1201,14 @@
             // the state since the context in which the current user
             // state was used has changed since it was inactive.
             onUserStateChangedLocked(userState);
-            migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null);
+            // It's better to have this migration in SettingsProvider. Unfortunately,
+            // SettingsProvider migrated database in a very early stage which A11yManagerService
+            // haven't finished or started the initialization. We cannot get enough information from
+            // A11yManagerService to execute these migrations in SettingsProvider. Passing 0 for
+            // restoreFromSdkInt to have this migration check execute every time, because we did not
+            // find out a way to detect the device finished the OTA and switch the user.
+            migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null,
+                    /* restoreFromSdkInt = */0);
 
             if (announceNewUser) {
                 // Schedule announcement of the current user if needed.
@@ -1234,7 +1253,8 @@
 
     // Called only during settings restore; currently supports only the owner user
     // TODO: http://b/22388012
-    void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting) {
+    void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting,
+            int restoreFromSdkInt) {
         readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
         readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
 
@@ -1246,7 +1266,32 @@
                 userState.mEnabledServices,
                 UserHandle.USER_SYSTEM);
         onUserStateChangedLocked(userState);
-        migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null);
+        migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null, restoreFromSdkInt);
+    }
+
+    /**
+     * User could enable accessibility services and configure accessibility button during the SUW.
+     * Merges current value of accessibility button settings into the restored one to make sure
+     * user's preferences of accessibility button updated in SUW are not lost.
+     *
+     * Called only during settings restore; currently supports only the owner user
+     * TODO: http://b/22388012
+     */
+    void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) {
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting,
+                /* doMerge = */false);
+        readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting,
+                /* doMerge = */true);
+
+        final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+        userState.mAccessibilityButtonTargets.clear();
+        userState.mAccessibilityButtonTargets.addAll(targetsFromSetting);
+        persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+                UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str);
+
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+        onUserStateChangedLocked(userState);
     }
 
     private int getClientStateLocked(AccessibilityUserState userState) {
@@ -1569,8 +1614,8 @@
      */
     private void readComponentNamesFromSettingLocked(String settingName, int userId,
             Set<ComponentName> outComponentNames) {
-        readColonDelimitedSettingToSet(settingName, userId, outComponentNames,
-                str -> ComponentName.unflattenFromString(str));
+        readColonDelimitedSettingToSet(settingName, userId,
+                str -> ComponentName.unflattenFromString(str), outComponentNames);
     }
 
     /**
@@ -1585,8 +1630,8 @@
     private void readComponentNamesFromStringLocked(String names,
             Set<ComponentName> outComponentNames,
             boolean doMerge) {
-        readColonDelimitedStringToSet(names, outComponentNames, doMerge,
-                str -> ComponentName.unflattenFromString(str));
+        readColonDelimitedStringToSet(names, str -> ComponentName.unflattenFromString(str),
+                outComponentNames, doMerge);
     }
 
     @Override
@@ -1596,15 +1641,15 @@
                 componentName -> componentName.flattenToShortString());
     }
 
-    private <T> void readColonDelimitedSettingToSet(String settingName, int userId, Set<T> outSet,
-            Function<String, T> toItem) {
+    private <T> void readColonDelimitedSettingToSet(String settingName, int userId,
+            Function<String, T> toItem, Set<T> outSet) {
         final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 settingName, userId);
-        readColonDelimitedStringToSet(settingValue, outSet, false, toItem);
+        readColonDelimitedStringToSet(settingValue, toItem, outSet, false);
     }
 
-    private <T> void readColonDelimitedStringToSet(String names, Set<T> outSet, boolean doMerge,
-            Function<String, T> toItem) {
+    private <T> void readColonDelimitedStringToSet(String names, Function<String, T> toItem,
+            Set<T> outSet, boolean doMerge) {
         if (!doMerge) {
             outSet.clear();
         }
@@ -1792,6 +1837,9 @@
                 if (userState.isMultiFingerGesturesEnabledLocked()) {
                     flags |= AccessibilityInputFilter.FLAG_REQUEST_MULTI_FINGER_GESTURES;
                 }
+                if (userState.isTwoFingerPassthroughEnabledLocked()) {
+                    flags |= AccessibilityInputFilter.FLAG_REQUEST_2_FINGER_PASSTHROUGH;
+                }
             }
             if (userState.isFilterKeyEventsEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
@@ -2075,6 +2123,7 @@
         boolean touchExplorationEnabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
         boolean serviceHandlesDoubleTapEnabled = false;
         boolean requestMultiFingerGestures = false;
+        boolean requestTwoFingerPassthrough = false;
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -2082,6 +2131,7 @@
                 touchExplorationEnabled = true;
                 serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled();
                 requestMultiFingerGestures = service.isMultiFingerGesturesEnabled();
+                requestTwoFingerPassthrough = service.isTwoFingerPassthroughEnabled();
                 break;
             }
         }
@@ -2098,13 +2148,14 @@
         }
         userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled);
         userState.setMultiFingerGesturesLocked(requestMultiFingerGestures);
+        userState.setTwoFingerPassthroughLocked(requestTwoFingerPassthrough);
     }
 
     private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
         final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
         final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str);
+        readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
         // Fall back to device's default a11y service, only when setting is never updated.
         if (settingValue == null) {
             final String defaultService = mContext.getString(
@@ -2128,7 +2179,7 @@
     private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
         final Set<String> targetsFromSetting = new ArraySet<>();
         readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                userState.mUserId, targetsFromSetting, str -> str);
+                userState.mUserId, str -> str, targetsFromSetting);
 
         final Set<String> currentTargets =
                 userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
@@ -2392,9 +2443,15 @@
      *    (It happens when an enabled accessibility service package is upgraded.)
      *
      * @param packageName The package name to check, or {@code null} to check all services.
+     * @param restoreFromSdkInt The target sdk version of the restored source device, or {@code 0}
+     *                          if the caller is not related to the restore.
      */
     private void migrateAccessibilityButtonSettingsIfNecessaryLocked(
-            AccessibilityUserState userState, @Nullable String packageName) {
+            AccessibilityUserState userState, @Nullable String packageName, int restoreFromSdkInt) {
+        // No need to migrate settings if they are restored from a version after Q.
+        if (restoreFromSdkInt > Build.VERSION_CODES.Q) {
+            return;
+        }
         final Set<String> buttonTargets =
                 userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
         int lastSize = buttonTargets.size();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index f865aa7..4c9e444 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -110,6 +110,7 @@
     private boolean mIsTouchExplorationEnabled;
     private boolean mServiceHandlesDoubleTap;
     private boolean mRequestMultiFingerGestures;
+    private boolean mRequestTwoFingerPassthrough;
     private int mUserInteractiveUiTimeout;
     private int mUserNonInteractiveUiTimeout;
     private int mNonInteractiveUiTimeout = 0;
@@ -169,6 +170,7 @@
         mIsTouchExplorationEnabled = false;
         mServiceHandlesDoubleTap = false;
         mRequestMultiFingerGestures = false;
+        mRequestTwoFingerPassthrough = false;
         mIsDisplayMagnificationEnabled = false;
         mIsAutoclickEnabled = false;
         mUserNonInteractiveUiTimeout = 0;
@@ -456,6 +458,8 @@
                 .append(String.valueOf(mServiceHandlesDoubleTap));
         pw.append(", requestMultiFingerGestures=")
                 .append(String.valueOf(mRequestMultiFingerGestures));
+        pw.append(", requestTwoFingerPassthrough=")
+                .append(String.valueOf(mRequestTwoFingerPassthrough));
         pw.append(", displayMagnificationEnabled=").append(String.valueOf(
                 mIsDisplayMagnificationEnabled));
         pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
@@ -790,6 +794,14 @@
     public void setMultiFingerGesturesLocked(boolean enabled) {
         mRequestMultiFingerGestures = enabled;
     }
+    public boolean isTwoFingerPassthroughEnabledLocked() {
+        return mRequestTwoFingerPassthrough;
+    }
+
+    public void setTwoFingerPassthroughLocked(boolean enabled) {
+        mRequestTwoFingerPassthrough = enabled;
+    }
+
 
     public int getUserInteractiveUiTimeoutLocked() {
         return mUserInteractiveUiTimeout;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index e9c70c6..8604fe7 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -94,8 +94,13 @@
     private boolean mServiceHandlesDoubleTap = false;
     // Whether multi-finger gestures are enabled.
     boolean mMultiFingerGesturesEnabled;
+    // Whether the two-finger passthrough is enabled when multi-finger gestures are enabled.
+    private boolean mTwoFingerPassthroughEnabled;
     // A list of all the multi-finger gestures, for easy adding and removal.
     private final List<GestureMatcher> mMultiFingerGestures = new ArrayList<>();
+    // A list of two-finger swipes, for easy adding and removal when turning on or off two-finger
+    // passthrough.
+    private final List<GestureMatcher> mTwoFingerSwipes = new ArrayList<>();
     // Shared state information.
     private TouchState mState;
 
@@ -105,6 +110,7 @@
         mListener = listener;
         mState = state;
         mMultiFingerGesturesEnabled = false;
+        mTwoFingerPassthroughEnabled = false;
         // Set up gestures.
         // Start with double tap.
         mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this));
@@ -161,14 +167,14 @@
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this));
         // Two-finger swipes.
-        mMultiFingerGestures.add(
+        mTwoFingerSwipes.add(
                 new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this));
-        mMultiFingerGestures.add(
+        mTwoFingerSwipes.add(
                 new MultiFingerSwipe(context, 2, LEFT, GESTURE_2_FINGER_SWIPE_LEFT, this));
-        mMultiFingerGestures.add(
+        mTwoFingerSwipes.add(
                 new MultiFingerSwipe(context, 2, RIGHT, GESTURE_2_FINGER_SWIPE_RIGHT, this));
-        mMultiFingerGestures.add(
-                new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
+        mTwoFingerSwipes.add(new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
+        mMultiFingerGestures.addAll(mTwoFingerSwipes);
         // Three-finger swipes.
         mMultiFingerGestures.add(
                 new MultiFingerSwipe(context, 3, DOWN, GESTURE_3_FINGER_SWIPE_DOWN, this));
@@ -360,6 +366,25 @@
         }
     }
 
+    public boolean isTwoFingerPassthroughEnabled() {
+        return mTwoFingerPassthroughEnabled;
+    }
+
+    public void setTwoFingerPassthroughEnabled(boolean mode) {
+        if (mTwoFingerPassthroughEnabled != mode) {
+            mTwoFingerPassthroughEnabled = mode;
+            if (!mode) {
+                mMultiFingerGestures.addAll(mTwoFingerSwipes);
+                if (mMultiFingerGesturesEnabled) {
+                    mGestures.addAll(mTwoFingerSwipes);
+                }
+            } else {
+                mMultiFingerGestures.removeAll(mTwoFingerSwipes);
+                mGestures.removeAll(mTwoFingerSwipes);
+            }
+        }
+    }
+
     public void setServiceHandlesDoubleTap(boolean mode) {
         mServiceHandlesDoubleTap = mode;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 696702f..c45da86 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -41,6 +41,7 @@
 import android.content.Context;
 import android.graphics.Region;
 import android.os.Handler;
+import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -91,12 +92,21 @@
     // The timeout after which we are no longer trying to detect a gesture.
     private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
 
+    // The height of the top and bottom edges for  edge-swipes.
+    // For now this is only used to allow three-finger edge-swipes from the bottom.
+    private static final float EDGE_SWIPE_HEIGHT_CM = 0.25f;
+
+    // The calculated edge height for the top and bottom edges.
+    private final float mEdgeSwipeHeightPixels;
     // Timeout before trying to decide what the user is trying to do.
     private final int mDetermineUserIntentTimeout;
 
     // Slop between the first and second tap to be a double tap.
     private final int mDoubleTapSlop;
 
+    // Slop to move before being considered a move rather than a tap.
+    private final int mTouchSlop;
+
     // The current state of the touch explorer.
     private TouchState mState;
 
@@ -174,6 +184,9 @@
         mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState);
         mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
         mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
+        mEdgeSwipeHeightPixels = metrics.ydpi / GestureUtils.CM_PER_INCH * EDGE_SWIPE_HEIGHT_CM;
         mHandler = mainHandler;
         mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
         mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
@@ -219,16 +232,10 @@
         if (mState.isTouchExploring()) {
             // If a touch exploration gesture is in progress send events for its end.
             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
-        }  else if (mState.isDragging()) {
-            mDraggingPointerId = INVALID_POINTER_ID;
-            // Send exit to all pointers that we have delivered.
-            mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
-        } else if (mState.isDelegating()) {
-            // Send exit to all pointers that we have delivered.
-            mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
-        } else if (mState.isGestureDetecting()) {
-            // No state specific cleanup required.
         }
+        mDraggingPointerId = INVALID_POINTER_ID;
+        // Send exit to any pointers that we have delivered as part of delegating or dragging.
+        mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
         // Remove all pending callbacks.
         mSendHoverEnterAndMoveDelayed.cancel();
         mSendHoverExitDelayed.cancel();
@@ -554,7 +561,26 @@
             // stream consistent.
             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
         }
+        if (mGestureDetector.isMultiFingerGesturesEnabled()
+                && mGestureDetector.isTwoFingerPassthroughEnabled()) {
+            if (event.getPointerCount() == 3) {
+                boolean isOnBottomEdge = false;
+                // If three fingers go down on the bottom edge of the screen, delegate immediately.
+                final long screenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
+                for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) {
+                    if (mReceivedPointerTracker.getReceivedPointerDownY(i)
+                            > (screenHeight - mEdgeSwipeHeightPixels)) {
+                        isOnBottomEdge = true;
+                    }
+                }
+                if (isOnBottomEdge) {
+                    mState.startDelegating();
+                    mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
+                }
+            }
+        }
     }
+
     /**
      * Handles ACTION_MOVE while in the touch interacting state. This is where transitions to
      * delegating and dragging states are handled.
@@ -563,7 +589,7 @@
             MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
         final int pointerIndex = event.findPointerIndex(pointerId);
-        final int pointerIdBits = (1 << pointerId);
+        int pointerIdBits = (1 << pointerId);
         switch (event.getPointerCount()) {
             case 1:
                 // We have not started sending events since we try to
@@ -574,12 +600,29 @@
                 }
                 break;
             case 2:
-                if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+                if (mGestureDetector.isMultiFingerGesturesEnabled()
+                        && !mGestureDetector.isTwoFingerPassthroughEnabled()) {
                     return;
                 }
                 // Make sure we don't have any pending transitions to touch exploration
                 mSendHoverEnterAndMoveDelayed.cancel();
                 mSendHoverExitDelayed.cancel();
+                if (mGestureDetector.isMultiFingerGesturesEnabled()
+                        && mGestureDetector.isTwoFingerPassthroughEnabled()) {
+                    if (pointerIndex < 0) {
+                        return;
+                    }
+                    final float deltaX =
+                            mReceivedPointerTracker.getReceivedPointerDownX(pointerId)
+                                    - rawEvent.getX(pointerIndex);
+                    final float deltaY =
+                            mReceivedPointerTracker.getReceivedPointerDownY(pointerId)
+                                    - rawEvent.getY(pointerIndex);
+                    final double moveDelta = Math.hypot(deltaX, deltaY);
+                    if (moveDelta < mTouchSlop) {
+                        return;
+                    }
+                }
                 // More than one pointer so the user is not touch exploring
                 // and now we have to decide whether to delegate or drag.
                 // Remove move history before send injected non-move events
@@ -588,8 +631,8 @@
                     // Two pointers moving in the same direction within
                     // a given distance perform a drag.
                     mState.startDragging();
-                    mDraggingPointerId = pointerId;
                     adjustEventLocationForDrag(event);
+                    pointerIdBits = 1 << mDraggingPointerId;
                     event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
                     mDispatcher.sendMotionEvent(
                             event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
@@ -648,7 +691,8 @@
                         event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
                 break;
             case 2:
-                if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+                if (mGestureDetector.isMultiFingerGesturesEnabled()
+                        && !mGestureDetector.isTwoFingerPassthroughEnabled()) {
                     return;
                 }
                 if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -703,7 +747,8 @@
      */
     private void handleMotionEventStateDragging(
             MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+        if (mGestureDetector.isMultiFingerGesturesEnabled()
+                && !mGestureDetector.isTwoFingerPassthroughEnabled()) {
             // Multi-finger gestures conflict with this functionality.
             return;
         }
@@ -756,6 +801,7 @@
                             // The two pointers are moving either in different directions or
                             // no close enough => delegate the gesture to the view hierarchy.
                             mState.startDelegating();
+                            mDraggingPointerId = INVALID_POINTER_ID;
                             // Remove move history before send injected non-move events
                             event = MotionEvent.obtainNoHistory(event);
                             // Send an event to the end of the drag gesture.
@@ -767,6 +813,7 @@
                         break;
                     default:
                         mState.startDelegating();
+                        mDraggingPointerId = INVALID_POINTER_ID;
                         event = MotionEvent.obtainNoHistory(event);
                         // Send an event to the end of the drag gesture.
                         mDispatcher.sendMotionEvent(
@@ -784,15 +831,16 @@
                 }
                 break;
             case ACTION_UP:
-                mAms.onTouchInteractionEnd();
-                // Announce the end of a new touch interaction.
-                mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
                 if (event.getPointerId(GestureUtils.getActionIndex(event)) == mDraggingPointerId) {
                     mDraggingPointerId = INVALID_POINTER_ID;
                     // Send an event to the end of the drag gesture.
                     mDispatcher.sendMotionEvent(
                             event, ACTION_UP, rawEvent, pointerIdBits, policyFlags);
                 }
+                mAms.onTouchInteractionEnd();
+                // Announce the end of a new touch interaction.
+                mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+
                 break;
         }
     }
@@ -911,21 +959,62 @@
     }
 
     /**
-     * Adjust the location of an injected event when performing a drag The new location will be in
-     * between the two fingers touching the screen.
+     * Adjust the location of an injected event when performing a drag. The location will be the
+     * location of the finger closest to an edge of the screen.
      */
     private void adjustEventLocationForDrag(MotionEvent event) {
-
         final float firstPtrX = event.getX(0);
         final float firstPtrY = event.getY(0);
+        final int firstPtrId = event.getPointerId(0);
         final float secondPtrX = event.getX(1);
         final float secondPtrY = event.getY(1);
-        final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
-        final float deltaX =
-                (pointerIndex == 0) ? (secondPtrX - firstPtrX) : (firstPtrX - secondPtrX);
-        final float deltaY =
-                (pointerIndex == 0) ? (secondPtrY - firstPtrY) : (firstPtrY - secondPtrY);
-        event.offsetLocation(deltaX / 2, deltaY / 2);
+        final int secondPtrId = event.getPointerId(1);
+        float draggingX = firstPtrX;
+        float draggingY = firstPtrY;
+        if (mDraggingPointerId != INVALID_POINTER_ID) {
+            // Just use the coordinates of the dragging pointer.
+            int pointerIndex = event.findPointerIndex(mDraggingPointerId);
+            if (pointerIndex >= 0) {
+                draggingX = event.getX(pointerIndex);
+                draggingY = event.getY(pointerIndex);
+            } else {
+                // We've lost track of the dragging pointer. Try to recover by invalidating it.
+                // We'll the drop into the code below to choose a new one.
+                mDraggingPointerId = INVALID_POINTER_ID;
+            }
+        }
+        // Not quite an else, since the above code can invalidate the pointer
+        if (mDraggingPointerId == INVALID_POINTER_ID) {
+            // The goal is to use the coordinates of the finger that is closest to its closest edge.
+            if (getDistanceToClosestEdge(firstPtrX, firstPtrY)
+                    < getDistanceToClosestEdge(secondPtrX, secondPtrY)) {
+                // X and Y initialized to firstPtrX and Y was right
+                mDraggingPointerId = firstPtrId;
+            } else {
+                draggingX = secondPtrX;
+                draggingY = secondPtrY;
+                mDraggingPointerId = secondPtrId;
+            }
+        }
+        event.setLocation(draggingX, draggingY);
+    }
+
+    private float getDistanceToClosestEdge(float x, float y) {
+        final long width = mContext.getResources().getDisplayMetrics().widthPixels;
+        final long height = mContext.getResources().getDisplayMetrics().heightPixels;
+        float distance = Float.MAX_VALUE;
+        if (x < (width - x)) {
+            distance = x;
+        } else {
+            distance = width - x;
+        }
+        if (distance > y) {
+            distance = y;
+        }
+        if (distance > (height - y)) {
+            distance = (height - y);
+        }
+        return distance;
     }
 
     public TouchState getState() {
@@ -954,6 +1043,13 @@
         mGestureDetector.setMultiFingerGesturesEnabled(enabled);
     }
 
+    /**
+     * This function turns on and off two-finger passthrough gestures such as drag and pinch when
+     * multi-finger gestures are enabled.
+     */
+    public void setTwoFingerPassthroughEnabled(boolean enabled) {
+        mGestureDetector.setTwoFingerPassthroughEnabled(enabled);
+    }
     public void setGestureDetectionPassthroughRegion(Region region) {
         mGestureDetectionPassthroughRegion = region;
     }
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/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 12e6e10..186812b 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1239,9 +1239,10 @@
 
     @Override
     public IRestoreSession beginRestoreSessionForUser(
-            int userId, String packageName, String transportID) throws RemoteException {
+            int userId, String packageName, String transportID,
+            @OperationType int operationType) throws RemoteException {
         return isUserReadyForBackup(userId)
-                ? beginRestoreSession(userId, packageName, transportID) : null;
+                ? beginRestoreSession(userId, packageName, transportID, operationType) : null;
     }
 
     /**
@@ -1250,13 +1251,15 @@
      */
     @Nullable
     public IRestoreSession beginRestoreSession(
-            @UserIdInt int userId, String packageName, String transportName) {
+            @UserIdInt int userId, String packageName, String transportName,
+            @OperationType int operationType) {
         UserBackupManagerService userBackupManagerService =
                 getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
 
         return userBackupManagerService == null
                 ? null
-                : userBackupManagerService.beginRestoreSession(packageName, transportName);
+                : userBackupManagerService.beginRestoreSession(packageName, transportName,
+                        operationType);
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index c9b09e3..0855b9d 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -10,6 +10,7 @@
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
+import android.app.backup.BackupManager;
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
 import android.app.backup.IBackupCallback;
@@ -146,7 +147,8 @@
     private IBackupAgent bindToAgent(ApplicationInfo targetApp) {
         try {
             return mBackupManagerService.bindToAgentSynchronous(targetApp,
-                    ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
+                    ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL,
+                    BackupManager.OperationType.BACKUP);
         } catch (SecurityException e) {
             Slog.e(TAG, "error in binding to agent for package " + targetApp.packageName
                     + ". " + e);
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 3ab81cb..29235dd 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1651,13 +1651,15 @@
 
     /** Fires off a backup agent, blocking until it attaches or times out. */
     @Nullable
-    public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
+    public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode,
+            @OperationType int operationType) {
         IBackupAgent agent = null;
         synchronized (mAgentConnectLock) {
             mConnecting = true;
             mConnectedAgent = null;
             try {
-                if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId)) {
+                if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId,
+                        operationType)) {
                     Slog.d(TAG, addUserIdToLogMessage(mUserId, "awaiting agent for " + app));
 
                     // success; wait for the agent to arrive
@@ -3973,7 +3975,8 @@
                                 restoreSet,
                                 packageName,
                                 token,
-                                listener);
+                                listener,
+                                mScheduledBackupEligibility);
                 mBackupHandler.sendMessage(msg);
             } catch (Exception e) {
                 // Calling into the transport broke; back off and proceed with the installation.
@@ -4002,13 +4005,15 @@
     }
 
     /** Hand off a restore session. */
-    public IRestoreSession beginRestoreSession(String packageName, String transport) {
+    public IRestoreSession beginRestoreSession(String packageName, String transport,
+            @OperationType int operationType) {
         if (DEBUG) {
             Slog.v(
                     TAG,
                     addUserIdToLogMessage(
                             mUserId,
-                            "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
+                            "beginRestoreSession: pkg=" + packageName + " transport=" + transport
+                                + "operationType=" + operationType));
         }
 
         boolean needPermission = true;
@@ -4065,7 +4070,8 @@
                                 "Restore session requested but currently running backups"));
                 return null;
             }
-            mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
+            mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport,
+                    getEligibilityRulesForOperation(operationType));
             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
                     mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
         }
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 846c6a2..fe5497f 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -41,6 +41,7 @@
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.remote.RemoteCall;
+import com.android.server.backup.utils.BackupEligibilityRules;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import java.io.File;
@@ -64,6 +65,7 @@
     private final int mOpToken;
     private final int mTransportFlags;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+    private final BackupEligibilityRules mBackupEligibilityRules;
 
     class FullBackupRunner implements Runnable {
         private final @UserIdInt int mUserId;
@@ -190,7 +192,8 @@
             BackupRestoreTask timeoutMonitor,
             long quota,
             int opToken,
-            int transportFlags) {
+            int transportFlags,
+            BackupEligibilityRules backupEligibilityRules) {
         this.backupManagerService = backupManagerService;
         mOutput = output;
         mPreflightHook = preflightHook;
@@ -204,6 +207,7 @@
                 Objects.requireNonNull(
                         backupManagerService.getAgentTimeoutParameters(),
                         "Timeout parameters cannot be null");
+        mBackupEligibilityRules = backupEligibilityRules;
     }
 
     public int preflightCheck() throws RemoteException {
@@ -302,7 +306,8 @@
             }
             mAgent =
                     backupManagerService.bindToAgentSynchronous(
-                            mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL);
+                            mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL,
+                            mBackupEligibilityRules.getOperationType());
         }
         return mAgent != null;
     }
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 0a11774..448e086 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -421,7 +421,8 @@
                                 this,
                                 Long.MAX_VALUE,
                                 mCurrentOpToken,
-                                /*transportFlags=*/ 0);
+                                /*transportFlags=*/ 0,
+                                mBackupEligibilityRules);
                 sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
 
                 // Don't need to check preflight result as there is no preflight hook.
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 1fa8892..a4d47d4 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -860,7 +860,8 @@
                             this,
                             mQuota,
                             mCurrentOpToken,
-                            mTransportFlags);
+                            mTransportFlags,
+                            mBackupEligibilityRules);
             try {
                 try {
                     if (!mIsCancelled) {
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 1bb4349..100dbae 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -305,8 +305,7 @@
                                 params.isSystemRestore,
                                 params.filterSet,
                                 params.listener,
-                                backupManagerService.getEligibilityRulesForOperation(
-                                        OperationType.BACKUP));
+                                params.backupEligibilityRules);
 
                 synchronized (backupManagerService.getPendingRestores()) {
                     if (backupManagerService.isRestoreInProgress()) {
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 6124171..7267cdf 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -731,7 +731,8 @@
         try {
             agent =
                     mBackupManagerService.bindToAgentSynchronous(
-                            packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL);
+                            packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL,
+                            mBackupEligibilityRules.getOperationType());
             if (agent == null) {
                 mReporter.onAgentError(packageName);
                 throw AgentException.transitory();
diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java
index a6fea6c..a08a1f8 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java
@@ -23,6 +23,7 @@
 
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.utils.BackupEligibilityRules;
 
 import java.util.Map;
 import java.util.Set;
@@ -37,6 +38,7 @@
     public final boolean isSystemRestore;
     @Nullable public final String[] filterSet;
     public final OnTaskFinishedListener listener;
+    public final BackupEligibilityRules backupEligibilityRules;
 
     /**
      * No kill after restore.
@@ -47,7 +49,8 @@
             IBackupManagerMonitor monitor,
             long token,
             PackageInfo packageInfo,
-            OnTaskFinishedListener listener) {
+            OnTaskFinishedListener listener,
+            BackupEligibilityRules eligibilityRules) {
         return new RestoreParams(
                 transportClient,
                 observer,
@@ -57,7 +60,8 @@
                 /* pmToken */ 0,
                 /* isSystemRestore */ false,
                 /* filterSet */ null,
-                listener);
+                listener,
+                eligibilityRules);
     }
 
     /**
@@ -70,7 +74,8 @@
             long token,
             String packageName,
             int pmToken,
-            OnTaskFinishedListener listener) {
+            OnTaskFinishedListener listener,
+            BackupEligibilityRules backupEligibilityRules) {
         String[] filterSet = {packageName};
         return new RestoreParams(
                 transportClient,
@@ -81,7 +86,8 @@
                 pmToken,
                 /* isSystemRestore */ false,
                 filterSet,
-                listener);
+                listener,
+                backupEligibilityRules);
     }
 
     /**
@@ -92,7 +98,8 @@
             IRestoreObserver observer,
             IBackupManagerMonitor monitor,
             long token,
-            OnTaskFinishedListener listener) {
+            OnTaskFinishedListener listener,
+            BackupEligibilityRules backupEligibilityRules) {
         return new RestoreParams(
                 transportClient,
                 observer,
@@ -102,7 +109,8 @@
                 /* pmToken */ 0,
                 /* isSystemRestore */ true,
                 /* filterSet */ null,
-                listener);
+                listener,
+                backupEligibilityRules);
     }
 
     /**
@@ -115,7 +123,8 @@
             long token,
             String[] filterSet,
             boolean isSystemRestore,
-            OnTaskFinishedListener listener) {
+            OnTaskFinishedListener listener,
+            BackupEligibilityRules backupEligibilityRules) {
         return new RestoreParams(
                 transportClient,
                 observer,
@@ -125,7 +134,8 @@
                 /* pmToken */ 0,
                 isSystemRestore,
                 filterSet,
-                listener);
+                listener,
+                backupEligibilityRules);
     }
 
     private RestoreParams(
@@ -137,7 +147,8 @@
             int pmToken,
             boolean isSystemRestore,
             @Nullable String[] filterSet,
-            OnTaskFinishedListener listener) {
+            OnTaskFinishedListener listener,
+            BackupEligibilityRules backupEligibilityRules) {
         this.transportClient = transportClient;
         this.observer = observer;
         this.monitor = monitor;
@@ -147,5 +158,6 @@
         this.isSystemRestore = isSystemRestore;
         this.filterSet = filterSet;
         this.listener = listener;
+        this.backupEligibilityRules = backupEligibilityRules;
     }
 }
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 5a57cdc..3102b5f 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -42,6 +42,7 @@
 import com.android.server.backup.params.RestoreGetSetsParams;
 import com.android.server.backup.params.RestoreParams;
 import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.utils.BackupEligibilityRules;
 
 import java.util.function.BiFunction;
 
@@ -55,6 +56,7 @@
     private final String mTransportName;
     private final UserBackupManagerService mBackupManagerService;
     private final int mUserId;
+    private final BackupEligibilityRules mBackupEligibilityRules;
     @Nullable private final String mPackageName;
     public RestoreSet[] mRestoreSets = null;
     boolean mEnded = false;
@@ -63,12 +65,14 @@
     public ActiveRestoreSession(
             UserBackupManagerService backupManagerService,
             @Nullable String packageName,
-            String transportName) {
+            String transportName,
+            BackupEligibilityRules backupEligibilityRules) {
         mBackupManagerService = backupManagerService;
         mPackageName = packageName;
         mTransportManager = backupManagerService.getTransportManager();
         mTransportName = transportName;
         mUserId = backupManagerService.getUserId();
+        mBackupEligibilityRules = backupEligibilityRules;
     }
 
     public void markTimedOut() {
@@ -178,7 +182,8 @@
                                                 observer,
                                                 monitor,
                                                 token,
-                                                listener),
+                                                listener,
+                                                mBackupEligibilityRules),
                                 "RestoreSession.restoreAll()");
                     } finally {
                         Binder.restoreCallingIdentity(oldId);
@@ -271,7 +276,8 @@
                                                 token,
                                                 packages,
                                                 /* isSystemRestore */ packages.length > 1,
-                                                listener),
+                                                listener,
+                                                mBackupEligibilityRules),
                                 "RestoreSession.restorePackages(" + packages.length + " packages)");
                     } finally {
                         Binder.restoreCallingIdentity(oldId);
@@ -363,7 +369,8 @@
                                     monitor,
                                     token,
                                     app,
-                                    listener),
+                                    listener,
+                                    mBackupEligibilityRules),
                     "RestoreSession.restorePackage(" + packageName + ")");
         } finally {
             Binder.restoreCallingIdentity(oldId);
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index e42d3bd..6220679 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -49,6 +49,7 @@
 import com.android.server.backup.KeyValueAdbRestoreEngine;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
+import com.android.server.backup.utils.BackupEligibilityRules;
 import com.android.server.backup.utils.BytesReadListener;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
 import com.android.server.backup.utils.RestoreUtils;
@@ -129,11 +130,13 @@
     private final boolean mIsAdbRestore;
     @GuardedBy("mPipesLock")
     private boolean mPipesClosed;
+    private final BackupEligibilityRules mBackupEligibilityRules;
 
     public FullRestoreEngine(UserBackupManagerService backupManagerService,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
-            int ephemeralOpToken, boolean isAdbRestore) {
+            int ephemeralOpToken, boolean isAdbRestore,
+            BackupEligibilityRules backupEligibilityRules) {
         mBackupManagerService = backupManagerService;
         mEphemeralOpToken = ephemeralOpToken;
         mMonitorTask = monitorTask;
@@ -147,6 +150,7 @@
                 "Timeout parameters cannot be null");
         mIsAdbRestore = isAdbRestore;
         mUserId = backupManagerService.getUserId();
+        mBackupEligibilityRules = backupEligibilityRules;
     }
 
     public IBackupAgent getAgent() {
@@ -365,7 +369,8 @@
                             mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
                                     FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
                                             ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
-                                            : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
+                                            : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL,
+                                    mBackupEligibilityRules.getOperationType());
                             mAgentPackage = pkg;
                         } catch (IOException | NameNotFoundException e) {
                             // fall through to error handling
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 923bb08..c94286f 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -25,12 +25,15 @@
 import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
 
 import android.app.backup.IFullBackupRestoreObserver;
+import android.content.pm.PackageManagerInternal;
 import android.os.ParcelFileDescriptor;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
+import com.android.server.backup.utils.BackupEligibilityRules;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
 import com.android.server.backup.utils.PasswordUtils;
 
@@ -102,7 +105,10 @@
             }
 
             FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null,
-                    mObserver, null, null, true, 0 /*unused*/, true);
+                    mObserver, null, null, true, 0 /*unused*/, true,
+                    BackupEligibilityRules.forBackup(mBackupManagerService.getPackageManager(),
+                                    LocalServices.getService(PackageManagerInternal.class),
+                                    mBackupManagerService.getUserId()));
             FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine,
                     tarInputStream);
             mEngineThread.run();
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index a7e3604..7baf559 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -162,6 +162,7 @@
 
     private final int mEphemeralOpToken;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+    private final BackupEligibilityRules mBackupEligibilityRules;
 
     @VisibleForTesting
     PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
@@ -171,6 +172,7 @@
         mTransportManager = null;
         mEphemeralOpToken = 0;
         mUserId = 0;
+        mBackupEligibilityRules = null;
         this.backupManagerService = backupManagerService;
     }
 
@@ -208,6 +210,7 @@
         mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
+        mBackupEligibilityRules = backupEligibilityRules;
 
         if (targetPackage != null) {
             // Single package restore
@@ -656,7 +659,8 @@
         // Good to go!  Set up and bind the agent...
         mAgent = backupManagerService.bindToAgentSynchronous(
                 mCurrentPackage.applicationInfo,
-                ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
+                ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL,
+                mBackupEligibilityRules.getOperationType());
         if (mAgent == null) {
             Slog.w(TAG, "Can't find backup agent for " + packageName);
             mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
@@ -913,7 +917,8 @@
                     mCurrentPackage.packageName);
 
             mEngine = new FullRestoreEngine(backupManagerService, this, null,
-                    mMonitor, mCurrentPackage, false, mEphemeralOpToken, false);
+                    mMonitor, mCurrentPackage, false, mEphemeralOpToken, false,
+                    mBackupEligibilityRules);
             mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]);
 
             ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index ee05c2b..d659897 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -318,4 +318,8 @@
             return true;
         }
     }
+
+    public int getOperationType() {
+        return mOperationType;
+    }
 }
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/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 7cf5fd6..b7fed87 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -50,5 +50,5 @@
      * Informs battery stats of binder stats for the given work source UID.
      */
     public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
-            Collection<BinderCallsStats.CallStat> callStats);
+            Collection<BinderCallsStats.CallStat> callStats,  int[] binderThreadNativeTids);
 }
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index a3c04be..c951359 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -28,7 +28,9 @@
 import android.net.Uri;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.ShellCommand;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -308,50 +310,134 @@
         }
 
         boolean verbose = false;
+        int worksourceUid = Process.INVALID_UID;
         if (args != null) {
-            for (final String arg : args) {
+            for (int i = 0; i < args.length; i++) {
+                String arg = args[i];
                 if ("-a".equals(arg)) {
                     verbose = true;
-                } else if ("--reset".equals(arg)) {
-                    reset();
-                    pw.println("binder_calls_stats reset.");
-                    return;
-                } else if ("--enable".equals(arg)) {
-                    Binder.setObserver(mBinderCallsStats);
-                    return;
-                } else if ("--disable".equals(arg)) {
-                    Binder.setObserver(null);
-                    return;
-                } else if ("--no-sampling".equals(arg)) {
-                    mBinderCallsStats.setSamplingInterval(1);
-                    return;
-                } else if ("--enable-detailed-tracking".equals(arg)) {
-                    SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
-                    mBinderCallsStats.setDetailedTracking(true);
-                    pw.println("Detailed tracking enabled");
-                    return;
-                } else if ("--disable-detailed-tracking".equals(arg)) {
-                    SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "");
-                    mBinderCallsStats.setDetailedTracking(false);
-                    pw.println("Detailed tracking disabled");
-                    return;
-                } else if ("--dump-worksource-provider".equals(arg)) {
-                    mWorkSourceProvider.dump(pw, AppIdToPackageMap.getSnapshot());
-                    return;
                 } else if ("-h".equals(arg)) {
-                    pw.println("binder_calls_stats commands:");
-                    pw.println("  --reset: Reset stats");
-                    pw.println("  --enable: Enable tracking binder calls");
-                    pw.println("  --disable: Disables tracking binder calls");
-                    pw.println("  --no-sampling: Tracks all calls");
-                    pw.println("  --enable-detailed-tracking: Enables detailed tracking");
-                    pw.println("  --disable-detailed-tracking: Disables detailed tracking");
+                    pw.println("dumpsys binder_calls_stats options:");
+                    pw.println("  -a: Verbose");
+                    pw.println("  --work-source-uid <UID>: Dump binder calls from the UID");
                     return;
-                } else {
-                    pw.println("Unknown option: " + arg);
+                } else if ("--work-source-uid".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        throw new IllegalArgumentException(
+                                "Argument expected after \"" + arg + "\"");
+                    }
+                    String uidArg = args[i];
+                    try {
+                        worksourceUid = Integer.parseInt(uidArg);
+                    } catch (NumberFormatException e) {
+                        pw.println("Invalid UID: " + uidArg);
+                        return;
+                    }
+                }
+            }
+
+            if (args.length > 0 && worksourceUid == Process.INVALID_UID) {
+                // For compatibility, support "cmd"-style commands when passed to "dumpsys".
+                BinderCallsStatsShellCommand command = new BinderCallsStatsShellCommand(pw);
+                int status = command.exec(this, null, FileDescriptor.out, FileDescriptor.err, args);
+                if (status == 0) {
+                    return;
                 }
             }
         }
-        mBinderCallsStats.dump(pw, AppIdToPackageMap.getSnapshot(), verbose);
+        mBinderCallsStats.dump(pw, AppIdToPackageMap.getSnapshot(), worksourceUid, verbose);
+    }
+
+    @Override
+    public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
+            ParcelFileDescriptor err, String[] args) {
+        ShellCommand command = new BinderCallsStatsShellCommand(null);
+        int status = command.exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
+                err.getFileDescriptor(), args);
+        if (status != 0) {
+            command.onHelp();
+        }
+        return status;
+    }
+
+    private class BinderCallsStatsShellCommand extends ShellCommand {
+        private final PrintWriter mPrintWriter;
+
+        BinderCallsStatsShellCommand(PrintWriter printWriter) {
+            mPrintWriter = printWriter;
+        }
+
+        @Override
+        public PrintWriter getOutPrintWriter() {
+            if (mPrintWriter != null) {
+                return mPrintWriter;
+            }
+            return super.getOutPrintWriter();
+        }
+
+        @Override
+        public int onCommand(String cmd) {
+            PrintWriter pw = getOutPrintWriter();
+            if (cmd == null) {
+                return -1;
+            }
+
+            switch (cmd) {
+                case "--reset":
+                    reset();
+                    pw.println("binder_calls_stats reset.");
+                    break;
+                case "--enable":
+                    Binder.setObserver(mBinderCallsStats);
+                    break;
+                case "--disable":
+                    Binder.setObserver(null);
+                    break;
+                case "--no-sampling":
+                    mBinderCallsStats.setSamplingInterval(1);
+                    break;
+                case "--enable-detailed-tracking":
+                    SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
+                    mBinderCallsStats.setDetailedTracking(true);
+                    pw.println("Detailed tracking enabled");
+                    break;
+                case "--disable-detailed-tracking":
+                    SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "");
+                    mBinderCallsStats.setDetailedTracking(false);
+                    pw.println("Detailed tracking disabled");
+                    break;
+                case "--dump-worksource-provider":
+                    mBinderCallsStats.setDetailedTracking(true);
+                    mWorkSourceProvider.dump(pw, AppIdToPackageMap.getSnapshot());
+                    break;
+                case "--work-source-uid":
+                    String uidArg = getNextArgRequired();
+                    try {
+                        int uid = Integer.parseInt(uidArg);
+                        mBinderCallsStats.recordAllCallsForWorkSourceUid(uid);
+                    } catch (NumberFormatException e) {
+                        pw.println("Invalid UID: " + uidArg);
+                        return -1;
+                    }
+                    break;
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+            return 0;
+        }
+
+        @Override
+        public void onHelp() {
+            PrintWriter pw = getOutPrintWriter();
+            pw.println("binder_calls_stats commands:");
+            pw.println("  --reset: Reset stats");
+            pw.println("  --enable: Enable tracking binder calls");
+            pw.println("  --disable: Disables tracking binder calls");
+            pw.println("  --no-sampling: Tracks all calls");
+            pw.println("  --enable-detailed-tracking: Enables detailed tracking");
+            pw.println("  --disable-detailed-tracking: Disables detailed tracking");
+            pw.println("  --work-source-uid <UID>: Track all binder calls from the UID");
+        }
     }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 02c08cc..ef62a99 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -220,6 +220,8 @@
 
 import com.google.android.collect.Lists;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -7513,18 +7515,34 @@
     public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId,
             int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr,
             String dstAddr) {
-        mKeepaliveTracker.startNattKeepalive(
-                getNetworkAgentInfoForNetwork(network), fd, resourceId,
-                intervalSeconds, cb,
-                srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT);
+        try {
+            mKeepaliveTracker.startNattKeepalive(
+                    getNetworkAgentInfoForNetwork(network), fd, resourceId,
+                    intervalSeconds, cb,
+                    srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT);
+        } finally {
+            // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
+            // startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately.
+            if (fd != null && Binder.getCallingPid() != Process.myPid()) {
+                IoUtils.closeQuietly(fd);
+            }
+        }
     }
 
     @Override
     public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds,
             ISocketKeepaliveCallback cb) {
-        enforceKeepalivePermission();
-        mKeepaliveTracker.startTcpKeepalive(
-                getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb);
+        try {
+            enforceKeepalivePermission();
+            mKeepaliveTracker.startTcpKeepalive(
+                    getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb);
+        } finally {
+            // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
+            // startTcpKeepalive calls Os.dup(fd) before returning, so we can close immediately.
+            if (fd != null && Binder.getCallingPid() != Process.myPid()) {
+                IoUtils.closeQuietly(fd);
+            }
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 0ddfa1c..97f3b37 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -185,10 +185,10 @@
     /** Set of interfaces with active alerts. */
     @GuardedBy("mQuotaLock")
     private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
-    /** Set of UIDs blacklisted on metered networks. */
+    /** Set of UIDs denylisted on metered networks. */
     @GuardedBy("mRulesLock")
     private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray();
-    /** Set of UIDs whitelisted on metered networks. */
+    /** Set of UIDs allowlisted on metered networks. */
     @GuardedBy("mRulesLock")
     private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray();
     /** Set of UIDs with cleartext penalties. */
@@ -561,27 +561,27 @@
             synchronized (mRulesLock) {
                 size = mUidRejectOnMetered.size();
                 if (size > 0) {
-                    if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered blacklist rules");
+                    if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules");
                     uidRejectOnQuota = mUidRejectOnMetered;
                     mUidRejectOnMetered = new SparseBooleanArray();
                 }
 
                 size = mUidAllowOnMetered.size();
                 if (size > 0) {
-                    if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered whitelist rules");
+                    if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules");
                     uidAcceptOnQuota = mUidAllowOnMetered;
                     mUidAllowOnMetered = new SparseBooleanArray();
                 }
             }
             if (uidRejectOnQuota != null) {
                 for (int i = 0; i < uidRejectOnQuota.size(); i++) {
-                    setUidMeteredNetworkBlacklist(uidRejectOnQuota.keyAt(i),
+                    setUidMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i),
                             uidRejectOnQuota.valueAt(i));
                 }
             }
             if (uidAcceptOnQuota != null) {
                 for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
-                    setUidMeteredNetworkWhitelist(uidAcceptOnQuota.keyAt(i),
+                    setUidMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i),
                             uidAcceptOnQuota.valueAt(i));
                 }
             }
@@ -1307,14 +1307,14 @@
         }
     }
 
-    private void setUidOnMeteredNetworkList(int uid, boolean blacklist, boolean enable) {
+    private void setUidOnMeteredNetworkList(int uid, boolean denylist, boolean enable) {
         NetworkStack.checkNetworkStackPermission(mContext);
 
         synchronized (mQuotaLock) {
             boolean oldEnable;
             SparseBooleanArray quotaList;
             synchronized (mRulesLock) {
-                quotaList = blacklist ? mUidRejectOnMetered : mUidAllowOnMetered;
+                quotaList = denylist ? mUidRejectOnMetered : mUidAllowOnMetered;
                 oldEnable = quotaList.get(uid, false);
             }
             if (oldEnable == enable) {
@@ -1324,7 +1324,7 @@
 
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth");
             try {
-                if (blacklist) {
+                if (denylist) {
                     if (enable) {
                         mNetdService.bandwidthAddNaughtyApp(uid);
                     } else {
@@ -1353,12 +1353,12 @@
     }
 
     @Override
-    public void setUidMeteredNetworkBlacklist(int uid, boolean enable) {
+    public void setUidMeteredNetworkDenylist(int uid, boolean enable) {
         setUidOnMeteredNetworkList(uid, true, enable);
     }
 
     @Override
-    public void setUidMeteredNetworkWhitelist(int uid, boolean enable) {
+    public void setUidMeteredNetworkAllowlist(int uid, boolean enable) {
         setUidOnMeteredNetworkList(uid, false, enable);
     }
 
@@ -1626,7 +1626,7 @@
                     }
                 }
             }
-            // Normally, whitelist chains only contain deny rules, so numUids == exemptUids.length.
+            // Normally, allowlist chains only contain deny rules, so numUids == exemptUids.length.
             // But the code does not guarantee this in any way, and at least in one case - if we add
             // a UID rule to the firewall, and then disable the firewall - the chains can contain
             // the wrong type of rule. In this case, don't close connections that we shouldn't.
@@ -1691,7 +1691,7 @@
             // Close any sockets that were opened by the affected UIDs. This has to be done after
             // disabling network connectivity, in case they react to the socket close by reopening
             // the connection and race with the iptables commands that enable the firewall. All
-            // whitelist and blacklist chains allow RSTs through.
+            // allowlist and denylist chains allow RSTs through.
             if (enable) {
                 closeSocketsForFirewallChainLocked(chain, chainName);
             }
@@ -1828,7 +1828,7 @@
             } else {
                 ruleName = "deny";
             }
-        } else { // Blacklist mode
+        } else { // Denylist mode
             if (rule == FIREWALL_RULE_DENY) {
                 ruleName = "deny";
             } else {
@@ -1913,8 +1913,8 @@
             pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
             pw.print("Data saver mode: "); pw.println(mDataSaverMode);
             synchronized (mRulesLock) {
-                dumpUidRuleOnQuotaLocked(pw, "blacklist", mUidRejectOnMetered);
-                dumpUidRuleOnQuotaLocked(pw, "whitelist", mUidAllowOnMetered);
+                dumpUidRuleOnQuotaLocked(pw, "denylist", mUidRejectOnMetered);
+                dumpUidRuleOnQuotaLocked(pw, "allowlist", mUidAllowOnMetered);
             }
         }
 
@@ -2179,9 +2179,9 @@
             }
         }
 
-        void setUidOnMeteredNetworkList(boolean blacklist, int uid, boolean enable) {
+        void setUidOnMeteredNetworkList(boolean denylist, int uid, boolean enable) {
             synchronized (mRulesLock) {
-                if (blacklist) {
+                if (denylist) {
                     mUidRejectOnMetered.put(uid, enable);
                 } else {
                     mUidAllowOnMetered.put(uid, enable);
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 1689656..e675d8d 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -1453,8 +1453,11 @@
             } else {
                 mHealthCheckState = HealthCheckState.ACTIVE;
             }
-            Slog.i(TAG, "Updated health check state for package " + getName() + ": "
-                    + toString(oldState) + " -> " + toString(mHealthCheckState));
+
+            if (oldState != mHealthCheckState) {
+                Slog.i(TAG, "Updated health check state for package " + getName() + ": "
+                        + toString(oldState) + " -> " + toString(mHealthCheckState));
+            }
             return mHealthCheckState;
         }
 
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index df9dee89..6dbb1e92 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -137,6 +137,7 @@
     int mCurUiMode = 0;
     private int mSetUiMode = 0;
     private boolean mHoldingConfiguration = false;
+    private int mCurrentUser;
 
     private Configuration mConfiguration = new Configuration();
     boolean mSystemReady;
@@ -325,6 +326,7 @@
 
     @Override
     public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
+        mCurrentUser = to.getUserIdentifier();
         getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
         verifySetupWizardCompleted();
     }
@@ -727,16 +729,30 @@
 
         @Override
         public boolean setNightModeActivated(boolean active) {
+            if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+                    != PackageManager.PERMISSION_GRANTED)) {
+                Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
+                return false;
+            }
+            final int user = Binder.getCallingUserHandle().getIdentifier();
+            if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.e(TAG, "Target user is not current user,"
+                        + " INTERACT_ACROSS_USERS permission is required");
+                return false;
+
+            }
             synchronized (mLock) {
-                final int user = UserHandle.getCallingUserId();
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                         unregisterScreenOffEventLocked();
                         mOverrideNightModeOff = !active;
                         mOverrideNightModeOn = active;
-                        mOverrideNightModeUser = user;
-                        persistNightModeOverrides(user);
+                        mOverrideNightModeUser = mCurrentUser;
+                        persistNightModeOverrides(mCurrentUser);
                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
                             && active) {
                         mNightMode = UiModeManager.MODE_NIGHT_YES;
@@ -746,7 +762,7 @@
                     }
                     updateConfigurationLocked();
                     applyConfigurationExternallyLocked();
-                    persistNightMode(user);
+                    persistNightMode(mCurrentUser);
                     return true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 72f29b4..59ac09c 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -76,6 +76,8 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -115,7 +117,6 @@
     // 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
@@ -128,8 +129,6 @@
     private final LinkedList<VibrationInfo> mPreviousVibrations;
     private final int mPreviousVibrationsLimit;
     private final boolean mAllowPriorityVibrationsInLowPowerMode;
-    private final boolean mSupportsAmplitudeControl;
-    private final boolean mSupportsExternalControl;
     private final List<Integer> mSupportedEffects;
     private final long mCapabilities;
     private final int mDefaultVibrationAmplitude;
@@ -168,28 +167,38 @@
     private boolean mIsVibrating;
     @GuardedBy("mLock")
     private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
-                new RemoteCallbackList<>();
+            new RemoteCallbackList<>();
     private int mHapticFeedbackIntensity;
     private int mNotificationIntensity;
     private int mRingIntensity;
     private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>();
 
-    static native boolean vibratorExists();
-    static native void vibratorInit();
-    static native void vibratorOn(long milliseconds);
-    static native void vibratorOff();
-    static native boolean vibratorSupportsAmplitudeControl();
-    static native void vibratorSetAmplitude(int amplitude);
-    static native int[] vibratorGetSupportedEffects();
+    static native long vibratorInit();
+
+    static native long vibratorGetFinalizer();
+
+    static native boolean vibratorExists(long controllerPtr);
+
+    static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration);
+
+    static native void vibratorOff(long controllerPtr);
+
+    static native void vibratorSetAmplitude(long controllerPtr, int amplitude);
+
+    static native int[] vibratorGetSupportedEffects(long controllerPtr);
+
     static native long vibratorPerformEffect(long effect, long strength, Vibration vibration,
             boolean withCallback);
-    static native void vibratorPerformComposedEffect(
+
+    static native void vibratorPerformComposedEffect(long controllerPtr,
             VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
-    static native boolean vibratorSupportsExternalControl();
-    static native void vibratorSetExternalControl(boolean enabled);
-    static native long vibratorGetCapabilities();
-    static native void vibratorAlwaysOnEnable(long id, long effect, long strength);
-    static native void vibratorAlwaysOnDisable(long id);
+
+    static native void vibratorSetExternalControl(long controllerPtr, boolean enabled);
+
+    static native long vibratorGetCapabilities(long controllerPtr);
+    static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect,
+            long strength);
+    static native void vibratorAlwaysOnDisable(long controllerPtr, long id);
 
     private final IUidObserver mUidObserver = new IUidObserver.Stub() {
         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
@@ -231,8 +240,7 @@
 
         // The actual effect to be played.
         public VibrationEffect effect;
-        // The original effect that was requested. This is non-null only when the original effect
-        // differs from the effect that's being played. Typically these two things differ because
+        // The original effect that was requested. Typically these two things differ because
         // the effect was scaled based on the users vibration intensity settings.
         public VibrationEffect originalEffect;
 
@@ -248,9 +256,13 @@
             this.reason = reason;
         }
 
+        @Override
         public void binderDied() {
             synchronized (mLock) {
                 if (this == mCurrentVibration) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Vibration finished because binder died, cleaning up");
+                    }
                     doCancelVibrateLocked();
                 }
             }
@@ -261,6 +273,9 @@
         public void onComplete() {
             synchronized (mLock) {
                 if (this == mCurrentVibration) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Vibration finished by callback, cleaning up");
+                    }
                     doCancelVibrateLocked();
                 }
             }
@@ -370,13 +385,20 @@
         mNativeWrapper = injector.getNativeWrapper();
         mH = injector.createHandler(Looper.myLooper());
 
-        mNativeWrapper.vibratorInit();
+        long controllerPtr = mNativeWrapper.vibratorInit();
+        long finalizerPtr = mNativeWrapper.vibratorGetFinalizer();
+
+        if (finalizerPtr != 0) {
+            NativeAllocationRegistry registry =
+                    NativeAllocationRegistry.createMalloced(
+                            VibratorService.class.getClassLoader(), finalizerPtr);
+            registry.registerNativeAllocation(this, controllerPtr);
+        }
+
         // Reset the hardware to a default state, in case this is a runtime
         // restart instead of a fresh boot.
         mNativeWrapper.vibratorOff();
 
-        mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl();
-        mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl();
         mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects());
         mCapabilities = mNativeWrapper.vibratorGetCapabilities();
 
@@ -605,7 +627,8 @@
         synchronized (mInputDeviceVibrators) {
             // Input device vibrators don't support amplitude controls yet, but are still used over
             // the system vibrator when connected.
-            return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
+            return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)
+                    && mInputDeviceVibrators.isEmpty();
         }
     }
 
@@ -907,7 +930,7 @@
     // Callback for whenever the current vibration has finished played out
     public void onVibrationFinished() {
         if (DEBUG) {
-            Slog.e(TAG, "Vibration finished, cleaning up");
+            Slog.d(TAG, "Vibration finished, cleaning up");
         }
         synchronized (mLock) {
             // Make sure the vibration is really done. This also reports that the vibration is
@@ -935,25 +958,22 @@
     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.uid, vib.attrs);
-                mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
+                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.
-                Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
                 mThread = new VibrateThread(waveform, vib.uid, vib.attrs);
                 mThread.start();
             } else if (vib.effect instanceof VibrationEffect.Prebaked) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
-                long timeout = doVibratorPrebakedEffectLocked(vib);
-                if (timeout > 0) {
-                    mH.postDelayed(mVibrationEndRunnable, timeout);
-                }
+                timeout = doVibratorPrebakedEffectLocked(vib);
             } else if (vib.effect instanceof  VibrationEffect.Composed) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 doVibratorComposedEffectLocked(vib);
@@ -961,10 +981,16 @@
                 // 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.
-                mH.postDelayed(mVibrationEndRunnable, 10000);
+                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);
         }
@@ -1054,18 +1080,18 @@
         return attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
     }
 
-    private int getAppOpMode(Vibration vib) {
+    private int getAppOpMode(int uid, String packageName, VibrationAttributes attrs) {
         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
-                vib.attrs.getAudioAttributes().getUsage(), vib.uid, vib.opPkg);
+                attrs.getAudioAttributes().getUsage(), uid, packageName);
         if (mode == AppOpsManager.MODE_ALLOWED) {
-            mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
+            mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, packageName);
         }
 
-        if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(vib.attrs)) {
+        if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(attrs)) {
             // If we're just ignoring the vibration op then this is set by DND and we should ignore
             // if we're asked to bypass. AppOps won't be able to record this operation, so make
             // sure we at least note it in the logs for debugging.
-            Slog.d(TAG, "Bypassing DND for vibration: " + vib);
+            Slog.d(TAG, "Bypassing DND for vibrate from uid " + uid);
             mode = AppOpsManager.MODE_ALLOWED;
         }
         return mode;
@@ -1087,7 +1113,7 @@
             return false;
         }
 
-        final int mode = getAppOpMode(vib);
+        final int mode = getAppOpMode(vib.uid, vib.opPkg, vib.attrs);
         if (mode != AppOpsManager.MODE_ALLOWED) {
             if (mode == AppOpsManager.MODE_ERRORED) {
                 // We might be getting calls from within system_server, so we don't actually
@@ -1257,7 +1283,18 @@
         return mNativeWrapper.vibratorExists();
     }
 
+    /** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */
+    private void doVibratorOn(long millis, int amplitude, Vibration vib) {
+        doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib);
+    }
+
+    /** Vibrates without native callback. */
     private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
+        doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null);
+    }
+
+    private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs,
+            @Nullable Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
         try {
             synchronized (mInputDeviceVibrators) {
@@ -1272,13 +1309,14 @@
                 final int vibratorCount = mInputDeviceVibrators.size();
                 if (vibratorCount != 0) {
                     for (int i = 0; i < vibratorCount; i++) {
-                        mInputDeviceVibrators.get(i).vibrate(millis, attrs.getAudioAttributes());
+                        Vibrator inputDeviceVibrator = mInputDeviceVibrators.get(i);
+                        inputDeviceVibrator.vibrate(millis, attrs.getAudioAttributes());
                     }
                 } else {
                     // Note: ordering is important here! Many haptic drivers will reset their
                     // amplitude when enabled, so we always have to enable first, then set the
                     // amplitude.
-                    mNativeWrapper.vibratorOn(millis);
+                    mNativeWrapper.vibratorOn(millis, vib);
                     doVibratorSetAmplitude(amplitude);
                 }
             }
@@ -1288,7 +1326,7 @@
     }
 
     private void doVibratorSetAmplitude(int amplitude) {
-        if (mSupportsAmplitudeControl) {
+        if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
             mNativeWrapper.vibratorSetAmplitude(amplitude);
         }
     }
@@ -1562,6 +1600,7 @@
         proto.flush();
     }
 
+    /** Thread that plays a single {@link VibrationEffect.Waveform}. */
     private class VibrateThread extends Thread {
         private final VibrationEffect.Waveform mWaveform;
         private final int mUid;
@@ -1708,39 +1747,45 @@
     @VisibleForTesting
     public static class NativeWrapper {
 
+        private long mNativeControllerPtr = 0;
+
         /** Checks if vibrator exists on device. */
         public boolean vibratorExists() {
-            return VibratorService.vibratorExists();
+            return VibratorService.vibratorExists(mNativeControllerPtr);
         }
 
-        /** Initializes connection to vibrator HAL service. */
-        public void vibratorInit() {
-            VibratorService.vibratorInit();
+        /**
+         * Returns native pointer to newly created controller and initializes connection to vibrator
+         * HAL service.
+         */
+        public long vibratorInit() {
+            mNativeControllerPtr = VibratorService.vibratorInit();
+            return mNativeControllerPtr;
+        }
+
+        /** Returns pointer to native finalizer function to be called by GC. */
+        public long vibratorGetFinalizer() {
+            return VibratorService.vibratorGetFinalizer();
         }
 
         /** Turns vibrator on for given time. */
-        public void vibratorOn(long milliseconds) {
-            VibratorService.vibratorOn(milliseconds);
+        public void vibratorOn(long milliseconds, @Nullable Vibration vibration) {
+            VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration);
         }
 
         /** Turns vibrator off. */
         public void vibratorOff() {
-            VibratorService.vibratorOff();
-        }
-
-        /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */
-        public boolean vibratorSupportsAmplitudeControl() {
-            return VibratorService.vibratorSupportsAmplitudeControl();
+            VibratorService.vibratorOff(mNativeControllerPtr);
         }
 
         /** Sets the amplitude for the vibrator to run. */
         public void vibratorSetAmplitude(int amplitude) {
-            VibratorService.vibratorSetAmplitude(amplitude);
+            VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude);
         }
 
         /** Returns all predefined effects supported by the device vibrator. */
         public int[] vibratorGetSupportedEffects() {
-            return VibratorService.vibratorGetSupportedEffects();
+            return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr);
         }
 
         /** Turns vibrator on to perform one of the supported effects. */
@@ -1752,32 +1797,27 @@
         /** Turns vibrator on to perform one of the supported composed effects. */
         public void vibratorPerformComposedEffect(
                 VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) {
-            VibratorService.vibratorPerformComposedEffect(effect, vibration);
-        }
-
-        /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */
-        public boolean vibratorSupportsExternalControl() {
-            return VibratorService.vibratorSupportsExternalControl();
+            VibratorService.vibratorPerformComposedEffect(mNativeControllerPtr, effect, vibration);
         }
 
         /** Enabled the device vibrator to be controlled by another service. */
         public void vibratorSetExternalControl(boolean enabled) {
-            VibratorService.vibratorSetExternalControl(enabled);
+            VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled);
         }
 
         /** Returns all capabilities of the device vibrator. */
         public long vibratorGetCapabilities() {
-            return VibratorService.vibratorGetCapabilities();
+            return VibratorService.vibratorGetCapabilities(mNativeControllerPtr);
         }
 
         /** Enable always-on vibration with given id and effect. */
         public void vibratorAlwaysOnEnable(long id, long effect, long strength) {
-            VibratorService.vibratorAlwaysOnEnable(id, effect, strength);
+            VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength);
         }
 
         /** Disable always-on vibration for given id. */
         public void vibratorAlwaysOnDisable(long id) {
-            VibratorService.vibratorAlwaysOnDisable(id);
+            VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id);
         }
     }
 
@@ -1852,11 +1892,11 @@
 
         @Override
         public int onExternalVibrationStart(ExternalVibration vib) {
-            if (!mSupportsExternalControl) {
+            if (!hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
                 return SCALE_MUTE;
             }
             if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
-                        vib.getUid(), -1 /*owningUid*/, true /*exported*/)
+                    vib.getUid(), -1 /*owningUid*/, true /*exported*/)
                     != PackageManager.PERMISSION_GRANTED) {
                 Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
                         + " tried to play externally controlled vibration"
@@ -1864,6 +1904,14 @@
                 return SCALE_MUTE;
             }
 
+            int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                if (mode == AppOpsManager.MODE_ERRORED) {
+                    Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
+                }
+                return SCALE_MUTE;
+            }
+
             final int scaleLevel;
             synchronized (mLock) {
                 if (!vib.equals(mCurrentExternalVibration)) {
@@ -2142,10 +2190,10 @@
                 if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
                     pw.println("  Compose effects");
                 }
-                if (mSupportsAmplitudeControl || hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
+                if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
                     pw.println("  Amplitude control");
                 }
-                if (mSupportsExternalControl || hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+                if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
                     pw.println("  External control");
                 }
                 if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 33a92e6..3eb26de 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1836,11 +1836,13 @@
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
                 + " type=" + resolvedType + " conn=" + connection.asBinder()
                 + " flags=0x" + Integer.toHexString(flags));
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
         final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
         if (callerApp == null) {
             throw new SecurityException(
                     "Unable to find app for caller " + caller
-                    + " (pid=" + Binder.getCallingPid()
+                    + " (pid=" + callingPid
                     + ") when binding service " + service);
         }
 
@@ -1880,19 +1882,19 @@
         }
 
         if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0 && !isCallerSystem) {
-            throw new SecurityException("Non-system caller (pid=" + Binder.getCallingPid()
+            throw new SecurityException("Non-system caller (pid=" + callingPid
                     + ") set BIND_SCHEDULE_LIKE_TOP_APP when binding service " + service);
         }
 
         if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) {
             throw new SecurityException(
-                    "Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+                    "Non-system caller " + caller + " (pid=" + callingPid
                     + ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service);
         }
 
         if ((flags & Context.BIND_ALLOW_INSTANT) != 0 && !isCallerSystem) {
             throw new SecurityException(
-                    "Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+                    "Non-system caller " + caller + " (pid=" + callingPid
                             + ") set BIND_ALLOW_INSTANT when binding service " + service);
         }
 
@@ -1908,7 +1910,7 @@
 
         ServiceLookupResult res =
             retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
-                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
+                    callingPid, callingUid, userId, true,
                     callerFg, isBindExternal, allowInstant);
         if (res == null) {
             return 0;
@@ -2068,7 +2070,7 @@
             if (!s.mAllowWhileInUsePermissionInFgs) {
                 s.mAllowWhileInUsePermissionInFgs =
                         shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
-                                Binder.getCallingPid(), Binder.getCallingUid(),
+                                callingPid, callingUid,
                                 service, s, false);
             }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e546a28..a838aad 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -176,6 +176,8 @@
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
 import android.app.backup.IBackupManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
@@ -1956,7 +1958,7 @@
                         nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
                     }
                     memInfo.readMemInfo();
-                    synchronized (ActivityManagerService.this) {
+                    synchronized (mProcessStats.mLock) {
                         if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in "
                                 + (SystemClock.uptimeMillis()-start) + "ms");
                         final long cachedKb = memInfo.getCachedSizeKb();
@@ -5374,7 +5376,7 @@
             try {
                 thread.scheduleCreateBackupAgent(backupTarget.appInfo,
                         compatibilityInfoForPackage(backupTarget.appInfo),
-                        backupTarget.backupMode, backupTarget.userId);
+                        backupTarget.backupMode, backupTarget.userId, backupTarget.operationType);
             } catch (Exception e) {
                 Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
                 badApp = true;
@@ -7048,9 +7050,7 @@
             mUsageStatsService.prepareShutdown();
         }
         mBatteryStatsService.shutdown();
-        synchronized (this) {
-            mProcessStats.shutdownLocked();
-        }
+        mProcessStats.shutdown();
 
         return timedout;
     }
@@ -12352,7 +12352,7 @@
             MemInfoReader memInfo = new MemInfoReader();
             memInfo.readMemInfo();
             if (nativeProcTotalPss > 0) {
-                synchronized (this) {
+                synchronized (mProcessStats.mLock) {
                     final long cachedKb = memInfo.getCachedSizeKb();
                     final long freeKb = memInfo.getFreeSizeKb();
                     final long zramKb = memInfo.getZramTotalSizeKb();
@@ -12934,7 +12934,7 @@
             MemInfoReader memInfo = new MemInfoReader();
             memInfo.readMemInfo();
             if (nativeProcTotalPss > 0) {
-                synchronized (this) {
+                synchronized (mProcessStats.mLock) {
                     final long cachedKb = memInfo.getCachedSizeKb();
                     final long freeKb = memInfo.getFreeSizeKb();
                     final long zramKb = memInfo.getZramTotalSizeKb();
@@ -13778,7 +13778,8 @@
     // Cause the target app to be launched if necessary and its backup agent
     // instantiated.  The backup agent will invoke backupAgentCreated() on the
     // activity manager to announce its creation.
-    public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId) {
+    public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId,
+            @OperationType int operationType) {
         if (DEBUG_BACKUP) {
             Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode
                     + " targetUserId=" + targetUserId + " callingUid = " + Binder.getCallingUid()
@@ -13821,7 +13822,7 @@
                         + app.packageName + ": " + e);
             }
 
-            BackupRecord r = new BackupRecord(app, backupMode, targetUserId);
+            BackupRecord r = new BackupRecord(app, backupMode, targetUserId, operationType);
             ComponentName hostingName =
                     (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL)
                             ? new ComponentName(app.packageName, app.backupAgentName)
@@ -13860,7 +13861,8 @@
                 if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc);
                 try {
                     proc.thread.scheduleCreateBackupAgent(app,
-                            compatibilityInfoForPackage(app), backupMode, targetUserId);
+                            compatibilityInfoForPackage(app), backupMode, targetUserId,
+                            operationType);
                 } catch (RemoteException e) {
                     // Will time out on the backup manager side
                 }
@@ -14053,13 +14055,13 @@
     public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
             int flags) {
-        return registerReceiverWithFeature(caller, callerPackage, null, receiver, filter,
-                permission, userId, flags);
+        return registerReceiverWithFeature(caller, callerPackage, null, null,
+                receiver, filter, permission, userId, flags);
     }
 
     public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
-            String callerFeatureId, IIntentReceiver receiver, IntentFilter filter,
-            String permission, int userId, int flags) {
+            String callerFeatureId, String receiverId, IIntentReceiver receiver,
+            IntentFilter filter, String permission, int userId, int flags) {
         enforceNotIsolatedCaller("registerReceiver");
         ArrayList<Intent> stickyIntents = null;
         ProcessRecord callerApp = null;
@@ -14196,7 +14198,7 @@
                         + " callerPackage is " + callerPackage);
             }
             BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
-                    permission, callingUid, userId, instantApp, visibleToInstantApps);
+                    receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps);
             if (rl.containsFilter(filter)) {
                 Slog.w(TAG, "Receiver with filter " + filter
                         + " already registered for pid " + rl.pid
@@ -14468,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")
@@ -15312,7 +15314,7 @@
                         OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
                         realCallingPid, userId, allowBackgroundActivityStarts,
                         backgroundActivityStartsToken,
-                        null /*broadcastWhitelist*/);
+                        null /* broadcastAllowList */);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -16505,9 +16507,7 @@
         }
 
         @Override public void run() {
-            synchronized (mService) {
-                mProcessStats.writeStateAsyncLocked();
-            }
+            mProcessStats.writeStateAsync();
         }
     }
 
@@ -16557,9 +16557,13 @@
         }
         mLastMemoryLevel = memFactor;
         mLastNumProcesses = mProcessList.getLruSizeLocked();
-        boolean allChanged = mProcessStats.setMemFactorLocked(
-                memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
-        final int trackerMemFactor = mProcessStats.getMemFactorLocked();
+        boolean allChanged;
+        int trackerMemFactor;
+        synchronized (mProcessStats.mLock) {
+            allChanged = mProcessStats.setMemFactorLocked(
+                    memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
+            trackerMemFactor = mProcessStats.getMemFactorLocked();
+        }
         if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
             if (mLowRamStartTime == 0) {
                 mLowRamStartTime = now;
diff --git a/services/core/java/com/android/server/am/BackupRecord.java b/services/core/java/com/android/server/am/BackupRecord.java
index 37b4be4..d419856 100644
--- a/services/core/java/com/android/server/am/BackupRecord.java
+++ b/services/core/java/com/android/server/am/BackupRecord.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
 import android.content.pm.ApplicationInfo;
 
 /** @hide */
@@ -30,14 +32,16 @@
     final ApplicationInfo appInfo;         // information about BackupAgent's app
     final int userId;                      // user for which backup is performed
     final int backupMode;                  // full backup / incremental / restore
+    @OperationType  final int operationType; // see BackupManager#OperationType
     ProcessRecord app;                     // where this agent is running or null
 
     // ----- Implementation -----
 
-    BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId) {
+    BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _operationType) {
         appInfo = _appInfo;
         backupMode = _backupMode;
         userId = _userId;
+        operationType = _operationType;
     }
 
     public String toString() {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5081b36..d72998b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -227,8 +227,9 @@
 
         @Override
         public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
-                Collection<BinderCallsStats.CallStat> callStats) {
-            mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats);
+                Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
+            mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats,
+                    binderThreadNativeTids);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 578db6f..50e06c3 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -28,6 +28,7 @@
     final ReceiverList receiverList;
     final String packageName;
     final String featureId;
+    final String receiverId;
     final String requiredPermission;
     final int owningUid;
     final int owningUserId;
@@ -35,12 +36,13 @@
     final boolean visibleToInstantApp;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
-            String _packageName, String _featureId, String _requiredPermission, int _owningUid, int _userId,
-            boolean _instantApp, boolean _visibleToInstantApp) {
+            String _packageName, String _featureId, String _receiverId, String _requiredPermission,
+            int _owningUid, int _userId, boolean _instantApp, boolean _visibleToInstantApp) {
         super(_filter);
         receiverList = _receiverList;
         packageName = _packageName;
         featureId = _featureId;
+        receiverId = _receiverId;
         requiredPermission = _requiredPermission;
         owningUid = _owningUid;
         owningUserId = _userId;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 12937b9..960d26b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -639,7 +639,7 @@
                 final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
                 if (opCode != AppOpsManager.OP_NONE
                         && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
-                        r.callerPackage, r.callerFeatureId, "")
+                        r.callerPackage, r.callerFeatureId, "Broadcast sent to protected receiver")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: broadcasting "
                             + r.intent.toString()
@@ -672,7 +672,8 @@
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                         && mService.getAppOpsManager().noteOpNoThrow(appOp,
-                        filter.receiverList.uid, filter.packageName, filter.featureId, "")
+                        filter.receiverList.uid, filter.packageName, filter.featureId,
+                        "Broadcast delivered to registered receiver " + filter.receiverId)
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent.toString()
@@ -704,7 +705,8 @@
         }
         if (!skip && r.appOp != AppOpsManager.OP_NONE
                 && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
-                filter.receiverList.uid, filter.packageName, filter.featureId, "")
+                filter.receiverList.uid, filter.packageName, filter.featureId,
+                "Broadcast delivered to registered receiver " + filter.receiverId)
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent.toString()
@@ -1366,9 +1368,10 @@
             skip = true;
         } else if (!skip && info.activityInfo.permission != null) {
             final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
-            if (opCode != AppOpsManager.OP_NONE
-                    && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid, r.callerPackage,
-                    r.callerFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
+            if (opCode != AppOpsManager.OP_NONE && mService.getAppOpsManager().noteOpNoThrow(opCode,
+                    r.callingUid, r.callerPackage, r.callerFeatureId,
+                    "Broadcast delivered to " + info.activityInfo.name)
+                    != AppOpsManager.MODE_ALLOWED) {
                 Slog.w(TAG, "Appop Denial: broadcasting "
                         + r.intent.toString()
                         + " from " + r.callerPackage + " (pid="
@@ -1407,7 +1410,8 @@
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                         && mService.getAppOpsManager().noteOpNoThrow(appOp,
                         info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                        null /* default featureId */, "")
+                        null /* default featureId */,
+                        "Broadcast delivered to " + info.activityInfo.name)
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent + " to "
@@ -1424,7 +1428,7 @@
         if (!skip && r.appOp != AppOpsManager.OP_NONE
                 && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
                 info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                null  /* default featureId */, "")
+                null  /* default featureId */, "Broadcast delivered to " + info.activityInfo.name)
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent + " to "
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 8970ec4..0f2dfcc 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -116,6 +116,10 @@
                 WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class,
                 WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT));
         sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
+                DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL,
+                WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, int.class,
+                WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT));
+        sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
                 DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE,
                 WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class,
                 WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT));
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f0343e1..bf15f1737 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -659,13 +659,15 @@
 
         updateUidsLocked(activeUids, nowElapsed);
 
-        if (mService.mProcessStats.shouldWriteNowLocked(now)) {
-            mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
-                    mService.mProcessStats));
-        }
+        synchronized (mService.mProcessStats.mLock) {
+            if (mService.mProcessStats.shouldWriteNowLocked(now)) {
+                mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
+                        mService.mProcessStats));
+            }
 
-        // Run this after making sure all procstates are updated.
-        mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+            // Run this after making sure all procstates are updated.
+            mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+        }
 
         if (DEBUG_OOM_ADJ) {
             final long duration = SystemClock.uptimeMillis() - now;
diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java
index b36e3c7..50d58f0 100644
--- a/services/core/java/com/android/server/am/PendingTempWhitelists.java
+++ b/services/core/java/com/android/server/am/PendingTempWhitelists.java
@@ -32,13 +32,13 @@
 
     void put(int uid, ActivityManagerService.PendingTempWhitelist value) {
         mPendingTempWhitelist.put(uid, value);
-        mService.mAtmInternal.onUidAddedToPendingTempWhitelist(uid, value.tag);
+        mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag);
     }
 
     void removeAt(int index) {
         final int uid = mPendingTempWhitelist.keyAt(index);
         mPendingTempWhitelist.removeAt(index);
-        mService.mAtmInternal.onUidRemovedFromPendingTempWhitelist(uid);
+        mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid);
     }
 
     ActivityManagerService.PendingTempWhitelist get(int uid) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 1647fda..e3e1339 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -673,30 +673,33 @@
 
     public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
         if (thread == null) {
-            final ProcessState origBase = baseProcessTracker;
-            if (origBase != null) {
-                origBase.setState(ProcessStats.STATE_NOTHING,
-                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
-                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                            uid, processName, pkgList.keyAt(ipkg),
-                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
-                            pkgList.valueAt(ipkg).appVersion);
+            synchronized (tracker.mLock) {
+                final ProcessState origBase = baseProcessTracker;
+                if (origBase != null) {
+                    origBase.setState(ProcessStats.STATE_NOTHING,
+                            tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+                            pkgList.mPkgList);
+                    for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                                uid, processName, pkgList.keyAt(ipkg),
+                                ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                                pkgList.valueAt(ipkg).appVersion);
+                    }
+                    origBase.makeInactive();
                 }
-                origBase.makeInactive();
-            }
-            baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
-                    info.longVersionCode, processName);
-            baseProcessTracker.makeActive();
-            for (int i=0; i<pkgList.size(); i++) {
-                ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
-                if (holder.state != null && holder.state != origBase) {
-                    holder.state.makeInactive();
-                }
-                tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
+                baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
                         info.longVersionCode, processName);
-                if (holder.state != baseProcessTracker) {
-                    holder.state.makeActive();
+                baseProcessTracker.makeActive();
+                for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
+                    ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                    if (holder.state != null && holder.state != origBase) {
+                        holder.state.makeInactive();
+                    }
+                    tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
+                            info.longVersionCode, processName);
+                    if (holder.state != baseProcessTracker) {
+                        holder.state.makeActive();
+                    }
                 }
             }
         }
@@ -707,27 +710,30 @@
     public void makeInactive(ProcessStatsService tracker) {
         thread = null;
         mWindowProcessController.setThread(null);
-        final ProcessState origBase = baseProcessTracker;
-        if (origBase != null) {
+        synchronized (tracker.mLock) {
+            final ProcessState origBase = baseProcessTracker;
             if (origBase != null) {
-                origBase.setState(ProcessStats.STATE_NOTHING,
-                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
-                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                            uid, processName, pkgList.keyAt(ipkg),
-                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
-                            pkgList.valueAt(ipkg).appVersion);
+                if (origBase != null) {
+                    origBase.setState(ProcessStats.STATE_NOTHING,
+                            tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+                            pkgList.mPkgList);
+                    for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                                uid, processName, pkgList.keyAt(ipkg),
+                                ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                                pkgList.valueAt(ipkg).appVersion);
+                    }
+                    origBase.makeInactive();
                 }
-                origBase.makeInactive();
-            }
-            baseProcessTracker = null;
-            for (int i=0; i<pkgList.size(); i++) {
-                ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
-                if (holder.state != null && holder.state != origBase) {
-                    holder.state.makeInactive();
+                baseProcessTracker = null;
+                for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
+                    ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                    if (holder.state != null && holder.state != origBase) {
+                        holder.state.makeInactive();
+                    }
+                    holder.pkg = null;
+                    holder.state = null;
                 }
-                holder.pkg = null;
-                holder.state = null;
             }
         }
     }
@@ -1026,17 +1032,19 @@
      */
     public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) {
         if (!pkgList.containsKey(pkg)) {
-            ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
-                    versionCode);
-            if (baseProcessTracker != null) {
-                tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
-                        processName);
-                pkgList.put(pkg, holder);
-                if (holder.state != baseProcessTracker) {
-                    holder.state.makeActive();
+            synchronized (tracker.mLock) {
+                ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+                        versionCode);
+                if (baseProcessTracker != null) {
+                    tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
+                            processName);
+                    pkgList.put(pkg, holder);
+                    if (holder.state != baseProcessTracker) {
+                        holder.state.makeActive();
+                    }
+                } else {
+                    pkgList.put(pkg, holder);
                 }
-            } else {
-                pkgList.put(pkg, holder);
             }
             return true;
         }
@@ -1072,31 +1080,33 @@
     public void resetPackageList(ProcessStatsService tracker) {
         final int N = pkgList.size();
         if (baseProcessTracker != null) {
-            long now = SystemClock.uptimeMillis();
-            baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
-                    tracker.getMemFactorLocked(), now, pkgList.mPkgList);
-            for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                        uid, processName, pkgList.keyAt(ipkg),
-                        ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
-                        pkgList.valueAt(ipkg).appVersion);
-            }
-            if (N != 1) {
-                for (int i=0; i<N; i++) {
-                    ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
-                    if (holder.state != null && holder.state != baseProcessTracker) {
-                        holder.state.makeInactive();
-                    }
-
+            synchronized (tracker.mLock) {
+                long now = SystemClock.uptimeMillis();
+                baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
+                        tracker.getMemFactorLocked(), now, pkgList.mPkgList);
+                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                            uid, processName, pkgList.keyAt(ipkg),
+                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                            pkgList.valueAt(ipkg).appVersion);
                 }
-                pkgList.clear();
-                ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
-                        info.longVersionCode);
-                tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
-                        info.longVersionCode, processName);
-                pkgList.put(info.packageName, holder);
-                if (holder.state != baseProcessTracker) {
-                    holder.state.makeActive();
+                if (N != 1) {
+                    for (int i = 0; i < N; i++) {
+                        ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                        if (holder.state != null && holder.state != baseProcessTracker) {
+                            holder.state.makeInactive();
+                        }
+
+                    }
+                    pkgList.clear();
+                    ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+                            info.longVersionCode);
+                    tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
+                            info.longVersionCode, processName);
+                    pkgList.put(info.packageName, holder);
+                    if (holder.state != baseProcessTracker) {
+                        holder.state.makeActive();
+                    }
                 }
             }
         } else if (N != 1) {
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index a168af5a..4e8c386 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -71,33 +71,62 @@
 
     final ActivityManagerService mAm;
     final File mBaseDir;
-    ProcessStats mProcessStats;
+
+    // Note: The locking order of the below 3 locks should be:
+    // mLock, mPendingWriteLock, mFileLock
+
+    // The lock object to protect the internal state/structures
+    final Object mLock = new Object();
+
+    // The lock object to protect the access to pending writes
+    final Object mPendingWriteLock = new Object();
+
+    // The lock object to protect the access to all of the file read/write
+    final ReentrantLock mFileLock = new ReentrantLock();
+
+    @GuardedBy("mLock")
+    final ProcessStats mProcessStats;
+
+    @GuardedBy("mFileLock")
     AtomicFile mFile;
+
+    @GuardedBy("mLock")
     boolean mCommitPending;
+
+    @GuardedBy("mLock")
     boolean mShuttingDown;
+
+    @GuardedBy("mLock")
     int mLastMemOnlyState = -1;
     boolean mMemFactorLowered;
 
-    final ReentrantLock mWriteLock = new ReentrantLock();
-    final Object mPendingWriteLock = new Object();
+    @GuardedBy("mPendingWriteLock")
     AtomicFile mPendingWriteFile;
+
+    @GuardedBy("mPendingWriteLock")
     Parcel mPendingWrite;
+
+    @GuardedBy("mPendingWriteLock")
     boolean mPendingWriteCommitted;
+
+    @GuardedBy("mLock")
     long mLastWriteTime;
 
     /** For CTS to inject the screen state. */
-    @GuardedBy("mAm")
+    @GuardedBy("mLock")
     Boolean mInjectedScreenState;
 
     public ProcessStatsService(ActivityManagerService am, File file) {
         mAm = am;
         mBaseDir = file;
         mBaseDir.mkdirs();
-        mProcessStats = new ProcessStats(true);
-        updateFile();
+        synchronized (mLock) {
+            mProcessStats = new ProcessStats(true);
+            updateFileLocked();
+        }
         SystemProperties.addChangeCallback(new Runnable() {
             @Override public void run() {
-                synchronized (mAm) {
+                synchronized (mLock) {
                     if (mProcessStats.evaluateSystemProperties(false)) {
                         mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS;
                         writeStateLocked(true, true);
@@ -121,32 +150,33 @@
         }
     }
 
-    @GuardedBy("mAm")
-    public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
+    @GuardedBy("mLock")
+    void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
             String packageName, int uid, long versionCode, String processName) {
         holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode);
         holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName);
     }
 
-    @GuardedBy("mAm")
-    public ProcessState getProcessStateLocked(String packageName,
+    @GuardedBy("mLock")
+    ProcessState getProcessStateLocked(String packageName,
             int uid, long versionCode, String processName) {
         return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
     }
 
-    @GuardedBy("mAm")
-    public ServiceState getServiceStateLocked(String packageName, int uid,
+    ServiceState getServiceState(String packageName, int uid,
             long versionCode, String processName, String className) {
-        return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
-                className);
+        synchronized (mLock) {
+            return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
+                    className);
+        }
     }
 
-    public boolean isMemFactorLowered() {
+    boolean isMemFactorLowered() {
         return mMemFactorLowered;
     }
 
-    @GuardedBy("mAm")
-    public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
+    @GuardedBy("mLock")
+    boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
         mMemFactorLowered = memFactor < mLastMemOnlyState;
         mLastMemOnlyState = memFactor;
         if (mInjectedScreenState != null) {
@@ -184,24 +214,24 @@
         return false;
     }
 
-    @GuardedBy("mAm")
-    public int getMemFactorLocked() {
+    @GuardedBy("mLock")
+    int getMemFactorLocked() {
         return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
     }
 
-    @GuardedBy("mAm")
-    public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
+    @GuardedBy("mLock")
+    void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
             long nativeMem) {
         mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
     }
 
-    @GuardedBy("mAm")
-    public void updateTrackingAssociationsLocked(int curSeq, long now) {
+    @GuardedBy("mLock")
+    void updateTrackingAssociationsLocked(int curSeq, long now) {
         mProcessStats.updateTrackingAssociationsLocked(curSeq, now);
     }
 
-    @GuardedBy("mAm")
-    public boolean shouldWriteNowLocked(long now) {
+    @GuardedBy("mLock")
+    boolean shouldWriteNowLocked(long now) {
         if (now > (mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime()
                     > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) &&
@@ -214,25 +244,27 @@
         return false;
     }
 
-    @GuardedBy("mAm")
-    public void shutdownLocked() {
+    void shutdown() {
         Slog.w(TAG, "Writing process stats before shutdown...");
-        mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
-        writeStateSyncLocked();
-        mShuttingDown = true;
+        synchronized (mLock) {
+            mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
+            writeStateSyncLocked();
+            mShuttingDown = true;
+        }
     }
 
-    @GuardedBy("mAm")
-    public void writeStateAsyncLocked() {
-        writeStateLocked(false);
+    void writeStateAsync() {
+        synchronized (mLock) {
+            writeStateLocked(false);
+        }
     }
 
-    @GuardedBy("mAm")
-    public void writeStateSyncLocked() {
+    @GuardedBy("mLock")
+    private void writeStateSyncLocked() {
         writeStateLocked(true);
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mLock")
     private void writeStateLocked(boolean sync) {
         if (mShuttingDown) {
             return;
@@ -242,8 +274,8 @@
         writeStateLocked(sync, commitPending);
     }
 
-    @GuardedBy("mAm")
-    public void writeStateLocked(boolean sync, final boolean commit) {
+    @GuardedBy("mLock")
+    private void writeStateLocked(boolean sync, final boolean commit) {
         final long totalTime;
         synchronized (mPendingWriteLock) {
             final long now = SystemClock.uptimeMillis();
@@ -255,13 +287,13 @@
                     mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
                 }
                 mProcessStats.writeToParcel(mPendingWrite, 0);
-                mPendingWriteFile = new AtomicFile(mFile.getBaseFile());
+                mPendingWriteFile = new AtomicFile(getCurrentFile());
                 mPendingWriteCommitted = commit;
             }
             if (commit) {
                 mProcessStats.resetSafely();
-                updateFile();
-                mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+                updateFileLocked();
+                scheduleRequestPssAllProcs(true, false);
             }
             mLastWriteTime = SystemClock.uptimeMillis();
             totalTime = SystemClock.uptimeMillis() - now;
@@ -279,14 +311,37 @@
         performWriteState(totalTime);
     }
 
-    private void updateFile() {
-        mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
-                + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+    private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) {
+        mAm.mHandler.post(() -> {
+            synchronized (mAm) {
+                mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), always, memLowered);
+            }
+        });
+    }
+
+    @GuardedBy("mLock")
+    private void updateFileLocked() {
+        mFileLock.lock();
+        try {
+            mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
+                    + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+        } finally {
+            mFileLock.unlock();
+        }
         mLastWriteTime = SystemClock.uptimeMillis();
     }
 
-    void performWriteState(long initialTime) {
-        if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile());
+    private File getCurrentFile() {
+        mFileLock.lock();
+        try {
+            return mFile.getBaseFile();
+        } finally {
+            mFileLock.unlock();
+        }
+    }
+
+    private void performWriteState(long initialTime) {
+        if (DEBUG) Slog.d(TAG, "Performing write to " + getCurrentFile());
         Parcel data;
         AtomicFile file;
         synchronized (mPendingWriteLock) {
@@ -298,7 +353,7 @@
             }
             mPendingWrite = null;
             mPendingWriteFile = null;
-            mWriteLock.lock();
+            mFileLock.lock();
         }
 
         final long startTime = SystemClock.uptimeMillis();
@@ -316,13 +371,13 @@
             file.failWrite(stream);
         } finally {
             data.recycle();
-            trimHistoricStatesWriteLocked();
-            mWriteLock.unlock();
+            trimHistoricStatesWriteLF();
+            mFileLock.unlock();
         }
     }
 
-    @GuardedBy("mAm")
-    boolean readLocked(ProcessStats stats, AtomicFile file) {
+    @GuardedBy("mFileLock")
+    private boolean readLF(ProcessStats stats, AtomicFile file) {
         try {
             FileInputStream stream = file.openRead();
             stats.read(stream);
@@ -387,7 +442,8 @@
         return true;
     }
 
-    private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent,
+    @GuardedBy("mFileLock")
+    private ArrayList<String> getCommittedFilesLF(int minNum, boolean inclCurrent,
             boolean inclCheckedIn) {
         File[] files = mBaseDir.listFiles();
         if (files == null || files.length <= minNum) {
@@ -414,9 +470,9 @@
         return filesArray;
     }
 
-    @GuardedBy("mAm")
-    public void trimHistoricStatesWriteLocked() {
-        ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
+    @GuardedBy("mFileLock")
+    private void trimHistoricStatesWriteLF() {
+        ArrayList<String> filesArray = getCommittedFilesLF(MAX_HISTORIC_STATES, false, true);
         if (filesArray == null) {
             return;
         }
@@ -427,8 +483,8 @@
         }
     }
 
-    @GuardedBy("mAm")
-    boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
+    @GuardedBy("mLock")
+    private boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
             boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
             boolean sepProcStates, int[] procStates, long now, String reqPackage) {
         ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked(
@@ -502,20 +558,21 @@
         return res;
     }
 
+    @Override
     public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
         mAm.mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
-        synchronized (mAm) {
+        synchronized (mLock) {
             long now = SystemClock.uptimeMillis();
             mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
             mProcessStats.mTimePeriodEndUptime = now;
             mProcessStats.writeToParcel(current, now, 0);
         }
-        mWriteLock.lock();
+        mFileLock.lock();
         try {
             if (historic != null) {
-                ArrayList<String> files = getCommittedFiles(0, false, true);
+                ArrayList<String> files = getCommittedFilesLF(0, false, true);
                 if (files != null) {
                     for (int i=files.size()-1; i>=0; i--) {
                         try {
@@ -529,7 +586,7 @@
                 }
             }
         } finally {
-            mWriteLock.unlock();
+            mFileLock.unlock();
         }
         return current.marshall();
     }
@@ -563,9 +620,9 @@
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
 
         long newHighWaterMark = highWaterMarkMs;
-        mWriteLock.lock();
+        mFileLock.lock();
         try {
-            ArrayList<String> files = getCommittedFiles(0, false, true);
+            ArrayList<String> files = getCommittedFilesLF(0, false, true);
             if (files != null) {
                 String highWaterMarkStr =
                         DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString();
@@ -612,7 +669,7 @@
         } catch (IOException e) {
             Slog.w(TAG, "Failure opening procstat file", e);
         } finally {
-            mWriteLock.unlock();
+            mFileLock.unlock();
         }
         return newHighWaterMark;
     }
@@ -625,7 +682,7 @@
         return mAm.mConstants.MIN_ASSOC_LOG_DURATION;
     }
 
-    private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
+    private static ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
             throws IOException {
         final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
         Thread thr = new Thread("ProcessStats pipe output") {
@@ -645,12 +702,13 @@
         return fds[0];
     }
 
+    @Override
     public ParcelFileDescriptor getStatsOverTime(long minTime) {
         mAm.mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
         long curTime;
-        synchronized (mAm) {
+        synchronized (mLock) {
             long now = SystemClock.uptimeMillis();
             mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
             mProcessStats.mTimePeriodEndUptime = now;
@@ -658,11 +716,11 @@
             curTime = mProcessStats.mTimePeriodEndRealtime
                     - mProcessStats.mTimePeriodStartRealtime;
         }
-        mWriteLock.lock();
+        mFileLock.lock();
         try {
             if (curTime < minTime) {
                 // Need to add in older stats to reach desired time.
-                ArrayList<String> files = getCommittedFiles(0, false, true);
+                ArrayList<String> files = getCommittedFilesLF(0, false, true);
                 if (files != null && files.size() > 0) {
                     current.setDataPosition(0);
                     ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
@@ -673,7 +731,7 @@
                         AtomicFile file = new AtomicFile(new File(files.get(i)));
                         i--;
                         ProcessStats moreStats = new ProcessStats(false);
-                        readLocked(moreStats, file);
+                        readLF(moreStats, file);
                         if (moreStats.mReadError == null) {
                             stats.add(moreStats);
                             StringBuilder sb = new StringBuilder();
@@ -712,13 +770,14 @@
         } catch (IOException e) {
             Slog.w(TAG, "Failed building output pipe", e);
         } finally {
-            mWriteLock.unlock();
+            mFileLock.unlock();
         }
         return null;
     }
 
+    @Override
     public int getCurrentMemoryState() {
-        synchronized (mAm) {
+        synchronized (mLock) {
             return mLastMemOnlyState;
         }
     }
@@ -947,7 +1006,7 @@
                 } else if ("--current".equals(arg)) {
                     currentOnly = true;
                 } else if ("--commit".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
                         writeStateLocked(true, true);
                         pw.println("Process stats committed.");
@@ -962,29 +1021,39 @@
                     }
                     section = parseSectionOptions(args[i]);
                 } else if ("--clear".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mProcessStats.resetSafely();
-                        mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
-                        ArrayList<String> files = getCommittedFiles(0, true, true);
-                        if (files != null) {
-                            for (int fi=0; fi<files.size(); fi++) {
-                                (new File(files.get(fi))).delete();
+                        scheduleRequestPssAllProcs(true, false);
+                        mFileLock.lock();
+                        try {
+                            ArrayList<String> files = getCommittedFilesLF(0, true, true);
+                            if (files != null) {
+                                for (int fi = files.size() - 1; fi >= 0; fi--) {
+                                    (new File(files.get(fi))).delete();
+                                }
                             }
+                        } finally {
+                            mFileLock.unlock();
                         }
                         pw.println("All process stats cleared.");
                         quit = true;
                     }
                 } else if ("--write".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         writeStateSyncLocked();
                         pw.println("Process stats written.");
                         quit = true;
                     }
                 } else if ("--read".equals(arg)) {
-                    synchronized (mAm) {
-                        readLocked(mProcessStats, mFile);
-                        pw.println("Process stats read.");
-                        quit = true;
+                    synchronized (mLock) {
+                        mFileLock.lock();
+                        try {
+                            readLF(mProcessStats, mFile);
+                            pw.println("Process stats read.");
+                            quit = true;
+                        } finally {
+                            mFileLock.unlock();
+                        }
                     }
                 } else if ("--start-testing".equals(arg)) {
                     synchronized (mAm) {
@@ -999,17 +1068,17 @@
                         quit = true;
                     }
                 } else if ("--pretend-screen-on".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mInjectedScreenState = true;
                     }
                     quit = true;
                 } else if ("--pretend-screen-off".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mInjectedScreenState = false;
                     }
                     quit = true;
                 } else if ("--stop-pretend-screen".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mInjectedScreenState = null;
                     }
                     quit = true;
@@ -1060,7 +1129,7 @@
                 }
             }
             pw.println();
-            synchronized (mAm) {
+            synchronized (mLock) {
                 dumpFilteredProcessesCsvLocked(pw, null,
                         csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
                         csvSepProcStats, csvProcStats, now, reqPackage);
@@ -1090,14 +1159,26 @@
             return;
         } else if (lastIndex > 0) {
             pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
-            ArrayList<String> files = getCommittedFiles(0, false, true);
-            if (lastIndex >= files.size()) {
-                pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
-                return;
+
+            ArrayList<String> files;
+            AtomicFile file;
+            ProcessStats processStats;
+
+            mFileLock.lock();
+            try {
+                files = getCommittedFilesLF(0, false, true);
+                if (lastIndex >= files.size()) {
+                    pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
+                    return;
+                }
+                file = new AtomicFile(new File(files.get(lastIndex)));
+                processStats = new ProcessStats(false);
+                readLF(processStats, file);
+            } finally {
+                mFileLock.unlock();
             }
-            AtomicFile file = new AtomicFile(new File(files.get(lastIndex)));
-            ProcessStats processStats = new ProcessStats(false);
-            readLocked(processStats, file);
+
+            // No lock is needed now, since only us have the access to the 'processStats'.
             if (processStats.mReadError != null) {
                 if (isCheckin || isCompact) pw.print("err,");
                 pw.print("Failure reading "); pw.print(files.get(lastIndex));
@@ -1118,7 +1199,7 @@
                     processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
                             dumpAll, activeOnly, section);
                     if (dumpAll) {
-                        pw.print("  mFile="); pw.println(mFile.getBaseFile());
+                        pw.print("  mFile="); pw.println(getCurrentFile());
                     }
                 } else {
                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1129,9 +1210,9 @@
 
         boolean sepNeeded = false;
         if ((dumpAll || isCheckin) && !currentOnly) {
-            mWriteLock.lock();
+            mFileLock.lock();
             try {
-                ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
+                ArrayList<String> files = getCommittedFilesLF(0, false, !isCheckin);
                 if (files != null) {
                     int start = isCheckin ? 0 : (files.size() - maxNum);
                     if (start < 0) {
@@ -1142,7 +1223,7 @@
                         try {
                             AtomicFile file = new AtomicFile(new File(files.get(i)));
                             ProcessStats processStats = new ProcessStats(false);
-                            readLocked(processStats, file);
+                            readLF(processStats, file);
                             if (processStats.mReadError != null) {
                                 if (isCheckin || isCompact) pw.print("err,");
                                 pw.print("Failure reading "); pw.print(files.get(i));
@@ -1188,11 +1269,11 @@
                     }
                 }
             } finally {
-                mWriteLock.unlock();
+                mFileLock.unlock();
             }
         }
         if (!isCheckin) {
-            synchronized (mAm) {
+            synchronized (mLock) {
                 if (isCompact) {
                     mProcessStats.dumpCheckinLocked(pw, reqPackage, section);
                 } else {
@@ -1204,7 +1285,7 @@
                         mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
                                 dumpAll, activeOnly, section);
                         if (dumpAll) {
-                            pw.print("  mFile="); pw.println(mFile.getBaseFile());
+                            pw.print("  mFile="); pw.println(getCurrentFile());
                         }
                     } else {
                         mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1249,7 +1330,7 @@
 
         // dump current procstats
         long now;
-        synchronized (mAm) {
+        synchronized (mLock) {
             now = SystemClock.uptimeMillis();
             final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
             mProcessStats.dumpDebug(proto, now, ProcessStats.REPORT_ALL);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 022b04d..4a27030 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -528,8 +528,9 @@
             return tracker;
         }
         if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
-            tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
-                    serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.longVersionCode,
+            tracker = ams.mProcessStats.getServiceState(serviceInfo.packageName,
+                    serviceInfo.applicationInfo.uid,
+                    serviceInfo.applicationInfo.longVersionCode,
                     serviceInfo.processName, serviceInfo.name);
             tracker.applyNewOwner(this);
         }
@@ -546,7 +547,8 @@
     public void makeRestarting(int memFactor, long now) {
         if (restartTracker == null) {
             if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
-                restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
+                restartTracker = ams.mProcessStats.getServiceState(
+                        serviceInfo.packageName,
                         serviceInfo.applicationInfo.uid,
                         serviceInfo.applicationInfo.longVersionCode,
                         serviceInfo.processName, serviceInfo.name);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 45f95fd..2bbbbf1 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -224,12 +224,35 @@
             if (!addSpeakerphoneClient(cb, pid, on)) {
                 return false;
             }
+            if (on) {
+                // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
+                // are mutually exclusive.
+                // See symmetrical operation for startBluetoothScoForClient_Sync().
+                mBtHelper.stopBluetoothScoForPid(pid);
+            }
             final boolean wasOn = isSpeakerphoneOn();
             updateSpeakerphoneOn(eventSource);
             return (wasOn != isSpeakerphoneOn());
         }
     }
 
+    /**
+     * Turns speakerphone off for a given pid and update speakerphone state.
+     * @param pid
+     */
+    @GuardedBy("mDeviceStateLock")
+    private void setSpeakerphoneOffForPid(int pid) {
+        SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
+        if (client == null) {
+            return;
+        }
+        client.unregisterDeathRecipient();
+        mSpeakerphoneClients.remove(client);
+        final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
+                .append(pid).append(")").toString();
+        updateSpeakerphoneOn(eventSource);
+    }
+
     @GuardedBy("mDeviceStateLock")
     private void updateSpeakerphoneOn(String eventSource) {
         if (isSpeakerphoneOnRequested()) {
@@ -488,6 +511,10 @@
     /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
                 @NonNull String eventSource) {
         synchronized (mDeviceStateLock) {
+            // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
+            // are mutually exclusive.
+            // See symmetrical operation for setSpeakerphoneOn(true).
+            setSpeakerphoneOffForPid(Binder.getCallingPid());
             mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
         }
     }
@@ -1379,6 +1406,16 @@
         return false;
     }
 
+    @GuardedBy("mDeviceStateLock")
+    private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
+        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+            if (cl.getPid() == pid) {
+                return cl;
+            }
+        }
+        return null;
+    }
+
     // List of clients requesting speakerPhone ON
     @GuardedBy("mDeviceStateLock")
     private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 366f303..b943966 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1282,7 +1282,6 @@
             }
 
             if (isPlatformTelevision()) {
-                checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller);
                 synchronized (mHdmiClientLock) {
                     if (mHdmiManager != null && mHdmiPlaybackClient != null) {
                         updateHdmiCecSinkLocked(mHdmiCecSink | false);
@@ -1302,22 +1301,54 @@
         }
     }
 
-    private void checkAddAllFixedVolumeDevices(int device, String caller) {
+    /**
+     * 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) {
         final int numStreamTypes = AudioSystem.getNumStreamTypes();
         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-            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();
+            updateVolumeStates(device, streamType, caller);
+        }
+    }
 
-            // Unmute streams if device is full volume
-            if (mFullVolumeDevices.contains(device)) {
-                mStreamStates[streamType].mute(false);
+    /**
+     * 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);
+                }
             }
         }
     }
@@ -4901,7 +4932,15 @@
         synchronized (VolumeStreamState.class) {
             for (int stream = 0; stream < mStreamStates.length; stream++) {
                 if (stream != skipStream) {
-                    mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/);
+                    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");
+                    }
                 }
             }
         }
@@ -4970,7 +5009,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
-        checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller);
+        updateVolumeStatesForAudioDevice(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller);
     }
 
     /**
@@ -7192,10 +7231,9 @@
                 // HDMI output
                 removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
             }
+            updateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI,
+                    "HdmiPlaybackClient.DisplayStatusCallback");
         }
-
-        checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI,
-                "HdmiPlaybackClient.DisplayStatusCallback");
     }
 
     private class MyHdmiControlStatusChangeListenerCallback
@@ -7903,9 +7941,6 @@
             return null;
         }
 
-        mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for "
-                + pcb.asBinder() + " with config:" + policyConfig)).printLog(TAG));
-
         String regId = null;
         synchronized (mAudioPolicies) {
             if (mAudioPolicies.containsKey(pcb.asBinder())) {
@@ -7916,6 +7951,13 @@
                 AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener,
                         isFocusPolicy, isTestFocusPolicy, isVolumeController, projection);
                 pcb.asBinder().linkToDeath(app, 0/*flags*/);
+
+                // logging after registration so we have the registration id
+                mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for "
+                        + pcb.asBinder() + " u/pid:" + Binder.getCallingUid() + "/"
+                        + Binder.getCallingPid() + " with config:" + app.toCompactLogString()))
+                        .printLog(TAG));
+
                 regId = app.getRegistrationId();
                 mAudioPolicies.put(pcb.asBinder(), app);
             } catch (RemoteException e) {
@@ -8079,7 +8121,10 @@
      * @param pcb nullable because on service interface
      */
     public void unregisterAudioPolicyAsync(@Nullable IAudioPolicyCallback pcb) {
-        unregisterAudioPolicy(pcb);
+        if (pcb == null) {
+            return;
+        }
+        unregisterAudioPolicyInt(pcb, "unregisterAudioPolicyAsync");
     }
 
     /**
@@ -8090,12 +8135,12 @@
         if (pcb == null) {
             return;
         }
-        unregisterAudioPolicyInt(pcb);
+        unregisterAudioPolicyInt(pcb, "unregisterAudioPolicy");
     }
 
 
-    private void unregisterAudioPolicyInt(@NonNull IAudioPolicyCallback pcb) {
-        mDynPolicyLogger.log((new AudioEventLogger.StringEvent("unregisterAudioPolicyAsync for "
+    private void unregisterAudioPolicyInt(@NonNull IAudioPolicyCallback pcb, String operationName) {
+        mDynPolicyLogger.log((new AudioEventLogger.StringEvent(operationName + " for "
                 + pcb.asBinder()).printLog(TAG)));
         synchronized (mAudioPolicies) {
             AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder());
@@ -8570,7 +8615,8 @@
         }
 
         public void binderDied() {
-            Log.i(TAG, "audio policy " + mPolicyCallback + " died");
+            mDynPolicyLogger.log((new AudioEventLogger.StringEvent("AudioPolicy "
+                    + mPolicyCallback.asBinder() + " died").printLog(TAG)));
             release();
         }
 
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 5e8f1ef..ded0f9a3 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -432,19 +432,35 @@
         // and this must be done on behalf of system server to make sure permissions are granted.
         final long ident = Binder.clearCallingIdentity();
         if (client != null) {
-            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-            client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                    SCO_MODE_VIRTUAL_CALL);
-            // If a disconnection is pending, the client will be removed whne clearAllScoClients()
-            // is called form receiveBtEvent()
-            if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
-                    && mScoAudioState != SCO_STATE_DEACTIVATING) {
-                client.remove(false /*stop */, true /*unregister*/);
-            }
+            stopAndRemoveClient(client, eventSource);
         }
         Binder.restoreCallingIdentity(ident);
     }
 
+    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
+        ScoClient client = getScoClientForPid(pid);
+        if (client == null) {
+            return;
+        }
+        final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
+                .append(pid).append(")").toString();
+        stopAndRemoveClient(client, eventSource);
+    }
+
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
+        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+        client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
+                SCO_MODE_VIRTUAL_CALL);
+        // If a disconnection is pending, the client will be removed when clearAllScoClients()
+        // is called form receiveBtEvent()
+        if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
+                && mScoAudioState != SCO_STATE_DEACTIVATING) {
+            client.remove(false /*stop */, true /*unregister*/);
+        }
+    }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
         if (mHearingAid == null) {
@@ -982,6 +998,16 @@
         return null;
     }
 
+    @GuardedBy("BtHelper.this")
+    private ScoClient getScoClientForPid(int pid) {
+        for (ScoClient cl : mScoClients) {
+            if (cl.getPid() == pid) {
+                return cl;
+            }
+        }
+        return null;
+    }
+
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     @GuardedBy("BtHelper.this")
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index 0a30b76..d98298c 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -20,6 +20,7 @@
 import android.app.backup.BackupAgentHelper;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupHelper;
+import android.app.backup.BackupManager;
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
 import android.app.backup.WallpaperBackupHelper;
@@ -87,8 +88,8 @@
     private int mUserId = UserHandle.USER_SYSTEM;
 
     @Override
-    public void onCreate(UserHandle user) {
-        super.onCreate(user);
+    public void onCreate(UserHandle user, @BackupManager.OperationType int operationType) {
+        super.onCreate(user, operationType);
 
         mUserId = user.getIdentifier();
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1f85d10..1c93d4e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -48,6 +48,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
+import android.net.DnsResolver;
 import android.net.INetworkManagementEventObserver;
 import android.net.Ikev2VpnProfile;
 import android.net.IpPrefix;
@@ -79,6 +80,7 @@
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -123,6 +125,7 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
 import java.util.ArrayList;
@@ -134,6 +137,8 @@
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -190,6 +195,7 @@
     // automated reconnection
 
     private final Context mContext;
+    @VisibleForTesting final Dependencies mDeps;
     private final NetworkInfo mNetworkInfo;
     @VisibleForTesting protected String mPackage;
     private int mOwnerUID;
@@ -252,17 +258,143 @@
     // Handle of the user initiating VPN.
     private final int mUserHandle;
 
+    interface RetryScheduler {
+        void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException;
+    }
+
+    static class Dependencies {
+        public void startService(final String serviceName) {
+            SystemService.start(serviceName);
+        }
+
+        public void stopService(final String serviceName) {
+            SystemService.stop(serviceName);
+        }
+
+        public boolean isServiceRunning(final String serviceName) {
+            return SystemService.isRunning(serviceName);
+        }
+
+        public boolean isServiceStopped(final String serviceName) {
+            return SystemService.isStopped(serviceName);
+        }
+
+        public File getStateFile() {
+            return new File("/data/misc/vpn/state");
+        }
+
+        public void sendArgumentsToDaemon(
+                final String daemon, final LocalSocket socket, final String[] arguments,
+                final RetryScheduler retryScheduler) throws IOException, InterruptedException {
+            final LocalSocketAddress address = new LocalSocketAddress(
+                    daemon, LocalSocketAddress.Namespace.RESERVED);
+
+            // Wait for the socket to connect.
+            while (true) {
+                try {
+                    socket.connect(address);
+                    break;
+                } catch (Exception e) {
+                    // ignore
+                }
+                retryScheduler.checkInterruptAndDelay(true /* sleepLonger */);
+            }
+            socket.setSoTimeout(500);
+
+            final OutputStream out = socket.getOutputStream();
+            for (String argument : arguments) {
+                byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
+                if (bytes.length >= 0xFFFF) {
+                    throw new IllegalArgumentException("Argument is too large");
+                }
+                out.write(bytes.length >> 8);
+                out.write(bytes.length);
+                out.write(bytes);
+                retryScheduler.checkInterruptAndDelay(false /* sleepLonger */);
+            }
+            out.write(0xFF);
+            out.write(0xFF);
+
+            // Wait for End-of-File.
+            final InputStream in = socket.getInputStream();
+            while (true) {
+                try {
+                    if (in.read() == -1) {
+                        break;
+                    }
+                } catch (Exception e) {
+                    // ignore
+                }
+                retryScheduler.checkInterruptAndDelay(true /* sleepLonger */);
+            }
+        }
+
+        @NonNull
+        public InetAddress resolve(final String endpoint)
+                throws ExecutionException, InterruptedException {
+            try {
+                return InetAddress.parseNumericAddress(endpoint);
+            } catch (IllegalArgumentException e) {
+                // Endpoint is not numeric : fall through and resolve
+            }
+
+            final CancellationSignal cancellationSignal = new CancellationSignal();
+            try {
+                final DnsResolver resolver = DnsResolver.getInstance();
+                final CompletableFuture<InetAddress> result = new CompletableFuture();
+                final DnsResolver.Callback<List<InetAddress>> cb =
+                        new DnsResolver.Callback<List<InetAddress>>() {
+                            @Override
+                            public void onAnswer(@NonNull final List<InetAddress> answer,
+                                    final int rcode) {
+                                if (answer.size() > 0) {
+                                    result.complete(answer.get(0));
+                                } else {
+                                    result.completeExceptionally(
+                                            new UnknownHostException(endpoint));
+                                }
+                            }
+
+                            @Override
+                            public void onError(@Nullable final DnsResolver.DnsException error) {
+                                // Unfortunately UnknownHostException doesn't accept a cause, so
+                                // print a message here instead. Only show the summary, not the
+                                // full stack trace.
+                                Log.e(TAG, "Async dns resolver error : " + error);
+                                result.completeExceptionally(new UnknownHostException(endpoint));
+                            }
+                        };
+                resolver.query(null /* network, null for default */, endpoint,
+                        DnsResolver.FLAG_EMPTY, r -> r.run(), cancellationSignal, cb);
+                return result.get();
+            } catch (final ExecutionException e) {
+                Log.e(TAG, "Cannot resolve VPN endpoint : " + endpoint + ".", e);
+                throw e;
+            } catch (final InterruptedException e) {
+                Log.e(TAG, "Legacy VPN was interrupted while resolving the endpoint", e);
+                cancellationSignal.cancel();
+                throw e;
+            }
+        }
+
+        public boolean checkInterfacePresent(final Vpn vpn, final String iface) {
+            return vpn.jniCheck(iface) == 0;
+        }
+    }
+
     public Vpn(Looper looper, Context context, INetworkManagementService netService,
             @UserIdInt int userHandle, @NonNull KeyStore keyStore) {
-        this(looper, context, netService, userHandle, keyStore,
+        this(looper, context, new Dependencies(), netService, userHandle, keyStore,
                 new SystemServices(context), new Ikev2SessionCreator());
     }
 
     @VisibleForTesting
-    protected Vpn(Looper looper, Context context, INetworkManagementService netService,
+    protected Vpn(Looper looper, Context context, Dependencies deps,
+            INetworkManagementService netService,
             int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices,
             Ikev2SessionCreator ikev2SessionCreator) {
         mContext = context;
+        mDeps = deps;
         mNetd = netService;
         mUserHandle = userHandle;
         mLooper = looper;
@@ -2129,7 +2261,8 @@
     }
 
     /** This class represents the common interface for all VPN runners. */
-    private abstract class VpnRunner extends Thread {
+    @VisibleForTesting
+    abstract class VpnRunner extends Thread {
 
         protected VpnRunner(String name) {
             super(name);
@@ -2638,7 +2771,7 @@
                     } catch (InterruptedException e) {
                     }
                     for (String daemon : mDaemons) {
-                        SystemService.stop(daemon);
+                        mDeps.stopService(daemon);
                     }
                 }
                 agentDisconnect();
@@ -2655,21 +2788,55 @@
             }
         }
 
+        private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) {
+            final String endpointAddressString = endpointAddress.getHostAddress();
+            // Perform some safety checks before inserting the address in place.
+            // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd.
+            if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) {
+                throw new IllegalStateException("Unexpected daemons order");
+            }
+
+            // Respectively, the positions at which racoon and mtpd take the server address
+            // argument are 1 and 2. Not all types of VPN require both daemons however, and
+            // in that case the corresponding argument array is null.
+            if (mArguments[0] != null) {
+                if (!mProfile.server.equals(mArguments[0][1])) {
+                    throw new IllegalStateException("Invalid server argument for racoon");
+                }
+                mArguments[0][1] = endpointAddressString;
+            }
+
+            if (mArguments[1] != null) {
+                if (!mProfile.server.equals(mArguments[1][2])) {
+                    throw new IllegalStateException("Invalid server argument for mtpd");
+                }
+                mArguments[1][2] = endpointAddressString;
+            }
+        }
+
         private void bringup() {
             // Catch all exceptions so we can clean up a few things.
             try {
+                // resolve never returns null. If it does because of some bug, it will be
+                // caught by the catch() block below and cleanup gracefully.
+                final InetAddress endpointAddress = mDeps.resolve(mProfile.server);
+
+                // Big hack : dynamically replace the address of the server in the arguments
+                // with the resolved address.
+                checkAndFixupArguments(endpointAddress);
+
                 // Initialize the timer.
                 mBringupStartTime = SystemClock.elapsedRealtime();
 
                 // Wait for the daemons to stop.
                 for (String daemon : mDaemons) {
-                    while (!SystemService.isStopped(daemon)) {
+                    while (!mDeps.isServiceStopped(daemon)) {
                         checkInterruptAndDelay(true);
                     }
                 }
 
                 // Clear the previous state.
-                File state = new File("/data/misc/vpn/state");
+                final File state = mDeps.getStateFile();
                 state.delete();
                 if (state.exists()) {
                     throw new IllegalStateException("Cannot delete the state");
@@ -2696,57 +2863,19 @@
 
                     // Start the daemon.
                     String daemon = mDaemons[i];
-                    SystemService.start(daemon);
+                    mDeps.startService(daemon);
 
                     // Wait for the daemon to start.
-                    while (!SystemService.isRunning(daemon)) {
+                    while (!mDeps.isServiceRunning(daemon)) {
                         checkInterruptAndDelay(true);
                     }
 
                     // Create the control socket.
                     mSockets[i] = new LocalSocket();
-                    LocalSocketAddress address = new LocalSocketAddress(
-                            daemon, LocalSocketAddress.Namespace.RESERVED);
 
-                    // Wait for the socket to connect.
-                    while (true) {
-                        try {
-                            mSockets[i].connect(address);
-                            break;
-                        } catch (Exception e) {
-                            // ignore
-                        }
-                        checkInterruptAndDelay(true);
-                    }
-                    mSockets[i].setSoTimeout(500);
-
-                    // Send over the arguments.
-                    OutputStream out = mSockets[i].getOutputStream();
-                    for (String argument : arguments) {
-                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
-                        if (bytes.length >= 0xFFFF) {
-                            throw new IllegalArgumentException("Argument is too large");
-                        }
-                        out.write(bytes.length >> 8);
-                        out.write(bytes.length);
-                        out.write(bytes);
-                        checkInterruptAndDelay(false);
-                    }
-                    out.write(0xFF);
-                    out.write(0xFF);
-
-                    // Wait for End-of-File.
-                    InputStream in = mSockets[i].getInputStream();
-                    while (true) {
-                        try {
-                            if (in.read() == -1) {
-                                break;
-                            }
-                        } catch (Exception e) {
-                            // ignore
-                        }
-                        checkInterruptAndDelay(true);
-                    }
+                    // Wait for the socket to connect and send over the arguments.
+                    mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments,
+                            this::checkInterruptAndDelay);
                 }
 
                 // Wait for the daemons to create the new state.
@@ -2754,7 +2883,7 @@
                     // Check if a running daemon is dead.
                     for (int i = 0; i < mDaemons.length; ++i) {
                         String daemon = mDaemons[i];
-                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
+                        if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) {
                             throw new IllegalStateException(daemon + " is dead");
                         }
                     }
@@ -2764,7 +2893,8 @@
                 // Now we are connected. Read and parse the new state.
                 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
                 if (parameters.length != 7) {
-                    throw new IllegalStateException("Cannot parse the state");
+                    throw new IllegalStateException("Cannot parse the state: '"
+                            + String.join("', '", parameters) + "'");
                 }
 
                 // Set the interface and the addresses in the config.
@@ -2793,20 +2923,15 @@
                 }
 
                 // Add a throw route for the VPN server endpoint, if one was specified.
-                String endpoint = parameters[5].isEmpty() ? mProfile.server : parameters[5];
-                if (!endpoint.isEmpty()) {
-                    try {
-                        InetAddress addr = InetAddress.parseNumericAddress(endpoint);
-                        if (addr instanceof Inet4Address) {
-                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
-                        } else if (addr instanceof Inet6Address) {
-                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
-                        } else {
-                            Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
-                        }
-                    } catch (IllegalArgumentException e) {
-                        Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
-                    }
+                if (endpointAddress instanceof Inet4Address) {
+                    mConfig.routes.add(new RouteInfo(
+                            new IpPrefix(endpointAddress, 32), RTN_THROW));
+                } else if (endpointAddress instanceof Inet6Address) {
+                    mConfig.routes.add(new RouteInfo(
+                            new IpPrefix(endpointAddress, 128), RTN_THROW));
+                } else {
+                    Log.e(TAG, "Unknown IP address family for VPN endpoint: "
+                            + endpointAddress);
                 }
 
                 // Here is the last step and it must be done synchronously.
@@ -2818,7 +2943,7 @@
                     checkInterruptAndDelay(false);
 
                     // Check if the interface is gone while we are waiting.
-                    if (jniCheck(mConfig.interfaze) == 0) {
+                    if (mDeps.checkInterfacePresent(Vpn.this, mConfig.interfaze)) {
                         throw new IllegalStateException(mConfig.interfaze + " is gone");
                     }
 
@@ -2849,7 +2974,7 @@
             while (true) {
                 Thread.sleep(2000);
                 for (int i = 0; i < mDaemons.length; i++) {
-                    if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
+                    if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) {
                         return;
                     }
                 }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 6f12155..b8e579d 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -349,9 +349,7 @@
 
     // Normalize entire brightness range to 0 - 1.
     protected static float normalizeAbsoluteBrightness(int brightness) {
-        return BrightnessSynchronizer.brightnessIntToFloat(brightness,
-                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
-                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+        return BrightnessSynchronizer.brightnessIntToFloat(brightness);
     }
 
     private Pair<float[], float[]> insertControlPoint(
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0979ad6..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);
         }
     }
 
@@ -2550,8 +2531,7 @@
         public boolean requestPowerState(DisplayPowerRequest request,
                 boolean waitForNegativeProximity) {
             synchronized (mSyncRoot) {
-                return mDisplayPowerController.requestPowerState(request,
-                        waitForNegativeProximity);
+                return mDisplayPowerController.requestPowerState(request, waitForNegativeProximity);
             }
         }
 
@@ -2679,6 +2659,10 @@
             return getDisplayedContentSampleInternal(displayId, maxFrames, timestamp);
         }
 
+        @Override
+        public void ignoreProximitySensorUntilChanged() {
+            mDisplayPowerController.ignoreProximitySensorUntilChanged();
+        }
     }
 
     class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 9411c562..58ef9d1 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -117,6 +117,7 @@
     private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
     private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6;
     private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
+    private static final int MSG_IGNORE_PROXIMITY = 8;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -263,6 +264,11 @@
     // go to sleep by the user.  While true, the screen remains off.
     private boolean mWaitingForNegativeProximity;
 
+    // True if the device should not take into account the proximity sensor
+    // until either the proximity sensor state changes, or there is no longer a
+    // request to listen to proximity sensor.
+    private boolean mIgnoreProximityUntilChanged;
+
     // The actual proximity sensor threshold value.
     private float mProximityThreshold;
 
@@ -699,13 +705,13 @@
         // Initialize screen state for battery stats.
         try {
             mBatteryStats.noteScreenState(mPowerState.getScreenState());
-            mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(mContext,
+            mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
                     mPowerState.getScreenBrightness()));
         } catch (RemoteException ex) {
             // same process
         }
         // Initialize all of the brightness tracking state
-        final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(mContext,
+        final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(
                 mPowerState.getScreenBrightness()));
         if (brightness >= 0.0f) {
             mBrightnessTracker.start(brightness);
@@ -760,8 +766,7 @@
 
             if (mPowerRequest == null) {
                 mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
-                mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
-                mPendingWaitForNegativeProximityLocked = false;
+                updatePendingProximityRequestsLocked();
                 mPendingRequestChangedLocked = false;
                 mustInitialize = true;
                 // Assume we're on and bright until told otherwise, since that's the state we turn
@@ -770,8 +775,7 @@
             } else if (mPendingRequestChangedLocked) {
                 previousPolicy = mPowerRequest.policy;
                 mPowerRequest.copyFrom(mPendingRequestLocked);
-                mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
-                mPendingWaitForNegativeProximityLocked = false;
+                updatePendingProximityRequestsLocked();
                 mPendingRequestChangedLocked = false;
                 mDisplayReadyLocked = false;
             } else {
@@ -822,9 +826,16 @@
         // Apply the proximity sensor.
         if (mProximitySensor != null) {
             if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
+                // At this point the policy says that the screen should be on, but we've been
+                // asked to listen to the prox sensor to adjust the display state, so lets make
+                // sure the sensor is on.
                 setProximitySensorEnabled(true);
                 if (!mScreenOffBecauseOfProximity
-                        && mProximity == PROXIMITY_POSITIVE) {
+                        && mProximity == PROXIMITY_POSITIVE
+                        && !mIgnoreProximityUntilChanged) {
+                    // Prox sensor already reporting "near" so we should turn off the screen.
+                    // Also checked that we aren't currently set to ignore the proximity sensor
+                    // temporarily.
                     mScreenOffBecauseOfProximity = true;
                     sendOnProximityPositiveWithWakelock();
                 }
@@ -832,18 +843,28 @@
                     && mScreenOffBecauseOfProximity
                     && mProximity == PROXIMITY_POSITIVE
                     && state != Display.STATE_OFF) {
+                // The policy says that we should have the screen on, but it's off due to the prox
+                // and we've been asked to wait until the screen is far from the user to turn it
+                // back on. Let keep the prox sensor on so we can tell when it's far again.
                 setProximitySensorEnabled(true);
             } else {
+                // We haven't been asked to use the prox sensor and we're not waiting on the screen
+                // to turn back on...so lets shut down the prox sensor.
                 setProximitySensorEnabled(false);
                 mWaitingForNegativeProximity = false;
             }
+
             if (mScreenOffBecauseOfProximity
-                    && mProximity != PROXIMITY_POSITIVE) {
+                    && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
+                // The screen *was* off due to prox being near, but now it's "far" so lets turn
+                // the screen back on.  Also turn it back on if we've been asked to ignore the
+                // prox sensor temporarily.
                 mScreenOffBecauseOfProximity = false;
                 sendOnProximityNegativeWithWakelock();
             }
         } else {
             mWaitingForNegativeProximity = false;
+            mIgnoreProximityUntilChanged = false;
         }
         if (mScreenOffBecauseOfProximity) {
             state = Display.STATE_OFF;
@@ -1093,7 +1114,7 @@
                     userInitiatedChange = false;
                 }
                 notifyBrightnessChanged(
-                        BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessState),
+                        BrightnessSynchronizer.brightnessFloatToInt(brightnessState),
                         userInitiatedChange, hadUserBrightnessPoint);
             }
 
@@ -1181,6 +1202,14 @@
         sendUpdatePowerState();
     }
 
+    /**
+     * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
+     * currently enabled and forcing the screen to be dark.
+     */
+    public void ignoreProximitySensorUntilChanged() {
+        mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
+    }
+
     public void setBrightnessConfiguration(BrightnessConfiguration c) {
         Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
         msg.sendToTarget();
@@ -1341,8 +1370,7 @@
             try {
                 // TODO(brightnessfloat): change BatteryStats to use float
                 mBatteryStats.noteScreenBrightness(
-                        BrightnessSynchronizer.brightnessFloatToInt(
-                        mContext, target));
+                        BrightnessSynchronizer.brightnessFloatToInt(target));
             } catch (RemoteException ex) {
                 // same process
             }
@@ -1529,6 +1557,7 @@
                 // Register the listener.
                 // Proximity sensor state already cleared initially.
                 mProximitySensorEnabled = true;
+                mIgnoreProximityUntilChanged = false;
                 mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
                         SensorManager.SENSOR_DELAY_NORMAL, mHandler);
             }
@@ -1538,6 +1567,7 @@
                 // Clear the proximity sensor state for next time.
                 mProximitySensorEnabled = false;
                 mProximity = PROXIMITY_UNKNOWN;
+                mIgnoreProximityUntilChanged = false;
                 mPendingProximity = PROXIMITY_UNKNOWN;
                 mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
                 mSensorManager.unregisterListener(mProximitySensorListener);
@@ -1580,6 +1610,11 @@
                 && mPendingProximityDebounceTime >= 0) {
             final long now = SystemClock.uptimeMillis();
             if (mPendingProximityDebounceTime <= now) {
+                if (mProximity != mPendingProximity) {
+                    // if the status of the sensor changed, stop ignoring.
+                    mIgnoreProximityUntilChanged = false;
+                    Slog.i(TAG, "No longer ignoring proximity [" + mPendingProximity + "]");
+                }
                 // Sensor reading accepted.  Apply the change then release the wake lock.
                 mProximity = mPendingProximity;
                 updatePowerState();
@@ -1723,6 +1758,27 @@
         }
     }
 
+    private void updatePendingProximityRequestsLocked() {
+        mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
+        mPendingWaitForNegativeProximityLocked = false;
+
+        if (mIgnoreProximityUntilChanged) {
+            // Also, lets stop waiting for negative proximity if we're ignoring it.
+            mWaitingForNegativeProximity = false;
+        }
+    }
+
+    private void ignoreProximitySensorUntilChangedInternal() {
+        if (!mIgnoreProximityUntilChanged
+                && mPowerRequest.useProximitySensor
+                && mProximity == PROXIMITY_POSITIVE) {
+            // Only ignore if it is still reporting positive (near)
+            mIgnoreProximityUntilChanged = true;
+            Slog.i(TAG, "Ignoring proximity");
+            updatePowerState();
+        }
+    }
+
     private final Runnable mOnStateChangedRunnable = new Runnable() {
         @Override
         public void run() {
@@ -1961,6 +2017,10 @@
                     mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1);
                     updatePowerState();
                     break;
+
+                case MSG_IGNORE_PROXIMITY:
+                    ignoreProximitySensorUntilChangedInternal();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 48fa1bf..0be428b 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -728,16 +728,14 @@
                         try {
                             if (isHalBrightnessRangeSpecified()) {
                                 brightness = displayBrightnessToHalBrightness(
-                                        BrightnessSynchronizer.brightnessFloatToIntRange(
-                                                getContext(), brightness));
+                                        BrightnessSynchronizer.brightnessFloatToIntRange(brightness));
                             }
                             if (mBacklight != null) {
                                 mBacklight.setBrightness(brightness);
                             }
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "ScreenBrightness",
-                                    BrightnessSynchronizer.brightnessFloatToInt(
-                                            getContext(), brightness));
+                                    BrightnessSynchronizer.brightnessFloatToInt(brightness));
                         } finally {
                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
                         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 596c1ec..d675b81 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -232,11 +232,12 @@
         mAutoTvOff = enabled;
     }
 
+    @Override
     @ServiceThreadOnly
     @VisibleForTesting
     void setIsActiveSource(boolean on) {
         assertRunOnServiceThread();
-        mIsActiveSource = on;
+        super.setIsActiveSource(on);
         if (on) {
             getWakeLock().acquire();
         } else {
@@ -274,19 +275,15 @@
 
     @Override
     @ServiceThreadOnly
-    protected boolean handleActiveSource(HdmiCecMessage message) {
-        super.handleActiveSource(message);
-        if (mIsActiveSource) {
-            return true;
-        }
+    protected void onActiveSourceLost() {
+        assertRunOnServiceThread();
         switch (mPowerStateChangeOnActiveSourceLost) {
             case STANDBY_NOW:
                 mService.standby();
-                return true;
+                return;
             case NONE:
-                return true;
+                return;
         }
-        return true;
     }
 
     @ServiceThreadOnly
@@ -398,9 +395,12 @@
     }
 
     @Override
+    @ServiceThreadOnly
     protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
+        assertRunOnServiceThread();
         if (physicalAddress != mService.getPhysicalAddress()) {
-            return; // Do nothing.
+            setActiveSource(physicalAddress);
+            return;
         }
         switch (mPlaybackDeviceActionOnRoutingControl) {
             case WAKE_UP_AND_SEND_ACTIVE_SOURCE:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 1c67718..44ad8ee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -114,6 +114,19 @@
     }
 
     @ServiceThreadOnly
+    protected void onActiveSourceLost() {
+        // Nothing to do.
+    }
+
+    @ServiceThreadOnly
+    protected void setActiveSource(int physicalAddress) {
+        assertRunOnServiceThread();
+        // Invalidate the internal active source record. This will also update mIsActiveSource.
+        ActiveSource activeSource = ActiveSource.of(Constants.ADDR_INVALID, physicalAddress);
+        setActiveSource(activeSource);
+    }
+
+    @ServiceThreadOnly
     protected boolean handleActiveSource(HdmiCecMessage message) {
         assertRunOnServiceThread();
         int logicalAddress = message.getSource();
@@ -148,6 +161,9 @@
         if (physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) {
             setAndBroadcastActiveSource(message, physicalAddress);
         }
+        if (physicalAddress != mService.getPhysicalAddress()) {
+            setActiveSource(physicalAddress);
+        }
         switchInputOnReceivingNewActivePath(physicalAddress);
         return true;
     }
@@ -156,18 +172,21 @@
     @ServiceThreadOnly
     protected boolean handleRoutingChange(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams(), 2);
+        if (physicalAddress != mService.getPhysicalAddress()) {
+            setActiveSource(physicalAddress);
+        }
         if (!isRoutingControlFeatureEnabled()) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
             return true;
         }
-        int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
         // if the current device is a pure playback device
         if (!mIsSwitchDevice
-                && newPath == mService.getPhysicalAddress()
+                && physicalAddress == mService.getPhysicalAddress()
                 && mService.isPlaybackDevice()) {
-            setAndBroadcastActiveSource(message, newPath);
+            setAndBroadcastActiveSource(message, physicalAddress);
         }
-        handleRoutingChangeAndInformation(newPath, message);
+        handleRoutingChangeAndInformation(physicalAddress, message);
         return true;
     }
 
@@ -175,11 +194,14 @@
     @ServiceThreadOnly
     protected boolean handleRoutingInformation(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+        if (physicalAddress != mService.getPhysicalAddress()) {
+            setActiveSource(physicalAddress);
+        }
         if (!isRoutingControlFeatureEnabled()) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
             return true;
         }
-        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
         // if the current device is a pure playback device
         if (!mIsSwitchDevice
                 && physicalAddress == mService.getPhysicalAddress()
@@ -222,7 +244,11 @@
     @ServiceThreadOnly
     void setIsActiveSource(boolean on) {
         assertRunOnServiceThread();
+        boolean wasActiveSource = mIsActiveSource;
         mIsActiveSource = on;
+        if (wasActiveSource && !mIsActiveSource) {
+            onActiveSourceLost();
+        }
     }
 
     protected void wakeUpIfActiveSource() {
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/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 74ed815..813def4 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.input;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -62,10 +63,12 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.UserHandle;
+import android.os.VibrationEffect;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -139,6 +142,8 @@
     private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6;
 
+    private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
+
     // Pointer to native input manager service object.
     private final long mPtr;
 
@@ -181,8 +186,7 @@
 
     // State for vibrator tokens.
     private Object mVibratorLock = new Object();
-    private HashMap<IBinder, VibratorToken> mVibratorTokens =
-            new HashMap<IBinder, VibratorToken>();
+    private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
     private int mNextVibratorTokenValue;
 
     // State for the currently installed input filter.
@@ -190,12 +194,16 @@
     IInputFilter mInputFilter; // guarded by mInputFilterLock
     InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
 
+    private final Object mGestureMonitorPidsLock = new Object();
+    @GuardedBy("mGestureMonitorPidsLock")
+    private final ArrayMap<IBinder, Integer> mGestureMonitorPidsByToken = new ArrayMap<>();
+
     // The associations of input devices to displays by port. Maps from input device port (String)
     // to display id (int). Currently only accessed by InputReader.
     private final Map<String, Integer> mStaticAssociations;
     private final Object mAssociationsLock = new Object();
     @GuardedBy("mAssociationLock")
-    private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>();
+    private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>();
 
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
@@ -540,13 +548,17 @@
         if (displayId < Display.DEFAULT_DISPLAY) {
             throw new IllegalArgumentException("displayId must >= 0.");
         }
+        final int pid = Binder.getCallingPid();
 
         final long ident = Binder.clearCallingIdentity();
         try {
             InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
             InputMonitorHost host = new InputMonitorHost(inputChannels[0]);
-            nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId,
-                    true /*isGestureMonitor*/);
+            nativeRegisterInputMonitor(
+                    mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/);
+            synchronized (mGestureMonitorPidsLock) {
+                mGestureMonitorPidsByToken.put(inputChannels[1].getToken(), pid);
+            }
             return new InputMonitor(inputChannels[1], host);
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -575,6 +587,9 @@
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
+        synchronized (mGestureMonitorPidsLock) {
+            mGestureMonitorPidsByToken.remove(inputChannel.getToken());
+        }
 
         nativeUnregisterInputChannel(mPtr, inputChannel);
     }
@@ -1716,7 +1731,37 @@
 
     // Binder call
     @Override
-    public void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, IBinder token) {
+    public void vibrate(int deviceId, VibrationEffect effect, IBinder token) {
+        long[] pattern;
+        int[] amplitudes;
+        int repeat;
+        if (effect instanceof VibrationEffect.OneShot) {
+            VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
+            pattern = new long[] { 0, oneShot.getDuration() };
+            int amplitude = oneShot.getAmplitude();
+            // android framework uses DEFAULT_AMPLITUDE to signal that the vibration
+            // should use some built-in default value, denoted here as DEFAULT_VIBRATION_MAGNITUDE
+            if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
+                amplitude = DEFAULT_VIBRATION_MAGNITUDE;
+            }
+            amplitudes = new int[] { 0, amplitude };
+            repeat = -1;
+        } else if (effect instanceof VibrationEffect.Waveform) {
+            VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
+            pattern = waveform.getTimings();
+            amplitudes = waveform.getAmplitudes();
+            for (int i = 0; i < amplitudes.length; i++) {
+                if (amplitudes[i] == VibrationEffect.DEFAULT_AMPLITUDE) {
+                    amplitudes[i] = DEFAULT_VIBRATION_MAGNITUDE;
+                }
+            }
+            repeat = waveform.getRepeatIndex();
+        } else {
+            // TODO: Add support for prebaked effects
+            Log.w(TAG, "Pre-baked effects aren't supported on input devices");
+            return;
+        }
+
         if (repeat >= pattern.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
@@ -1735,7 +1780,6 @@
                 mVibratorTokens.put(token, v);
             }
         }
-
         synchronized (v) {
             v.mVibrating = true;
             nativeVibrate(mPtr, deviceId, pattern, amplitudes, repeat, v.mTokenValue);
@@ -1838,6 +1882,7 @@
         if (dumpStr != null) {
             pw.println(dumpStr);
             dumpAssociations(pw);
+            dumpGestureMonitorPidsByToken(pw);
         }
     }
 
@@ -1861,6 +1906,19 @@
         }
     }
 
+    private void dumpGestureMonitorPidsByToken(PrintWriter pw) {
+        synchronized (mGestureMonitorPidsLock) {
+            if (!mGestureMonitorPidsByToken.isEmpty()) {
+                pw.println("Gesture monitor pids by token:");
+                for (int i = 0; i < mGestureMonitorPidsByToken.size(); i++) {
+                    pw.print("  " + i + ": ");
+                    pw.print(" token: " + mGestureMonitorPidsByToken.keyAt(i));
+                    pw.println(" pid: " + mGestureMonitorPidsByToken.valueAt(i));
+                }
+            }
+        }
+    }
+
     private boolean checkCallingPermission(String permission, String func) {
         // Quick check: if the calling permission is me, it's all okay.
         if (Binder.getCallingPid() == Process.myPid()) {
@@ -1883,6 +1941,7 @@
     public void monitor() {
         synchronized (mInputFilterLock) { }
         synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
+        synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */}
         nativeMonitor(mPtr);
     }
 
@@ -1944,6 +2003,9 @@
 
     // Native callback.
     private void notifyInputChannelBroken(IBinder token) {
+        synchronized (mGestureMonitorPidsLock) {
+            mGestureMonitorPidsByToken.remove(token);
+        }
         mWindowManagerCallbacks.notifyInputChannelBroken(token);
     }
 
@@ -1959,8 +2021,12 @@
     // Native callback.
     private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
             String reason) {
-        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle,
-                token, reason);
+        Integer gestureMonitorPid;
+        synchronized (mGestureMonitorPidsLock) {
+            gestureMonitorPid = mGestureMonitorPidsByToken.get(token);
+        }
+        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, token, gestureMonitorPid,
+                reason);
     }
 
     // Native callback.
@@ -2206,22 +2272,48 @@
      * Callback interface implemented by the Window Manager.
      */
     public interface WindowManagerCallbacks {
+        /**
+         * This callback is invoked when the confuguration changes.
+         */
         public void notifyConfigurationChanged();
 
+        /**
+         * This callback is invoked when the lid switch changes state.
+         * @param whenNanos the time when the change occurred
+         * @param lidOpen true if the lid is open
+         */
         public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
 
+        /**
+         * This callback is invoked when the camera lens cover switch changes state.
+         * @param whenNanos the time when the change occurred
+         * @param lensCovered true is the lens is covered
+         */
         public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
 
+        /**
+         * This callback is invoked when an input channel is closed unexpectedly.
+         * @param token the connection token of the broken channel
+         */
         public void notifyInputChannelBroken(IBinder token);
 
         /**
-         * Notifies the window manager about an application that is not responding.
-         * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
+         * Notify the window manager about an application that is not responding.
+         * Return a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
          */
         long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
-                String reason);
+                @Nullable Integer pid, String reason);
 
-        public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
+        /**
+         * This callback is invoked when an event first arrives to InputDispatcher and before it is
+         * placed onto InputDispatcher's queue. If this event is intercepted, it will never be
+         * processed by InputDispacher.
+         * @param event The key event that's arriving to InputDispatcher
+         * @param policyFlags The policy flags
+         * @return the flags that tell InputDispatcher how to handle the event (for example, whether
+         * to pass it to the user)
+         */
+        int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
 
         /**
          * Provides an opportunity for the window manager policy to intercept early motion event
@@ -2231,11 +2323,23 @@
         int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
                 int policyFlags);
 
-        public long interceptKeyBeforeDispatching(IBinder token,
-                KeyEvent event, int policyFlags);
+        /**
+         * This callback is invoked just before the key is about to be sent to an application.
+         * This allows the policy to make some last minute decisions on whether to intercept this
+         * key.
+         * @param token the window token that's about to receive this event
+         * @param event the key event that's being dispatched
+         * @param policyFlags the policy flags
+         * @return negative value if the key should be skipped (not sent to the app). 0 if the key
+         * should proceed getting dispatched to the app. positive value to indicate the additional
+         * time delay, in nanoseconds, to wait before sending this key to the app.
+         */
+        long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags);
 
-        public KeyEvent dispatchUnhandledKey(IBinder token,
-                KeyEvent event, int policyFlags);
+        /**
+         * Dispatch unhandled key
+         */
+        KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags);
 
         public int getPointerLayer();
 
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index c4f8441..ac9b7ea 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -318,8 +318,7 @@
                     SurfaceControl.setDisplayBrightness(mDisplayToken, brightness);
                 } else {
                     // Old system
-                    int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(
-                            getContext(), brightness);
+                    int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(brightness);
                     int color = brightnessInt & 0x000000ff;
                     color = 0xff000000 | (color << 16) | (color << 8) | color;
                     setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
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/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index e17cca4..cea5a69 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -88,7 +88,7 @@
 
     // Default time limit in milliseconds for the ConnectivityManager to find a suitable
     // network with SUPL connectivity or report an error.
-    private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000;
+    private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000;
 
     private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5;
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index f1b89c7..d6e37bac 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -33,6 +33,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.internal.widget.LockPatternUtils.USER_FRP;
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW;
 import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
 import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
 
@@ -186,6 +187,9 @@
     private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
     private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
 
+    // TODO (b/145978626) LockSettingsService no longer accepts challenges in the verifyCredential
+    //  paths. These are temporarily left around to ensure that resetLockout works. It will be
+    //  removed once resetLockout is compartmentalized.
     // No challenge provided
     private static final int CHALLENGE_NONE = 0;
     // Challenge was provided from the external caller (non-LockSettingsService)
@@ -1308,7 +1312,7 @@
         try {
             doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
                     challengeType, challenge, profileHandle, null /* progressCallback */,
-                    resetLockouts);
+                    resetLockouts, 0 /* flags */);
         } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
                 | NoSuchAlgorithmException | NoSuchPaddingException
                 | InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -1608,8 +1612,8 @@
             // Verify the parent credential again, to make sure we have a fresh enough
             // auth token such that getDecryptedPasswordForTiedProfile() inside
             // setLockCredentialInternal() can function correctly.
-            verifyCredential(savedCredential, /* challenge */ 0,
-                    mUserManager.getProfileParent(userId).id);
+            verifyCredential(savedCredential, mUserManager.getProfileParent(userId).id,
+                    0 /* flags */);
             savedCredential.zeroize();
             savedCredential = LockscreenCredential.createNone();
         }
@@ -1724,7 +1728,7 @@
         fixateNewestUserKeyAuth(userId);
         // Refresh the auth token
         doVerifyCredential(credential, CHALLENGE_FROM_CALLER, 0, userId,
-                null /* progressCallback */);
+                null /* progressCallback */, 0 /* flags */);
         synchronizeUnifiedWorkChallengeForProfiles(userId, null);
         sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
         return true;
@@ -1835,7 +1839,7 @@
             throw new IllegalArgumentException("Non-OK response verifying a credential we just set "
                     + vcr.getResponseCode());
         }
-        byte[] token = vcr.getPayload();
+        byte[] token = vcr.getGatekeeperHAT();
         if (token == null) {
             throw new IllegalArgumentException("Empty payload verifying a credential we just set");
         }
@@ -1968,35 +1972,56 @@
             ICheckCredentialProgressCallback progressCallback) {
         checkPasswordReadPermission(userId);
         try {
-            return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback);
+            return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId, progressCallback,
+                    0 /* flags */);
         } finally {
             scheduleGc();
         }
     }
 
     @Override
+    @Nullable
     public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
-            long challenge, int userId) {
+            int userId, int flags) {
         checkPasswordReadPermission(userId);
-        @ChallengeType int challengeType = CHALLENGE_FROM_CALLER;
-        if (challenge == 0) {
-            Slog.w(TAG, "VerifyCredential called with challenge=0");
-            challengeType = CHALLENGE_NONE;
 
-        }
         try {
-            return doVerifyCredential(credential, challengeType, challenge, userId,
-                    null /* progressCallback */);
+            return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId,
+                    null /* progressCallback */, flags);
         } finally {
             scheduleGc();
         }
     }
 
+    @Override
+    public VerifyCredentialResponse verifyGatekeeperPassword(byte[] gatekeeperPassword,
+            long challenge, int userId) {
+        checkPasswordReadPermission(userId);
+
+        VerifyCredentialResponse response;
+        synchronized (mSpManager) {
+            response = mSpManager.verifyChallengeInternal(getGateKeeperService(),
+                    gatekeeperPassword, challenge, userId);
+        }
+        return response;
+    }
+
+    /**
+     * @param credential User's lockscreen credential
+     * @param challengeType Owner of the challenge
+     * @param challenge Challenge to be wrapped within Gatekeeper's HAT, if the credential is
+     *                  verified
+     * @param userId User to verify the credential for
+     * @param progressCallback Receive progress callbacks
+     * @param flags See {@link LockPatternUtils.VerifyFlag}
+     * @return See {@link VerifyCredentialResponse}
+     */
     private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
             @ChallengeType int challengeType, long challenge, int userId,
-            ICheckCredentialProgressCallback progressCallback) {
+            ICheckCredentialProgressCallback progressCallback,
+            @LockPatternUtils.VerifyFlag int flags) {
         return doVerifyCredential(credential, challengeType, challenge, userId,
-                progressCallback, null /* resetLockouts */);
+                progressCallback, null /* resetLockouts */, flags);
     }
 
     /**
@@ -2006,7 +2031,8 @@
     private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
             @ChallengeType int challengeType, long challenge, int userId,
             ICheckCredentialProgressCallback progressCallback,
-            @Nullable ArrayList<PendingResetLockout> resetLockouts) {
+            @Nullable ArrayList<PendingResetLockout> resetLockouts,
+            @LockPatternUtils.VerifyFlag int flags) {
         if (credential == null || credential.isNone()) {
             throw new IllegalArgumentException("Credential can't be null or empty");
         }
@@ -2017,7 +2043,7 @@
         }
         VerifyCredentialResponse response = null;
         response = spBasedDoVerifyCredential(credential, challengeType, challenge,
-                userId, progressCallback, resetLockouts);
+                userId, progressCallback, resetLockouts, flags);
         // The user employs synthetic password based credential.
         if (response != null) {
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -2050,7 +2076,7 @@
 
     @Override
     public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
-            long challenge, int userId) {
+            int userId, @LockPatternUtils.VerifyFlag int flags) {
         checkPasswordReadPermission(userId);
         if (!isManagedProfileWithUnifiedLock(userId)) {
             throw new IllegalArgumentException("User id must be managed profile with unified lock");
@@ -2059,10 +2085,11 @@
         // Unlock parent by using parent's challenge
         final VerifyCredentialResponse parentResponse = doVerifyCredential(
                 credential,
-                CHALLENGE_FROM_CALLER,
-                challenge,
+                CHALLENGE_NONE,
+                0L,
                 parentProfileId,
-                null /* progressCallback */);
+                null /* progressCallback */,
+                flags);
         if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
             // Failed, just return parent's response
             return parentResponse;
@@ -2071,9 +2098,10 @@
         try {
             // Unlock work profile, and work profile with unified lock must use password only
             return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
-                    CHALLENGE_FROM_CALLER,
-                    challenge,
-                    userId, null /* progressCallback */);
+                    CHALLENGE_NONE,
+                    0L,
+                    userId, null /* progressCallback */,
+                    flags);
         } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
                 | NoSuchAlgorithmException | NoSuchPaddingException
                 | InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -2132,8 +2160,8 @@
             unlockKeystore(credential.getCredential(), userId);
 
             Slog.i(TAG, "Unlocking user " + userId + " with token length "
-                    + response.getPayload().length);
-            unlockUser(userId, response.getPayload(), secretFromCredential(credential));
+                    + response.getGatekeeperHAT().length);
+            unlockUser(userId, response.getGatekeeperHAT(), secretFromCredential(credential));
 
             if (isManagedProfileWithSeparatedLock(userId)) {
                 setDeviceUnlockedForUser(userId);
@@ -2684,7 +2712,8 @@
     private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
             @ChallengeType int challengeType, long challenge,
             int userId, ICheckCredentialProgressCallback progressCallback,
-            @Nullable ArrayList<PendingResetLockout> resetLockouts) {
+            @Nullable ArrayList<PendingResetLockout> resetLockouts,
+            @LockPatternUtils.VerifyFlag int flags) {
 
         final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);
 
@@ -2705,6 +2734,8 @@
 
         final AuthenticationResult authResult;
         VerifyCredentialResponse response;
+        final boolean returnGkPw = (flags & VERIFY_FLAG_RETURN_GK_PW) != 0;
+
         synchronized (mSpManager) {
             if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
                 return null;
@@ -2717,8 +2748,8 @@
             long handle = getSyntheticPasswordHandleLocked(userId);
             authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
                     getGateKeeperService(), handle, userCredential, userId, progressCallback);
-
             response = authResult.gkResponse;
+
             // credential has matched
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                 // perform verifyChallenge with synthetic password which generates the real GK auth
@@ -2739,7 +2770,7 @@
                 if (resetLockouts == null) {
                     resetLockouts = new ArrayList<>();
                 }
-                resetLockouts.add(new PendingResetLockout(userId, response.getPayload()));
+                resetLockouts.add(new PendingResetLockout(userId, response.getGatekeeperHAT()));
             }
 
             onCredentialVerified(authResult.authToken, challengeType, challenge, resetLockouts,
@@ -2750,7 +2781,12 @@
             }
         }
 
-        return response;
+        if (response.isMatched() && returnGkPw) {
+            return new VerifyCredentialResponse.Builder()
+                    .setGatekeeperPassword(authResult.authToken.deriveGkPassword()).build();
+        } else {
+            return response;
+        }
     }
 
     private void onCredentialVerified(AuthenticationToken authToken,
@@ -3172,7 +3208,8 @@
             if (cred == null) {
                 return false;
             }
-            return doVerifyCredential(cred, CHALLENGE_NONE, 0, userId, null /* progressCallback */)
+            return doVerifyCredential(cred, CHALLENGE_NONE, 0, userId,
+                    null /* progressCallback */, 0 /* flags */)
                     .getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
         }
     }
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d644b1d..e31bf3d 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -482,11 +482,12 @@
                     (int status, WeaverReadResponse readResponse) -> {
                     switch (status) {
                         case WeaverReadStatus.OK:
-                            response[0] = new VerifyCredentialResponse(
-                                    fromByteArrayList(readResponse.value));
+                            response[0] = new VerifyCredentialResponse.Builder().setGatekeeperHAT(
+                                    fromByteArrayList(readResponse.value)).build();
                             break;
                         case WeaverReadStatus.THROTTLE:
-                            response[0] = new VerifyCredentialResponse(readResponse.timeout);
+                            response[0] = VerifyCredentialResponse
+                                    .fromTimeout(readResponse.timeout);
                             Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
                             break;
                         case WeaverReadStatus.INCORRECT_KEY:
@@ -494,7 +495,8 @@
                                 response[0] = VerifyCredentialResponse.ERROR;
                                 Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
                             } else {
-                                response[0] = new VerifyCredentialResponse(readResponse.timeout);
+                                response[0] = VerifyCredentialResponse
+                                        .fromTimeout(readResponse.timeout);
                                 Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
                                         + slot);
                             }
@@ -1007,7 +1009,8 @@
                 return result;
             }
             sid = GateKeeper.INVALID_SECURE_USER_ID;
-            applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
+            applicationId = transformUnderWeaverSecret(pwdToken,
+                    result.gkResponse.getGatekeeperHAT());
         } else {
             byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
             GateKeeperResponse response;
@@ -1045,7 +1048,7 @@
                     }
                 }
             } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
-                result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
+                result.gkResponse = VerifyCredentialResponse.fromTimeout(response.getTimeout());
                 return result;
             } else  {
                 result.gkResponse = VerifyCredentialResponse.ERROR;
@@ -1096,12 +1099,12 @@
             }
             VerifyCredentialResponse response = weaverVerify(slotId, null);
             if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
-                    response.getPayload() == null) {
+                    response.getGatekeeperHAT() == null) {
                 Slog.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
                 result.gkResponse = VerifyCredentialResponse.ERROR;
                 return result;
             }
-            secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
+            secdiscardable = SyntheticPasswordCrypto.decrypt(response.getGatekeeperHAT(),
                     PERSONALISATION_WEAVER_TOKEN, secdiscardable);
         }
         byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
@@ -1174,6 +1177,12 @@
      */
     public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
             @NonNull AuthenticationToken auth, long challenge, int userId) {
+        return verifyChallengeInternal(gatekeeper, auth.deriveGkPassword(), challenge, userId);
+    }
+
+    protected @Nullable VerifyCredentialResponse verifyChallengeInternal(
+            IGateKeeperService gatekeeper, @NonNull byte[] gatekeeperPassword, long challenge,
+            int userId) {
         byte[] spHandle = loadSyntheticPasswordHandle(userId);
         if (spHandle == null) {
             // There is no password handle associated with the given user, i.e. the user is not
@@ -1183,18 +1192,19 @@
         GateKeeperResponse response;
         try {
             response = gatekeeper.verifyChallenge(userId, challenge,
-                    spHandle, auth.deriveGkPassword());
+                    spHandle, gatekeeperPassword);
         } catch (RemoteException e) {
             Slog.e(TAG, "Fail to verify with gatekeeper " + userId, e);
             return VerifyCredentialResponse.ERROR;
         }
         int responseCode = response.getResponseCode();
         if (responseCode == GateKeeperResponse.RESPONSE_OK) {
-            VerifyCredentialResponse result = new VerifyCredentialResponse(response.getPayload());
+            VerifyCredentialResponse result = new VerifyCredentialResponse.Builder()
+                    .setGatekeeperHAT(response.getPayload()).build();
             if (response.getShouldReEnroll()) {
                 try {
                     response = gatekeeper.enroll(userId, spHandle, spHandle,
-                            auth.deriveGkPassword());
+                            gatekeeperPassword);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Failed to invoke gatekeeper.enroll", e);
                     response = GateKeeperResponse.ERROR;
@@ -1203,7 +1213,8 @@
                     spHandle = response.getPayload();
                     saveSyntheticPasswordHandle(spHandle, userId);
                     // Call self again to re-verify with updated handle
-                    return verifyChallenge(gatekeeper, auth, challenge, userId);
+                    return verifyChallengeInternal(gatekeeper, gatekeeperPassword, challenge,
+                            userId);
                 } else {
                     // Fall through, return result from the previous verification attempt.
                     Slog.w(TAG, "Fail to re-enroll SP handle for user " + userId);
@@ -1211,7 +1222,7 @@
             }
             return result;
         } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
-            return new VerifyCredentialResponse(response.getTimeout());
+            return VerifyCredentialResponse.fromTimeout(response.getTimeout());
         } else {
             return VerifyCredentialResponse.ERROR;
         }
diff --git a/services/core/java/com/android/server/media/HandlerExecutor.java b/services/core/java/com/android/server/media/HandlerExecutor.java
new file mode 100644
index 0000000..7c9e72b
--- /dev/null
+++ b/services/core/java/com/android/server/media/HandlerExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * An adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+public class HandlerExecutor implements Executor {
+    private final Handler mHandler;
+
+    public HandlerExecutor(@NonNull Handler handler) {
+        mHandler = Objects.requireNonNull(handler);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        if (!mHandler.post(command)) {
+            throw new RejectedExecutionException(mHandler + " is shutting down");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 5d1b7491..162c388 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -20,7 +20,6 @@
 import android.media.Session2CommandGroup;
 import android.media.Session2Token;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 3bd18f9..006d78e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -70,9 +70,9 @@
 
     static final int NTWK_BLOCKED_POWER = 0;
     static final int NTWK_ALLOWED_NON_METERED = 1;
-    static final int NTWK_BLOCKED_BLACKLIST = 2;
-    static final int NTWK_ALLOWED_WHITELIST = 3;
-    static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
+    static final int NTWK_BLOCKED_DENYLIST = 2;
+    static final int NTWK_ALLOWED_ALLOWLIST = 3;
+    static final int NTWK_ALLOWED_TMP_ALLOWLIST = 4;
     static final int NTWK_BLOCKED_BG_RESTRICT = 5;
     static final int NTWK_ALLOWED_DEFAULT = 6;
     static final int NTWK_ALLOWED_SYSTEM = 7;
@@ -269,12 +269,12 @@
                 return "blocked by power restrictions";
             case NTWK_ALLOWED_NON_METERED:
                 return "allowed on unmetered network";
-            case NTWK_BLOCKED_BLACKLIST:
-                return "blacklisted on metered network";
-            case NTWK_ALLOWED_WHITELIST:
-                return "whitelisted on metered network";
-            case NTWK_ALLOWED_TMP_WHITELIST:
-                return "temporary whitelisted on metered network";
+            case NTWK_BLOCKED_DENYLIST:
+                return "denylisted on metered network";
+            case NTWK_ALLOWED_ALLOWLIST:
+                return "allowlisted on metered network";
+            case NTWK_ALLOWED_TMP_ALLOWLIST:
+                return "temporary allowlisted on metered network";
             case NTWK_BLOCKED_BG_RESTRICT:
                 return "blocked when background is restricted";
             case NTWK_ALLOWED_DEFAULT:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d6557f6..295143e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -101,13 +101,13 @@
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_ALLOWLIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_ALLOWLIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
-import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BLACKLIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_DENYLIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
@@ -507,7 +507,7 @@
      * UIDs that have been initially white-listed by system to avoid restricted background.
      */
     @GuardedBy("mUidRulesFirstLock")
-    private final SparseBooleanArray mDefaultRestrictBackgroundWhitelistUids =
+    private final SparseBooleanArray mDefaultRestrictBackgroundAllowlistUids =
             new SparseBooleanArray();
 
     /**
@@ -515,7 +515,7 @@
      * but later revoked by user.
      */
     @GuardedBy("mUidRulesFirstLock")
-    private final SparseBooleanArray mRestrictBackgroundWhitelistRevokedUids =
+    private final SparseBooleanArray mRestrictBackgroundAllowlistRevokedUids =
             new SparseBooleanArray();
 
     /** Set of ifaces that are metered. */
@@ -582,7 +582,7 @@
     @GuardedBy("mUidRulesFirstLock")
     private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
 
-    // TODO: keep whitelist of system-critical services that should never have
+    // TODO: keep allowlist of system-critical services that should never have
     // rules enforced, such as system, phone, and radio UIDs.
 
     // TODO: migrate notifications to SystemUI
@@ -668,26 +668,26 @@
     }
 
     /**
-     * Whitelists pre-defined apps for restrict background, but only if the user didn't already
-     * revoke the whitelist.
+     * Allows pre-defined apps for restrict background, but only if the user didn't already
+     * revoked them.
      *
-     * @return whether any uid has been whitelisted.
+     * @return whether any uid has been allowlisted.
      */
     @GuardedBy("mUidRulesFirstLock")
-    boolean addDefaultRestrictBackgroundWhitelistUidsUL() {
+    boolean addDefaultRestrictBackgroundAllowlistUidsUL() {
         final List<UserInfo> users = mUserManager.getUsers();
         final int numberUsers = users.size();
 
         boolean changed = false;
         for (int i = 0; i < numberUsers; i++) {
             final UserInfo user = users.get(i);
-            changed = addDefaultRestrictBackgroundWhitelistUidsUL(user.id) || changed;
+            changed = addDefaultRestrictBackgroundAllowlistUidsUL(user.id) || changed;
         }
         return changed;
     }
 
     @GuardedBy("mUidRulesFirstLock")
-    private boolean addDefaultRestrictBackgroundWhitelistUidsUL(int userId) {
+    private boolean addDefaultRestrictBackgroundAllowlistUidsUL(int userId) {
         final SystemConfig sysConfig = SystemConfig.getInstance();
         final PackageManager pm = mContext.getPackageManager();
         final ArraySet<String> allowDataUsage = sysConfig.getAllowInDataUsageSave();
@@ -695,7 +695,7 @@
         for (int i = 0; i < allowDataUsage.size(); i++) {
             final String pkg = allowDataUsage.valueAt(i);
             if (LOGD)
-                Slog.d(TAG, "checking restricted background whitelisting for package " + pkg
+                Slog.d(TAG, "checking restricted background allowlisting for package " + pkg
                         + " and user " + userId);
             final ApplicationInfo app;
             try {
@@ -706,20 +706,20 @@
                 continue;
             }
             if (!app.isPrivilegedApp()) {
-                Slog.e(TAG, "addDefaultRestrictBackgroundWhitelistUidsUL(): "
+                Slog.e(TAG, "addDefaultRestrictBackgroundAllowlistUidsUL(): "
                         + "skipping non-privileged app  " + pkg);
                 continue;
             }
             final int uid = UserHandle.getUid(userId, app.uid);
-            mDefaultRestrictBackgroundWhitelistUids.append(uid, true);
+            mDefaultRestrictBackgroundAllowlistUids.append(uid, true);
             if (LOGD)
                 Slog.d(TAG, "Adding uid " + uid + " (user " + userId + ") to default restricted "
-                        + "background whitelist. Revoked status: "
-                        + mRestrictBackgroundWhitelistRevokedUids.get(uid));
-            if (!mRestrictBackgroundWhitelistRevokedUids.get(uid)) {
+                        + "background allowlist. Revoked status: "
+                        + mRestrictBackgroundAllowlistRevokedUids.get(uid));
+            if (!mRestrictBackgroundAllowlistRevokedUids.get(uid)) {
                 if (LOGD)
                     Slog.d(TAG, "adding default package " + pkg + " (uid " + uid + " for user "
-                            + userId + ") to restrict background whitelist");
+                            + userId + ") to restrict background allowlist");
                 setUidPolicyUncheckedUL(uid, POLICY_ALLOW_METERED_BACKGROUND, false);
                 changed = true;
             }
@@ -799,7 +799,7 @@
                                 }
                             });
 
-                    if (addDefaultRestrictBackgroundWhitelistUidsUL()) {
+                    if (addDefaultRestrictBackgroundAllowlistUidsUL()) {
                         writePolicyAL();
                     }
 
@@ -1005,14 +1005,14 @@
                 case ACTION_USER_ADDED:
                     synchronized (mUidRulesFirstLock) {
                         // Remove any persistable state for the given user; both cleaning up after a
-                        // USER_REMOVED, and one last sanity check during USER_ADDED
+                        // USER_REMOVED, and one last check during USER_ADDED
                         removeUserStateUL(userId, true, false);
                         // Removing outside removeUserStateUL since that can also be called when
                         // user resets app preferences.
                         mMeteredRestrictedUids.remove(userId);
                         if (action == ACTION_USER_ADDED) {
-                            // Add apps that are whitelisted by default.
-                            addDefaultRestrictBackgroundWhitelistUidsUL(userId);
+                            // Add apps that are allowlisted by default.
+                            addDefaultRestrictBackgroundAllowlistUidsUL(userId);
                         }
                         // Update global restrict for that user
                         synchronized (mNetworkPoliciesSecondLock) {
@@ -2196,12 +2196,12 @@
             in.setInput(fis, StandardCharsets.UTF_8.name());
 
              // Must save the <restrict-background> tags and convert them to <uid-policy> later,
-             // to skip UIDs that were explicitly blacklisted.
-            final SparseBooleanArray whitelistedRestrictBackground = new SparseBooleanArray();
+             // to skip UIDs that were explicitly denylisted.
+            final SparseBooleanArray allowlistedRestrictBackground = new SparseBooleanArray();
 
             int type;
             int version = VERSION_INIT;
-            boolean insideWhitelist = false;
+            boolean insideAllowlist = false;
             while ((type = in.next()) != END_DOCUMENT) {
                 final String tag = in.getName();
                 if (type == START_TAG) {
@@ -2340,28 +2340,28 @@
                             Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
                         }
                     } else if (TAG_WHITELIST.equals(tag)) {
-                        insideWhitelist = true;
-                    } else if (TAG_RESTRICT_BACKGROUND.equals(tag) && insideWhitelist) {
+                        insideAllowlist = true;
+                    } else if (TAG_RESTRICT_BACKGROUND.equals(tag) && insideAllowlist) {
                         final int uid = readIntAttribute(in, ATTR_UID);
-                        whitelistedRestrictBackground.append(uid, true);
-                    } else if (TAG_REVOKED_RESTRICT_BACKGROUND.equals(tag) && insideWhitelist) {
+                        allowlistedRestrictBackground.append(uid, true);
+                    } else if (TAG_REVOKED_RESTRICT_BACKGROUND.equals(tag) && insideAllowlist) {
                         final int uid = readIntAttribute(in, ATTR_UID);
-                        mRestrictBackgroundWhitelistRevokedUids.put(uid, true);
+                        mRestrictBackgroundAllowlistRevokedUids.put(uid, true);
                     }
                 } else if (type == END_TAG) {
                     if (TAG_WHITELIST.equals(tag)) {
-                        insideWhitelist = false;
+                        insideAllowlist = false;
                     }
 
                 }
             }
 
-            final int size = whitelistedRestrictBackground.size();
+            final int size = allowlistedRestrictBackground.size();
             for (int i = 0; i < size; i++) {
-                final int uid = whitelistedRestrictBackground.keyAt(i);
+                final int uid = allowlistedRestrictBackground.keyAt(i);
                 final int policy = mUidPolicy.get(uid, POLICY_NONE);
                 if ((policy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
-                    Slog.w(TAG, "ignoring restrict-background-whitelist for " + uid
+                    Slog.w(TAG, "ignoring restrict-background-allowlist for " + uid
                             + " because its policy is " + uidPoliciesToString(policy));
                     continue;
                 }
@@ -2533,13 +2533,13 @@
 
             out.endTag(null, TAG_POLICY_LIST);
 
-            // write all whitelists
+            // write all allowlists
             out.startTag(null, TAG_WHITELIST);
 
-            // revoked restrict background whitelist
-            int size = mRestrictBackgroundWhitelistRevokedUids.size();
+            // revoked restrict background allowlist
+            int size = mRestrictBackgroundAllowlistRevokedUids.size();
             for (int i = 0; i < size; i++) {
-                final int uid = mRestrictBackgroundWhitelistRevokedUids.keyAt(i);
+                final int uid = mRestrictBackgroundAllowlistRevokedUids.keyAt(i);
                 out.startTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
                 writeIntAttribute(out, ATTR_UID, uid);
                 out.endTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
@@ -2619,21 +2619,21 @@
         setUidPolicyUncheckedUL(uid, policy, false);
 
         final boolean notifyApp;
-        if (!isUidValidForWhitelistRulesUL(uid)) {
+        if (!isUidValidForAllowlistRulesUL(uid)) {
             notifyApp = false;
         } else {
-            final boolean wasBlacklisted = oldPolicy == POLICY_REJECT_METERED_BACKGROUND;
-            final boolean isBlacklisted = policy == POLICY_REJECT_METERED_BACKGROUND;
-            final boolean wasWhitelisted = oldPolicy == POLICY_ALLOW_METERED_BACKGROUND;
-            final boolean isWhitelisted = policy == POLICY_ALLOW_METERED_BACKGROUND;
-            final boolean wasBlocked = wasBlacklisted || (mRestrictBackground && !wasWhitelisted);
-            final boolean isBlocked = isBlacklisted || (mRestrictBackground && !isWhitelisted);
-            if ((wasWhitelisted && (!isWhitelisted || isBlacklisted))
-                    && mDefaultRestrictBackgroundWhitelistUids.get(uid)
-                    && !mRestrictBackgroundWhitelistRevokedUids.get(uid)) {
+            final boolean wasDenylisted = oldPolicy == POLICY_REJECT_METERED_BACKGROUND;
+            final boolean isDenylisted = policy == POLICY_REJECT_METERED_BACKGROUND;
+            final boolean wasAllowlisted = oldPolicy == POLICY_ALLOW_METERED_BACKGROUND;
+            final boolean isAllowlisted = policy == POLICY_ALLOW_METERED_BACKGROUND;
+            final boolean wasBlocked = wasDenylisted || (mRestrictBackground && !wasAllowlisted);
+            final boolean isBlocked = isDenylisted || (mRestrictBackground && !isAllowlisted);
+            if ((wasAllowlisted && (!isAllowlisted || isDenylisted))
+                    && mDefaultRestrictBackgroundAllowlistUids.get(uid)
+                    && !mRestrictBackgroundAllowlistRevokedUids.get(uid)) {
                 if (LOGD)
-                    Slog.d(TAG, "Adding uid " + uid + " to revoked restrict background whitelist");
-                mRestrictBackgroundWhitelistRevokedUids.append(uid, true);
+                    Slog.d(TAG, "Adding uid " + uid + " to revoked restrict background allowlist");
+                mRestrictBackgroundAllowlistRevokedUids.append(uid, true);
             }
             notifyApp = wasBlocked != isBlocked;
         }
@@ -2700,11 +2700,11 @@
         mLogger.removingUserState(userId);
         boolean changed = false;
 
-        // Remove entries from revoked default restricted background UID whitelist
-        for (int i = mRestrictBackgroundWhitelistRevokedUids.size() - 1; i >= 0; i--) {
-            final int uid = mRestrictBackgroundWhitelistRevokedUids.keyAt(i);
+        // Remove entries from revoked default restricted background UID allowlist
+        for (int i = mRestrictBackgroundAllowlistRevokedUids.size() - 1; i >= 0; i--) {
+            final int uid = mRestrictBackgroundAllowlistRevokedUids.keyAt(i);
             if (UserHandle.getUserId(uid) == userId) {
-                mRestrictBackgroundWhitelistRevokedUids.removeAt(i);
+                mRestrictBackgroundAllowlistRevokedUids.removeAt(i);
                 changed = true;
             }
         }
@@ -2913,7 +2913,7 @@
             Slog.d(TAG, "setRestrictBackgroundUL(): " + restrictBackground + "; reason: " + reason);
             final boolean oldRestrictBackground = mRestrictBackground;
             mRestrictBackground = restrictBackground;
-            // Must whitelist foreground apps before turning data saver mode on.
+            // Must allowlist foreground apps before turning data saver mode on.
             // TODO: there is no need to iterate through all apps here, just those in the foreground,
             // so it could call AM to get the UIDs of such apps, and iterate through them instead.
             updateRulesForRestrictBackgroundUL();
@@ -2966,7 +2966,7 @@
                 Binder.restoreCallingIdentity(token);
             }
             if (policy == POLICY_REJECT_METERED_BACKGROUND) {
-                // App is blacklisted.
+                // App is denylisted.
                 return RESTRICT_BACKGROUND_STATUS_ENABLED;
             }
             if (!mRestrictBackground) {
@@ -3543,25 +3543,25 @@
                     fout.decreaseIndent();
                 }
 
-                size = mDefaultRestrictBackgroundWhitelistUids.size();
+                size = mDefaultRestrictBackgroundAllowlistUids.size();
                 if (size > 0) {
-                    fout.println("Default restrict background whitelist uids:");
+                    fout.println("Default restrict background allowlist uids:");
                     fout.increaseIndent();
                     for (int i = 0; i < size; i++) {
                         fout.print("UID=");
-                        fout.print(mDefaultRestrictBackgroundWhitelistUids.keyAt(i));
+                        fout.print(mDefaultRestrictBackgroundAllowlistUids.keyAt(i));
                         fout.println();
                     }
                     fout.decreaseIndent();
                 }
 
-                size = mRestrictBackgroundWhitelistRevokedUids.size();
+                size = mRestrictBackgroundAllowlistRevokedUids.size();
                 if (size > 0) {
-                    fout.println("Default restrict background whitelist uids revoked by users:");
+                    fout.println("Default restrict background allowlist uids revoked by users:");
                     fout.increaseIndent();
                     for (int i = 0; i < size; i++) {
                         fout.print("UID=");
-                        fout.print(mRestrictBackgroundWhitelistRevokedUids.keyAt(i));
+                        fout.print(mRestrictBackgroundAllowlistRevokedUids.keyAt(i));
                         fout.println();
                     }
                     fout.decreaseIndent();
@@ -3882,7 +3882,7 @@
 
     @GuardedBy("mUidRulesFirstLock")
     void updateRuleForAppIdleUL(int uid) {
-        if (!isUidValidForBlacklistRulesUL(uid)) return;
+        if (!isUidValidForDenylistRulesUL(uid)) return;
 
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRuleForAppIdleUL: " + uid );
@@ -3915,13 +3915,13 @@
         final SparseIntArray blockedUids = new SparseIntArray();
         for (int i = 0; i < ruleCount; i++) {
             final int uid = mUidFirewallStandbyRules.keyAt(i);
-            if (!isUidValidForBlacklistRulesUL(uid)) {
+            if (!isUidValidForDenylistRulesUL(uid)) {
                 continue;
             }
             int oldRules = mUidRules.get(uid);
             if (enableChain) {
                 // Chain wasn't enabled before and the other power-related
-                // chains are whitelists, so we can clear the
+                // chains are allowlists, so we can clear the
                 // MASK_ALL_NETWORKS part of the rules and re-inform listeners if
                 // the effective rules result in blocking network access.
                 oldRules &= MASK_METERED_NETWORKS;
@@ -4079,10 +4079,10 @@
     // TODO: the MEDIA / DRM restriction might not be needed anymore, in which case both
     // methods below could be merged into a isUidValidForRules() method.
     @GuardedBy("mUidRulesFirstLock")
-    private boolean isUidValidForBlacklistRulesUL(int uid) {
+    private boolean isUidValidForDenylistRulesUL(int uid) {
         // allow rules on specific system services, and any apps
         if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID
-                || isUidValidForWhitelistRulesUL(uid)) {
+                || isUidValidForAllowlistRulesUL(uid)) {
             return true;
         }
 
@@ -4090,7 +4090,7 @@
     }
 
     @GuardedBy("mUidRulesFirstLock")
-    private boolean isUidValidForWhitelistRulesUL(int uid) {
+    private boolean isUidValidForAllowlistRulesUL(int uid) {
         return UserHandle.isApp(uid) && hasInternetPermissionUL(uid);
     }
 
@@ -4235,23 +4235,23 @@
 
     /**
      * Applies network rules to bandwidth controllers based on process state and user-defined
-     * restrictions (blacklist / whitelist).
+     * restrictions (allowlist / denylist).
      *
      * <p>
      * {@code netd} defines 3 firewall chains that govern whether an app has access to metered
      * networks:
      * <ul>
-     * <li>@{code bw_penalty_box}: UIDs added to this chain do not have access (blacklist).
-     * <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're
-     *     also blacklisted.
+     * <li>@{code bw_penalty_box}: UIDs added to this chain do not have access (denylist).
+     * <li>@{code bw_happy_box}: UIDs added to this chain have access (allowlist), unless they're
+     *     also denylisted.
      * <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}),
-     *     no UIDs other than those whitelisted will have access.
+     *     no UIDs other than those allowlisted will have access.
      * <ul>
      *
      * <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the
-     * {@link #setUidPolicy(int, int)} and {@link #addRestrictBackgroundWhitelistedUid(int)} /
-     * {@link #removeRestrictBackgroundWhitelistedUid(int)} methods (for blacklist and whitelist
-     * respectively): these methods set the proper internal state (blacklist / whitelist), then call
+     * {@link #setUidPolicy(int, int)} and {@link #addRestrictBackgroundAllowlistedUid(int)} /
+     * {@link #removeRestrictBackgroundDenylistedUid(int)} methods (for denylist and allowlist
+     * respectively): these methods set the proper internal state (denylist / allowlist), then call
      * this ({@link #updateRulesForDataUsageRestrictionsUL(int)}) to propagate the rules to
      * {@link INetworkManagementService}, but this method should also be called in events (like
      * Data Saver Mode flips or UID state changes) that might affect the foreground app, since the
@@ -4260,7 +4260,7 @@
      * <ul>
      * <li>When Data Saver mode is on, the foreground app should be temporarily added to
      *     {@code bw_happy_box} before the @{code bw_data_saver} chain is enabled.
-     * <li>If the foreground app is blacklisted by the user, it should be temporarily removed from
+     * <li>If the foreground app is denylisted by the user, it should be temporarily removed from
      *     {@code bw_penalty_box}.
      * <li>When the app leaves foreground state, the temporary changes above should be reverted.
      * </ul>
@@ -4285,7 +4285,7 @@
     }
 
     private void updateRulesForDataUsageRestrictionsULInner(int uid) {
-        if (!isUidValidForWhitelistRulesUL(uid)) {
+        if (!isUidValidForAllowlistRulesUL(uid)) {
             if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid);
             return;
         }
@@ -4295,8 +4295,8 @@
         final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
         final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid);
 
-        final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
-        final boolean isWhitelisted = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
+        final boolean isDenylisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+        final boolean isAllowlisted = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
         final int oldRule = oldUidRules & MASK_METERED_NETWORKS;
         int newRule = RULE_NONE;
 
@@ -4304,15 +4304,15 @@
         if (isRestrictedByAdmin) {
             newRule = RULE_REJECT_METERED;
         } else if (isForeground) {
-            if (isBlacklisted || (mRestrictBackground && !isWhitelisted)) {
+            if (isDenylisted || (mRestrictBackground && !isAllowlisted)) {
                 newRule = RULE_TEMPORARY_ALLOW_METERED;
-            } else if (isWhitelisted) {
+            } else if (isAllowlisted) {
                 newRule = RULE_ALLOW_METERED;
             }
         } else {
-            if (isBlacklisted) {
+            if (isDenylisted) {
                 newRule = RULE_REJECT_METERED;
-            } else if (mRestrictBackground && isWhitelisted) {
+            } else if (mRestrictBackground && isAllowlisted) {
                 newRule = RULE_ALLOW_METERED;
             }
         }
@@ -4321,8 +4321,8 @@
         if (LOGV) {
             Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
                     + ": isForeground=" +isForeground
-                    + ", isBlacklisted=" + isBlacklisted
-                    + ", isWhitelisted=" + isWhitelisted
+                    + ", isDenylisted=" + isDenylisted
+                    + ", isAllowlisted=" + isAllowlisted
                     + ", isRestrictedByAdmin=" + isRestrictedByAdmin
                     + ", oldRule=" + uidRulesToString(oldRule)
                     + ", newRule=" + uidRulesToString(newRule)
@@ -4339,49 +4339,49 @@
         // Second step: apply bw changes based on change of state.
         if (newRule != oldRule) {
             if (hasRule(newRule, RULE_TEMPORARY_ALLOW_METERED)) {
-                // Temporarily whitelist foreground app, removing from blacklist if necessary
+                // Temporarily allowlist foreground app, removing from denylist if necessary
                 // (since bw_penalty_box prevails over bw_happy_box).
 
-                setMeteredNetworkWhitelist(uid, true);
+                setMeteredNetworkAllowlist(uid, true);
                 // TODO: if statement below is used to avoid an unnecessary call to netd / iptables,
                 // but ideally it should be just:
-                //    setMeteredNetworkBlacklist(uid, isBlacklisted);
-                if (isBlacklisted) {
-                    setMeteredNetworkBlacklist(uid, false);
+                //    setMeteredNetworkDenylist(uid, isDenylisted);
+                if (isDenylisted) {
+                    setMeteredNetworkDenylist(uid, false);
                 }
             } else if (hasRule(oldRule, RULE_TEMPORARY_ALLOW_METERED)) {
-                // Remove temporary whitelist from app that is not on foreground anymore.
+                // Remove temporary allowlist from app that is not on foreground anymore.
 
                 // TODO: if statements below are used to avoid unnecessary calls to netd / iptables,
                 // but ideally they should be just:
-                //    setMeteredNetworkWhitelist(uid, isWhitelisted);
-                //    setMeteredNetworkBlacklist(uid, isBlacklisted);
-                if (!isWhitelisted) {
-                    setMeteredNetworkWhitelist(uid, false);
+                //    setMeteredNetworkAllowlist(uid, isAllowlisted);
+                //    setMeteredNetworkDenylist(uid, isDenylisted);
+                if (!isAllowlisted) {
+                    setMeteredNetworkAllowlist(uid, false);
                 }
-                if (isBlacklisted || isRestrictedByAdmin) {
-                    setMeteredNetworkBlacklist(uid, true);
+                if (isDenylisted || isRestrictedByAdmin) {
+                    setMeteredNetworkDenylist(uid, true);
                 }
             } else if (hasRule(newRule, RULE_REJECT_METERED)
                     || hasRule(oldRule, RULE_REJECT_METERED)) {
-                // Flip state because app was explicitly added or removed to blacklist.
-                setMeteredNetworkBlacklist(uid, (isBlacklisted || isRestrictedByAdmin));
-                if (hasRule(oldRule, RULE_REJECT_METERED) && isWhitelisted) {
-                    // Since blacklist prevails over whitelist, we need to handle the special case
-                    // where app is whitelisted and blacklisted at the same time (although such
-                    // scenario should be blocked by the UI), then blacklist is removed.
-                    setMeteredNetworkWhitelist(uid, isWhitelisted);
+                // Flip state because app was explicitly added or removed to denylist.
+                setMeteredNetworkDenylist(uid, (isDenylisted || isRestrictedByAdmin));
+                if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowlisted) {
+                    // Since dneylist prevails over allowlist, we need to handle the special case
+                    // where app is allowlisted and denylisted at the same time (although such
+                    // scenario should be blocked by the UI), then denylist is removed.
+                    setMeteredNetworkAllowlist(uid, isAllowlisted);
                 }
             } else if (hasRule(newRule, RULE_ALLOW_METERED)
                     || hasRule(oldRule, RULE_ALLOW_METERED)) {
-                // Flip state because app was explicitly added or removed to whitelist.
-                setMeteredNetworkWhitelist(uid, isWhitelisted);
+                // Flip state because app was explicitly added or removed to allowlist.
+                setMeteredNetworkAllowlist(uid, isAllowlisted);
             } else {
                 // All scenarios should have been covered above.
                 Log.wtf(TAG, "Unexpected change of metered UID state for " + uid
                         + ": foreground=" + isForeground
-                        + ", whitelisted=" + isWhitelisted
-                        + ", blacklisted=" + isBlacklisted
+                        + ", allowlisted=" + isAllowlisted
+                        + ", denylisted=" + isDenylisted
                         + ", isRestrictedByAdmin=" + isRestrictedByAdmin
                         + ", newRule=" + uidRulesToString(newUidRules)
                         + ", oldRule=" + uidRulesToString(oldUidRules));
@@ -4397,7 +4397,7 @@
      * listeners in case of change.
      * <p>
      * There are 3 power-related rules that affects whether an app has background access on
-     * non-metered networks, and when the condition applies and the UID is not whitelisted for power
+     * non-metered networks, and when the condition applies and the UID is not allowlisted for power
      * restriction, it's added to the equivalent firewall chain:
      * <ul>
      * <li>App is idle: {@code fw_standby} firewall chain.
@@ -4406,7 +4406,7 @@
      * </ul>
      * <p>
      * This method updates the power-related part of the {@link #mUidRules} for a given uid based on
-     * these modes, the UID process state (foreground or not), and the UIDwhitelist state.
+     * these modes, the UID process state (foreground or not), and the UID allowlist state.
      * <p>
      * <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
      */
@@ -4450,7 +4450,7 @@
     @GuardedBy("mUidRulesFirstLock")
     private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules,
             boolean isUidIdle) {
-        if (!isUidValidForBlacklistRulesUL(uid)) {
+        if (!isUidValidForDenylistRulesUL(uid)) {
             if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
             return RULE_NONE;
         }
@@ -4859,23 +4859,23 @@
         }
     }
 
-    private void setMeteredNetworkBlacklist(int uid, boolean enable) {
-        if (LOGV) Slog.v(TAG, "setMeteredNetworkBlacklist " + uid + ": " + enable);
+    private void setMeteredNetworkDenylist(int uid, boolean enable) {
+        if (LOGV) Slog.v(TAG, "setMeteredNetworkDenylist " + uid + ": " + enable);
         try {
-            mNetworkManager.setUidMeteredNetworkBlacklist(uid, enable);
+            mNetworkManager.setUidMeteredNetworkDenylist(uid, enable);
         } catch (IllegalStateException e) {
-            Log.wtf(TAG, "problem setting blacklist (" + enable + ") rules for " + uid, e);
+            Log.wtf(TAG, "problem setting denylist (" + enable + ") rules for " + uid, e);
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
     }
 
-    private void setMeteredNetworkWhitelist(int uid, boolean enable) {
-        if (LOGV) Slog.v(TAG, "setMeteredNetworkWhitelist " + uid + ": " + enable);
+    private void setMeteredNetworkAllowlist(int uid, boolean enable) {
+        if (LOGV) Slog.v(TAG, "setMeteredNetworkAllowlist " + uid + ": " + enable);
         try {
-            mNetworkManager.setUidMeteredNetworkWhitelist(uid, enable);
+            mNetworkManager.setUidMeteredNetworkAllowlist(uid, enable);
         } catch (IllegalStateException e) {
-            Log.wtf(TAG, "problem setting whitelist (" + enable + ") rules for " + uid, e);
+            Log.wtf(TAG, "problem setting allowlist (" + enable + ") rules for " + uid, e);
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
@@ -4936,7 +4936,7 @@
     }
 
     /**
-     * Add or remove a uid to the firewall blacklist for all network ifaces.
+     * Add or remove a uid to the firewall denylist for all network ifaces.
      */
     private void setUidFirewallRule(int chain, int uid, int rule) {
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
@@ -4966,7 +4966,7 @@
     }
 
     /**
-     * Add or remove a uid to the firewall blacklist for all network ifaces.
+     * Add or remove a uid to the firewall denylist for all network ifaces.
      */
     @GuardedBy("mUidRulesFirstLock")
     private void enableFirewallChainUL(int chain, boolean enable) {
@@ -4995,8 +4995,8 @@
             mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
             mNetworkManager
                     .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager.setUidMeteredNetworkWhitelist(uid, false);
-            mNetworkManager.setUidMeteredNetworkBlacklist(uid, false);
+            mNetworkManager.setUidMeteredNetworkAllowlist(uid, false);
+            mNetworkManager.setUidMeteredNetworkDenylist(uid, false);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e);
         } catch (RemoteException e) {
@@ -5189,13 +5189,13 @@
             reason = NTWK_ALLOWED_NON_METERED;
         }
         else if (hasRule(uidRules, RULE_REJECT_METERED)) {
-            reason = NTWK_BLOCKED_BLACKLIST;
+            reason = NTWK_BLOCKED_DENYLIST;
         }
         else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
-            reason = NTWK_ALLOWED_WHITELIST;
+            reason = NTWK_ALLOWED_ALLOWLIST;
         }
         else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
-            reason = NTWK_ALLOWED_TMP_WHITELIST;
+            reason = NTWK_ALLOWED_TMP_ALLOWLIST;
         }
         else if (isBackgroundRestricted) {
             reason = NTWK_BLOCKED_BG_RESTRICT;
@@ -5208,13 +5208,13 @@
         switch(reason) {
             case NTWK_ALLOWED_DEFAULT:
             case NTWK_ALLOWED_NON_METERED:
-            case NTWK_ALLOWED_TMP_WHITELIST:
-            case NTWK_ALLOWED_WHITELIST:
+            case NTWK_ALLOWED_TMP_ALLOWLIST:
+            case NTWK_ALLOWED_ALLOWLIST:
             case NTWK_ALLOWED_SYSTEM:
                 blocked = false;
                 break;
             case NTWK_BLOCKED_POWER:
-            case NTWK_BLOCKED_BLACKLIST:
+            case NTWK_BLOCKED_DENYLIST:
             case NTWK_BLOCKED_BG_RESTRICT:
                 blocked = true;
                 break;
@@ -5234,7 +5234,7 @@
         public void resetUserState(int userId) {
             synchronized (mUidRulesFirstLock) {
                 boolean changed = removeUserStateUL(userId, false, true);
-                changed = addDefaultRestrictBackgroundWhitelistUidsUL(userId) || changed;
+                changed = addDefaultRestrictBackgroundAllowlistUidsUL(userId) || changed;
                 if (changed) {
                     synchronized (mNetworkPoliciesSecondLock) {
                         writePolicyAL();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d71c33e..2d052da 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7288,12 +7288,12 @@
 
     @GuardedBy("mToastQueue")
     private void keepProcessAliveForToastIfNeededLocked(int pid) {
-        int toastCount = 0; // toasts from this pid
+        int toastCount = 0; // toasts from this pid, rendered by the app
         ArrayList<ToastRecord> list = mToastQueue;
         int n = list.size();
         for (int i = 0; i < n; i++) {
             ToastRecord r = list.get(i);
-            if (r.pid == pid) {
+            if (r.pid == pid && r.keepProcessAlive()) {
                 toastCount++;
             }
         }
diff --git a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
index 2b91a00..17e0b39 100644
--- a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
+++ b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
@@ -71,6 +71,13 @@
     }
 
     @Override
+    public boolean keepProcessAlive() {
+        // As custom toasts are rendered by the app, we need to keep the app alive for it to show
+        // the toast.
+        return true;
+    }
+
+    @Override
     public String toString() {
         return "CustomToastRecord{"
                 + Integer.toHexString(System.identityHashCode(this))
diff --git a/services/core/java/com/android/server/notification/toast/ToastRecord.java b/services/core/java/com/android/server/notification/toast/ToastRecord.java
index 7915f70..33906cc 100644
--- a/services/core/java/com/android/server/notification/toast/ToastRecord.java
+++ b/services/core/java/com/android/server/notification/toast/ToastRecord.java
@@ -85,4 +85,14 @@
         }
         pw.println(prefix + this);
     }
+
+    /**
+     * Returns whether it's necessary to bump the process state to keep it alive in order to show
+     * the toast.
+     */
+    public boolean keepProcessAlive() {
+        // By default we assume the toast is rendered by the systemUI. Any toast rendered by the app
+        // should override this method.
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 249b6801..07527c2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -270,11 +270,12 @@
     abstract boolean revertActiveSessions();
 
     /**
-     * Abandons the staged session with the given sessionId.
+     * Abandons the staged session with the given sessionId. Client should handle {@code false}
+     * return value carefully as failure here can leave device in inconsistent state.
      *
-     * @return {@code true} upon success, {@code false} if any remote exception occurs
+     * @return {@code true} upon success, {@code false} if any exception occurs
      */
-    abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
+    abstract boolean abortStagedSession(int sessionId);
 
     /**
      * Uninstalls given {@code apexPackage}.
@@ -753,17 +754,13 @@
         }
 
         @Override
-        boolean abortStagedSession(int sessionId) throws PackageManagerException {
+        boolean abortStagedSession(int sessionId) {
             try {
                 waitForApexService().abortStagedSession(sessionId);
                 return true;
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Unable to contact apexservice", re);
-                return false;
             } catch (Exception e) {
-                throw new PackageManagerException(
-                        PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                        "Failed to abort staged session : " + e.getMessage());
+                Slog.e(TAG, e.getMessage(), e);
+                return false;
             }
         }
 
@@ -1122,7 +1119,7 @@
         }
 
         @Override
-        boolean abortStagedSession(int sessionId) throws PackageManagerException {
+        boolean abortStagedSession(int sessionId) {
             throw new UnsupportedOperationException();
         }
 
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 92c0c6a..def9c78f 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -35,6 +35,9 @@
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.component.ParsedProvider;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
 import android.os.Process;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -48,6 +51,7 @@
 import android.util.SparseSetArray;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.FgThread;
@@ -61,6 +65,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.concurrent.Executor;
 
 /**
  * The entity responsible for filtering visibility between apps based on declarations in their
@@ -96,6 +101,12 @@
     private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>();
 
     /**
+     * Executor for running reasonably short background tasks such as building the initial
+     * visibility cache.
+     */
+    private final Executor mBackgroundExecutor;
+
+    /**
      * Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of
      * protected broadcast. This in turn invalidates all prior additions and require a very
      * computationally expensive recomputing.
@@ -125,6 +136,8 @@
     private PackageParser.SigningDetails mSystemSigningDetails;
     private Set<String> mProtectedBroadcasts = new ArraySet<>();
 
+    private final Object mCacheLock = new Object();
+
     /**
      * This structure maps uid -> uid and indicates whether access from the first should be
      * filtered to the second. It's essentially a cache of the
@@ -132,6 +145,7 @@
      * NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on
      * initial scam and is null until {@link #onSystemReady()} is called.
      */
+    @GuardedBy("mCacheLock")
     private volatile SparseArray<SparseBooleanArray> mShouldFilterCache;
 
     @VisibleForTesting(visibility = PRIVATE)
@@ -139,13 +153,15 @@
             FeatureConfig featureConfig,
             String[] forceQueryableList,
             boolean systemAppsQueryable,
-            @Nullable OverlayReferenceMapper.Provider overlayProvider) {
+            @Nullable OverlayReferenceMapper.Provider overlayProvider,
+            Executor backgroundExecutor) {
         mFeatureConfig = featureConfig;
         mForceQueryableByDevicePackageNames = forceQueryableList;
         mSystemAppsQueryable = systemAppsQueryable;
         mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
                 overlayProvider);
         mStateProvider = stateProvider;
+        mBackgroundExecutor = backgroundExecutor;
     }
 
     /**
@@ -337,8 +353,13 @@
                         injector.getUserManagerInternal().getUserInfos());
             }
         };
+        HandlerThread appsFilterThread = new HandlerThread("appsFilter");
+        appsFilterThread.start();
+        Handler appsFilterHandler = new Handler(appsFilterThread.getLooper());
+        Executor executor = new HandlerExecutor(appsFilterHandler);
+
         AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig,
-                forcedQueryablePackageNames, forceSystemAppsQueryable, null);
+                forcedQueryablePackageNames, forceSystemAppsQueryable, null, executor);
         featureConfig.setAppsFilter(appsFilter);
         return appsFilter;
     }
@@ -470,29 +491,26 @@
             if (mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
                 Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
             }
-            if (mShouldFilterCache != null) {
-                // update the cache in a one-off manner since we've got all the information we need.
-                SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
-                if (visibleUids == null) {
-                    visibleUids = new SparseBooleanArray();
-                    mShouldFilterCache.put(recipientUid, visibleUids);
+            synchronized (mCacheLock) {
+                if (mShouldFilterCache != null) {
+                    // update the cache in a one-off manner since we've got all the information we
+                    // need.
+                    SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
+                    if (visibleUids == null) {
+                        visibleUids = new SparseBooleanArray();
+                        mShouldFilterCache.put(recipientUid, visibleUids);
+                    }
+                    visibleUids.put(visibleUid, false);
                 }
-                visibleUids.put(visibleUid, false);
             }
         }
     }
 
     public void onSystemReady() {
-        mStateProvider.runWithState(new StateProvider.CurrentStateCallback() {
-            @Override
-            public void currentState(ArrayMap<String, PackageSetting> settings,
-                    UserInfo[] users) {
-                mShouldFilterCache = new SparseArray<>(users.length * settings.size());
-            }
-        });
-        mFeatureConfig.onSystemReady();
         mOverlayReferenceMapper.rebuildIfDeferred();
-        updateEntireShouldFilterCache();
+        mFeatureConfig.onSystemReady();
+
+        updateEntireShouldFilterCacheAsync();
     }
 
     /**
@@ -510,10 +528,12 @@
             }
             mStateProvider.runWithState((settings, users) -> {
                 addPackageInternal(newPkgSetting, settings);
-                if (mShouldFilterCache != null) {
-                    updateShouldFilterCacheForPackage(
-                            null, newPkgSetting, settings, users, settings.size());
-                } // else, rebuild entire cache when system is ready
+                synchronized (mCacheLock) {
+                    if (mShouldFilterCache != null) {
+                        updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting,
+                                settings, users, settings.size());
+                    } // else, rebuild entire cache when system is ready
+                }
             });
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -607,6 +627,7 @@
         mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
     }
 
+    @GuardedBy("mCacheLock")
     private void removeAppIdFromVisibilityCache(int appId) {
         if (mShouldFilterCache == null) {
             return;
@@ -625,33 +646,47 @@
         }
     }
 
+    private void updateEntireShouldFilterCacheAsync() {
+        mBackgroundExecutor.execute(this::updateEntireShouldFilterCache);
+    }
+
     private void updateEntireShouldFilterCache() {
         mStateProvider.runWithState((settings, users) -> {
-            mShouldFilterCache.clear();
+            SparseArray<SparseBooleanArray> cache =
+                    new SparseArray<>(users.length * settings.size());
             for (int i = settings.size() - 1; i >= 0; i--) {
-                updateShouldFilterCacheForPackage(
+                updateShouldFilterCacheForPackage(cache,
                         null /*skipPackage*/, settings.valueAt(i), settings, users, i);
             }
+            synchronized (mCacheLock) {
+                mShouldFilterCache = cache;
+            }
         });
     }
 
     public void onUsersChanged() {
-        if (mShouldFilterCache != null) {
-            updateEntireShouldFilterCache();
+        synchronized (mCacheLock) {
+            if (mShouldFilterCache != null) {
+                updateEntireShouldFilterCache();
+            }
         }
     }
 
     private void updateShouldFilterCacheForPackage(String packageName) {
-        mStateProvider.runWithState((settings, users) -> {
-            updateShouldFilterCacheForPackage(null /* skipPackage */, settings.get(packageName),
-                    settings, users, settings.size() /*maxIndex*/);
-        });
-
+        synchronized (mCacheLock) {
+            if (mShouldFilterCache != null) {
+                mStateProvider.runWithState((settings, users) -> {
+                    updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */,
+                            settings.get(packageName), settings, users,
+                            settings.size() /*maxIndex*/);
+                });
+            }
+        }
     }
 
-    private void updateShouldFilterCacheForPackage(@Nullable String skipPackageName,
-            PackageSetting subjectSetting, ArrayMap<String, PackageSetting> allSettings,
-            UserInfo[] allUsers, int maxIndex) {
+    private void updateShouldFilterCacheForPackage(SparseArray<SparseBooleanArray> cache,
+            @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String,
+            PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) {
         for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
             PackageSetting otherSetting = allSettings.valueAt(i);
             if (subjectSetting.appId == otherSetting.appId) {
@@ -668,17 +703,17 @@
                 for (int ou = 0; ou < userCount; ou++) {
                     int otherUser = allUsers[ou].id;
                     int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId);
-                    if (!mShouldFilterCache.contains(subjectUid)) {
-                        mShouldFilterCache.put(subjectUid, new SparseBooleanArray(appxUidCount));
+                    if (!cache.contains(subjectUid)) {
+                        cache.put(subjectUid, new SparseBooleanArray(appxUidCount));
                     }
                     int otherUid = UserHandle.getUid(otherUser, otherSetting.appId);
-                    if (!mShouldFilterCache.contains(otherUid)) {
-                        mShouldFilterCache.put(otherUid, new SparseBooleanArray(appxUidCount));
+                    if (!cache.contains(otherUid)) {
+                        cache.put(otherUid, new SparseBooleanArray(appxUidCount));
                     }
-                    mShouldFilterCache.get(subjectUid).put(otherUid,
+                    cache.get(subjectUid).put(otherUid,
                             shouldFilterApplicationInternal(
                                     subjectUid, subjectSetting, otherSetting, otherUser));
-                    mShouldFilterCache.get(otherUid).put(subjectUid,
+                    cache.get(otherUid).put(subjectUid,
                             shouldFilterApplicationInternal(
                                     otherUid, otherSetting, subjectSetting, subjectUser));
                 }
@@ -712,7 +747,8 @@
      * This method recomputes all component / intent-based visibility and is intended to match the
      * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)}
      */
-    private void recomputeComponentVisibility(ArrayMap<String, PackageSetting> existingSettings) {
+    private void recomputeComponentVisibility(
+            ArrayMap<String, PackageSetting> existingSettings) {
         mQueriesViaComponent.clear();
         for (int i = existingSettings.size() - 1; i >= 0; i--) {
             PackageSetting setting = existingSettings.valueAt(i);
@@ -854,15 +890,17 @@
                 }
             }
 
-            removeAppIdFromVisibilityCache(setting.appId);
-            if (mShouldFilterCache != null && setting.sharedUser != null) {
-                for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
-                    PackageSetting siblingSetting = setting.sharedUser.packages.valueAt(i);
-                    if (siblingSetting == setting) {
-                        continue;
+            synchronized (mCacheLock) {
+                removeAppIdFromVisibilityCache(setting.appId);
+                if (mShouldFilterCache != null && setting.sharedUser != null) {
+                    for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
+                        PackageSetting siblingSetting = setting.sharedUser.packages.valueAt(i);
+                        if (siblingSetting == setting) {
+                            continue;
+                        }
+                        updateShouldFilterCacheForPackage(mShouldFilterCache, setting.name,
+                                siblingSetting, settings, users, settings.size());
                     }
-                    updateShouldFilterCacheForPackage(
-                            setting.name, siblingSetting, settings, users, settings.size());
                 }
             }
         });
@@ -888,26 +926,29 @@
                     || callingAppId == targetPkgSetting.appId) {
                 return false;
             }
-            if (mShouldFilterCache != null) { // use cache
-                SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid);
-                final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId);
-                if (shouldFilterTargets == null) {
-                    Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + callingUid);
-                    return true;
-                }
-                int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid);
-                if (indexOfTargetUid < 0) {
-                    Slog.w(TAG, "Encountered calling -> target with no cached rules: "
-                            + callingUid + " -> " + targetUid);
-                    return true;
-                }
-                if (!shouldFilterTargets.valueAt(indexOfTargetUid)) {
-                    return false;
-                }
-            } else {
-                if (!shouldFilterApplicationInternal(
-                        callingUid, callingSetting, targetPkgSetting, userId)) {
-                    return false;
+            synchronized (mCacheLock) {
+                if (mShouldFilterCache != null) { // use cache
+                    SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid);
+                    final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId);
+                    if (shouldFilterTargets == null) {
+                        Slog.wtf(TAG, "Encountered calling uid with no cached rules: "
+                                + callingUid);
+                        return true;
+                    }
+                    int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid);
+                    if (indexOfTargetUid < 0) {
+                        Slog.w(TAG, "Encountered calling -> target with no cached rules: "
+                                + callingUid + " -> " + targetUid);
+                        return true;
+                    }
+                    if (!shouldFilterTargets.valueAt(indexOfTargetUid)) {
+                        return false;
+                    }
+                } else {
+                    if (!shouldFilterApplicationInternal(
+                            callingUid, callingSetting, targetPkgSetting, userId)) {
+                        return false;
+                    }
                 }
             }
             if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(callingAppId)) {
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 0eaac41..9646b9c 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -52,6 +52,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import libcore.io.IoUtils;
 import libcore.util.HexEncoding;
@@ -112,6 +113,7 @@
     private static final String ATTR_GRANTED = "granted";
 
     private final PackageManagerService mService;
+    private final PermissionManagerServiceInternal mPermissionManager;
     private final CookiePersistence mCookiePersistence;
 
     /** State for uninstalled instant apps */
@@ -131,8 +133,10 @@
     @GuardedBy("mService.mLock")
     private SparseArray<SparseBooleanArray> mInstalledInstantAppUids;
 
-    public InstantAppRegistry(PackageManagerService service) {
+    public InstantAppRegistry(PackageManagerService service,
+            PermissionManagerServiceInternal permissionManager) {
         mService = service;
+        mPermissionManager = permissionManager;
         mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper());
     }
 
@@ -861,7 +865,8 @@
         String[] requestedPermissions = new String[pkg.getRequestedPermissions().size()];
         pkg.getRequestedPermissions().toArray(requestedPermissions);
 
-        Set<String> permissions = ps.getPermissionsState().getPermissions(userId);
+        Set<String> permissions = mPermissionManager.getGrantedPermissions(
+                pkg.getPackageName(), userId);
         String[] grantedPermissions = new String[permissions.size()];
         permissions.toArray(grantedPermissions);
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 55e7ca8..840645e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -442,7 +442,7 @@
         // After reboot housekeeping.
         for (int i = 0; i < mSessions.size(); ++i) {
             PackageInstallerSession session = mSessions.valueAt(i);
-            session.onAfterSessionRead();
+            session.onAfterSessionRead(mSessions);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7765f18..ff9edd5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -122,7 +122,7 @@
 import android.util.ExceptionUtils;
 import android.util.MathUtils;
 import android.util.Slog;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
 import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.R;
@@ -169,7 +169,7 @@
     private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 2;
     private static final int MSG_INSTALL = 3;
     private static final int MSG_ON_PACKAGE_INSTALLED = 4;
-    private static final int MSG_SESSION_VERIFICATION_FAILURE = 5;
+    private static final int MSG_SESSION_VALIDATION_FAILURE = 5;
 
     /** XML constants used for persisting a session */
     static final String TAG_SESSION = "session";
@@ -336,7 +336,7 @@
     @GuardedBy("mLock")
     private PackageParser.SigningDetails mSigningDetails;
     @GuardedBy("mLock")
-    private SparseIntArray mChildSessionIds = new SparseIntArray();
+    private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
     @GuardedBy("mLock")
     private int mParentSessionId;
 
@@ -475,10 +475,10 @@
                             packageName, returnCode, message, extras);
 
                     break;
-                case MSG_SESSION_VERIFICATION_FAILURE:
+                case MSG_SESSION_VALIDATION_FAILURE:
                     final int error = msg.arg1;
                     final String detailMessage = (String) msg.obj;
-                    onSessionVerificationFailure(error, detailMessage);
+                    onSessionValidationFailure(error, detailMessage);
                     break;
             }
 
@@ -589,7 +589,9 @@
         this.mShouldBeSealed = sealed;
         if (childSessionIds != null) {
             for (int childSessionId : childSessionIds) {
-                mChildSessionIds.put(childSessionId, 0);
+                // Null values will be resolved to actual object references in
+                // #onAfterSessionRead later.
+                mChildSessions.put(childSessionId, null);
             }
         }
         this.mParentSessionId = parentSessionId;
@@ -708,10 +710,7 @@
             info.isStaged = params.isStaged;
             info.rollbackDataPolicy = params.rollbackDataPolicy;
             info.parentSessionId = mParentSessionId;
-            info.childSessionIds = mChildSessionIds.copyKeys();
-            if (info.childSessionIds == null) {
-                info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
-            }
+            info.childSessionIds = getChildSessionIdsLocked();
             info.isStagedSessionApplied = mStagedSessionApplied;
             info.isStagedSessionReady = mStagedSessionReady;
             info.isStagedSessionFailed = mStagedSessionFailed;
@@ -1159,27 +1158,22 @@
             return;
         }
         if (isMultiPackage()) {
-            final SparseIntArray remainingSessions;
-            final int[] childSessionIds;
             synchronized (mLock) {
-                remainingSessions = mChildSessionIds.clone();
-                childSessionIds = mChildSessionIds.copyKeys();
-            }
-            final IntentSender childIntentSender =
-                    new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
-                            .getIntentSender();
-            boolean sealFailed = false;
-            for (int i = childSessionIds.length - 1; i >= 0; --i) {
-                final int childSessionId = childSessionIds[i];
-                // seal all children, regardless if any of them fail; we'll throw/return
-                // as appropriate once all children have been processed
-                if (!mSessionProvider.getSession(childSessionId)
-                        .markAsSealed(childIntentSender, forTransfer)) {
-                    sealFailed = true;
+                final IntentSender childIntentSender =
+                        new ChildStatusIntentReceiver(mChildSessions.clone(), statusReceiver)
+                                .getIntentSender();
+                boolean sealFailed = false;
+                for (int i = mChildSessions.size() - 1; i >= 0; --i) {
+                    // seal all children, regardless if any of them fail; we'll throw/return
+                    // as appropriate once all children have been processed
+                    if (!mChildSessions.valueAt(i)
+                            .markAsSealed(childIntentSender, forTransfer)) {
+                        sealFailed = true;
+                    }
                 }
-            }
-            if (sealFailed) {
-                return;
+                if (sealFailed) {
+                    return;
+                }
             }
         }
 
@@ -1218,21 +1212,20 @@
         }
 
         if (isMultiPackage()) {
-            final int[] childSessionIds;
+            final List<PackageInstallerSession> childSessions;
             synchronized (mLock) {
-                childSessionIds = mChildSessionIds.copyKeys();
+                childSessions = getChildSessionsLocked();
             }
-            int childCount = childSessionIds.length;
+            int childCount = childSessions.size();
 
             // This will contain all child sessions that do not encounter an unrecoverable failure
             ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);
 
             for (int i = childCount - 1; i >= 0; --i) {
-                final int childSessionId = childSessionIds[i];
                 // commit all children, regardless if any of them fail; we'll throw/return
                 // as appropriate once all children have been processed
                 try {
-                    PackageInstallerSession session = mSessionProvider.getSession(childSessionId);
+                    PackageInstallerSession session = childSessions.get(i);
                     allSessionsReady &= session.streamValidateAndCommit();
                     nonFailingSessions.add(session);
                 } catch (PackageManagerException e) {
@@ -1246,14 +1239,14 @@
             // the parent
             if (unrecoverableFailure != null) {
                 // {@link #streamValidateAndCommit()} calls
-                // {@link #onSessionVerificationFailure(PackageManagerException)}, but we don't
+                // {@link #onSessionValidationFailure(PackageManagerException)}, but we don't
                 // expect it to ever do so for parent sessions. Call that on this parent to clean
                 // it up and notify listeners of the error.
-                onSessionVerificationFailure(unrecoverableFailure);
+                onSessionValidationFailure(unrecoverableFailure);
                 // fail other child sessions that did not already fail
                 for (int i = nonFailingSessions.size() - 1; i >= 0; --i) {
                     PackageInstallerSession session = nonFailingSessions.get(i);
-                    session.onSessionVerificationFailure(unrecoverableFailure);
+                    session.onSessionValidationFailure(unrecoverableFailure);
                 }
             }
         }
@@ -1293,7 +1286,7 @@
     }
 
     private class ChildStatusIntentReceiver {
-        private final SparseIntArray mChildSessionsRemaining;
+        private final SparseArray<PackageInstallerSession> mChildSessionsRemaining;
         private final IntentSender mStatusReceiver;
         private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
@@ -1303,7 +1296,7 @@
             }
         };
 
-        private ChildStatusIntentReceiver(SparseIntArray remainingSessions,
+        private ChildStatusIntentReceiver(SparseArray<PackageInstallerSession> remainingSessions,
                 IntentSender statusReceiver) {
             this.mChildSessionsRemaining = remainingSessions;
             this.mStatusReceiver = statusReceiver;
@@ -1413,8 +1406,6 @@
     private boolean markAsSealed(@NonNull IntentSender statusReceiver, boolean forTransfer) {
         Objects.requireNonNull(statusReceiver);
 
-        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
-
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotDestroyedLocked("commit");
@@ -1446,7 +1437,7 @@
             }
 
             try {
-                sealLocked(childSessions);
+                sealLocked();
             } catch (PackageManagerException e) {
                 return false;
             }
@@ -1487,21 +1478,14 @@
         return true;
     }
 
-    /** Return a list of child sessions or null if the session is not multipackage
-     *
-     * <p> This method is handy to prevent potential deadlocks (b/123391593)
-     */
-    private @Nullable List<PackageInstallerSession> getChildSessionsNotLocked() {
-        if (Thread.holdsLock(mLock)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
-                    + " is holding mLock", new Throwable());
-        }
+    @GuardedBy("mLock")
+    private @Nullable List<PackageInstallerSession> getChildSessionsLocked() {
         List<PackageInstallerSession> childSessions = null;
         if (isMultiPackage()) {
-            final int[] childSessionIds = getChildSessionIds();
-            childSessions = new ArrayList<>(childSessionIds.length);
-            for (int childSessionId : childSessionIds) {
-                childSessions.add(mSessionProvider.getSession(childSessionId));
+            int size = mChildSessions.size();
+            childSessions = new ArrayList<>(size);
+            for (int i = 0; i < size; ++i) {
+                childSessions.add(mChildSessions.valueAt(i));
             }
         }
         return childSessions;
@@ -1563,23 +1547,23 @@
      *                                 session was sealed this is the only possible exception.
      */
     @GuardedBy("mLock")
-    private void sealLocked(List<PackageInstallerSession> childSessions)
+    private void sealLocked()
             throws PackageManagerException {
         try {
             assertNoWriteFileTransfersOpenLocked();
             assertPreparedAndNotDestroyedLocked("sealing of session");
 
             mSealed = true;
-
+            List<PackageInstallerSession> childSessions = getChildSessionsLocked();
             if (childSessions != null) {
                 assertMultiPackageConsistencyLocked(childSessions);
             }
         } catch (PackageManagerException e) {
-            throw onSessionVerificationFailure(e);
+            throw onSessionValidationFailure(e);
         } catch (Throwable e) {
             // Convert all exceptions into package manager exceptions as only those are handled
             // in the code above.
-            throw onSessionVerificationFailure(new PackageManagerException(e));
+            throw onSessionValidationFailure(new PackageManagerException(e));
         }
     }
 
@@ -1613,20 +1597,20 @@
             }
             return true;
         } catch (PackageManagerException e) {
-            throw onSessionVerificationFailure(e);
+            throw onSessionValidationFailure(e);
         } catch (Throwable e) {
             // Convert all exceptions into package manager exceptions as only those are handled
             // in the code above.
-            throw onSessionVerificationFailure(new PackageManagerException(e));
+            throw onSessionValidationFailure(new PackageManagerException(e));
         }
     }
 
-    private PackageManagerException onSessionVerificationFailure(PackageManagerException e) {
-        onSessionVerificationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
+    private PackageManagerException onSessionValidationFailure(PackageManagerException e) {
+        onSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
         return e;
     }
 
-    private void onSessionVerificationFailure(int error, String detailMessage) {
+    private void onSessionValidationFailure(int error, String detailMessage) {
         // Session is sealed but could not be verified, we need to destroy it.
         destroyInternal();
         // Dispatch message to remove session from PackageInstallerService.
@@ -1657,17 +1641,30 @@
      *
      * <p> This is meant to be called after all of the sessions are loaded and added to
      * PackageInstallerService
+     *
+     * @param allSessions All sessions loaded by PackageInstallerService, guaranteed to be
+     *                    immutable by the caller during the method call. Used to resolve child
+     *                    sessions Ids to actual object reference.
      */
-    void onAfterSessionRead() {
+    void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
         synchronized (mLock) {
+            // Resolve null values to actual object references
+            for (int i = mChildSessions.size() - 1; i >= 0; --i) {
+                int childSessionId = mChildSessions.keyAt(i);
+                PackageInstallerSession childSession = allSessions.get(childSessionId);
+                if (childSession != null) {
+                    mChildSessions.setValueAt(i, childSession);
+                } else {
+                    Slog.e(TAG, "Child session not existed: " + childSessionId);
+                    mChildSessions.removeAt(i);
+                }
+            }
+
             if (!mShouldBeSealed || isStagedAndInTerminalState()) {
                 return;
             }
-        }
-        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
-        synchronized (mLock) {
             try {
-                sealLocked(childSessions);
+                sealLocked();
 
                 if (isApexInstallation()) {
                     // APEX installations rely on certain fields to be populated after reboot.
@@ -1708,14 +1705,12 @@
             throw new SecurityException("Can only transfer sessions that use public options");
         }
 
-        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
-
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("transfer");
 
             try {
-                sealLocked(childSessions);
+                sealLocked();
             } catch (PackageManagerException e) {
                 throw new IllegalArgumentException("Package is not valid", e);
             }
@@ -1746,14 +1741,8 @@
             return;
         }
 
-        // For a multiPackage session, read the child sessions
-        // outside of the lock, because reading the child
-        // sessions with the lock held could lead to deadlock
-        // (b/123391593).
-        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
-
         try {
-            verifyNonStaged(childSessions);
+            verifyNonStaged();
         } catch (PackageManagerException e) {
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
             Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -1762,7 +1751,7 @@
         }
     }
 
-    private void verifyNonStaged(List<PackageInstallerSession> childSessions)
+    private void verifyNonStaged()
             throws PackageManagerException {
         final PackageManagerService.VerificationParams verifyingSession =
                 makeVerificationParams();
@@ -1770,6 +1759,10 @@
             return;
         }
         if (isMultiPackage()) {
+            final List<PackageInstallerSession> childSessions;
+            synchronized (mLock) {
+                childSessions = getChildSessionsLocked();
+            }
             List<PackageManagerService.VerificationParams> verifyingChildSessions =
                     new ArrayList<>(childSessions.size());
             boolean success = true;
@@ -1803,7 +1796,7 @@
         }
     }
 
-    private void installNonStaged(List<PackageInstallerSession> childSessions)
+    private void installNonStaged()
             throws PackageManagerException {
         final PackageManagerService.InstallParams installingSession =
                 makeInstallParams();
@@ -1811,6 +1804,10 @@
             return;
         }
         if (isMultiPackage()) {
+            final List<PackageInstallerSession> childSessions;
+            synchronized (mLock) {
+                childSessions = getChildSessionsLocked();
+            }
             List<PackageManagerService.InstallParams> installingChildSessions =
                     new ArrayList<>(childSessions.size());
             boolean success = true;
@@ -2004,9 +2001,8 @@
             return;
         }
 
-        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
         try {
-            installNonStaged(childSessions);
+            installNonStaged();
         } catch (PackageManagerException e) {
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
             Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -2091,7 +2087,7 @@
         if (ps == null) {
             return 0;
         }
-        final File apkDirOrPath = ps.codePath;
+        final File apkDirOrPath = ps.getCodePath();
         if (apkDirOrPath == null) {
             return 0;
         }
@@ -2741,15 +2737,6 @@
         }
     }
 
-    /**
-     * Adds a child session ID without any safety / sanity checks. This should only be used to
-     * build a session from XML or similar.
-     */
-    @GuardedBy("mLock")
-    void addChildSessionIdLocked(int sessionId) {
-        mChildSessionIds.put(sessionId, 0);
-    }
-
     public void open() throws IOException {
         if (mActiveCount.getAndIncrement() == 0) {
             mCallback.onSessionActiveChanged(this, true);
@@ -2804,7 +2791,6 @@
                             + getParentSessionId() +  " and may not be abandoned directly.");
         }
 
-        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
         synchronized (mLock) {
             if (params.isStaged && mDestroyed) {
                 // If a user abandons staged session in an unsafe state, then system will try to
@@ -2828,7 +2814,7 @@
                     mCallback.onStagedSessionChanged(this);
                     return;
                 }
-                cleanStageDir(childSessions);
+                cleanStageDir(getChildSessionsLocked());
             }
 
             if (mRelinquished) {
@@ -2990,7 +2976,7 @@
                         synchronized (mLock) {
                             mDataLoaderFinished = true;
                         }
-                        dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+                        dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                                 "Failure to obtain data loader");
                         return;
                     }
@@ -3040,7 +3026,7 @@
                             synchronized (mLock) {
                                 mDataLoaderFinished = true;
                             }
-                            dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+                            dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                                     "Failed to prepare image.");
                             if (manualStartAndDestroy) {
                                 dataLoader.destroy(dataLoaderId);
@@ -3061,7 +3047,7 @@
                             synchronized (mLock) {
                                 mDataLoaderFinished = true;
                             }
-                            dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+                            dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                                     "DataLoader reported unrecoverable failure.");
                             break;
                     }
@@ -3117,7 +3103,7 @@
                             synchronized (mLock) {
                                 mDataLoaderFinished = true;
                             }
-                            dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+                            dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                                     "Image is missing pages required for installation.");
                             break;
                     }
@@ -3142,15 +3128,22 @@
         return false;
     }
 
-    private void dispatchSessionVerificationFailure(int error, String detailMessage) {
-        mHandler.obtainMessage(MSG_SESSION_VERIFICATION_FAILURE, error, -1,
+    private void dispatchSessionValidationFailure(int error, String detailMessage) {
+        mHandler.obtainMessage(MSG_SESSION_VALIDATION_FAILURE, error, -1,
                 detailMessage).sendToTarget();
     }
 
     @GuardedBy("mLock")
     private int[] getChildSessionIdsLocked() {
-        final int[] childSessionIds = mChildSessionIds.copyKeys();
-        return childSessionIds != null ? childSessionIds : EMPTY_CHILD_SESSION_ARRAY;
+        int size = mChildSessions.size();
+        if (size == 0) {
+            return EMPTY_CHILD_SESSION_ARRAY;
+        }
+        final int[] childSessionIds = new int[size];
+        for (int i = 0; i < size; ++i) {
+            childSessionIds[i] = mChildSessions.keyAt(i);
+        }
+        return childSessionIds;
     }
 
     @Override
@@ -3205,12 +3198,12 @@
                 assertCallerIsOwnerOrRootLocked();
                 assertPreparedAndNotSealedLocked("addChildSessionId");
 
-                final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId);
+                final int indexOfSession = mChildSessions.indexOfKey(childSessionId);
                 if (indexOfSession >= 0) {
                     return;
                 }
                 childSession.setParentSessionId(this.sessionId);
-                addChildSessionIdLocked(childSessionId);
+                mChildSessions.put(childSessionId, childSession);
             }
         } finally {
             releaseTransactionLock();
@@ -3220,30 +3213,23 @@
 
     @Override
     public void removeChildSessionId(int sessionId) {
-        final PackageInstallerSession session = mSessionProvider.getSession(sessionId);
-        try {
-            acquireTransactionLock();
-            if (session != null) {
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotSealedLocked("removeChildSessionId");
+
+            final int indexOfSession = mChildSessions.indexOfKey(sessionId);
+            if (indexOfSession < 0) {
+                // not added in the first place; no-op
+                return;
+            }
+            PackageInstallerSession session = mChildSessions.valueAt(indexOfSession);
+            try {
+                acquireTransactionLock();
                 session.acquireTransactionLock();
-            }
-
-            synchronized (mLock) {
-                assertCallerIsOwnerOrRootLocked();
-                assertPreparedAndNotSealedLocked("removeChildSessionId");
-
-                final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
-                if (indexOfSession < 0) {
-                    // not added in the first place; no-op
-                    return;
-                }
-                if (session != null) {
-                    session.setParentSessionId(SessionInfo.INVALID_ID);
-                }
-                mChildSessionIds.removeAt(indexOfSession);
-            }
-        } finally {
-            releaseTransactionLock();
-            if (session != null) {
+                session.setParentSessionId(SessionInfo.INVALID_ID);
+                mChildSessions.removeAt(indexOfSession);
+            } finally {
+                releaseTransactionLock();
                 session.releaseTransactionLock();
             }
         }
@@ -3321,7 +3307,8 @@
     /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = true;
             mStagedSessionApplied = false;
             mStagedSessionFailed = false;
@@ -3332,33 +3319,38 @@
     }
 
     /** {@hide} */
-    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
-                                String errorMessage) {
+    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
+        List<PackageInstallerSession> childSessions;
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = false;
             mStagedSessionApplied = false;
             mStagedSessionFailed = true;
             mStagedSessionErrorCode = errorCode;
             mStagedSessionErrorMessage = errorMessage;
             Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
+            childSessions = getChildSessionsLocked();
         }
-        cleanStageDirNotLocked();
+        cleanStageDir(childSessions);
         mCallback.onStagedSessionChanged(this);
     }
 
     /** {@hide} */
     void setStagedSessionApplied() {
+        List<PackageInstallerSession> childSessions;
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = false;
             mStagedSessionApplied = true;
             mStagedSessionFailed = false;
             mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
             mStagedSessionErrorMessage = "";
             Slog.d(TAG, "Marking session " + sessionId + " as applied");
+            childSessions = getChildSessionsLocked();
         }
-        cleanStageDirNotLocked();
+        cleanStageDir(childSessions);
         mCallback.onStagedSessionChanged(this);
     }
 
@@ -3426,23 +3418,10 @@
         }
     }
 
-    /**
-     * <b>must not hold {@link #mLock}</b>
-     */
-    private void cleanStageDirNotLocked() {
-        if (Thread.holdsLock(mLock)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
-                    + " is holding mLock", new Throwable());
-        }
-        cleanStageDir(getChildSessionsNotLocked());
-    }
-
     private void cleanStageDir(List<PackageInstallerSession> childSessions) {
         if (childSessions != null) {
             for (PackageInstallerSession childSession : childSessions) {
-                if (childSession != null) {
-                    childSession.cleanStageDir();
-                }
+                childSession.cleanStageDir();
             }
         } else {
             cleanStageDir();
@@ -3502,7 +3481,7 @@
         pw.printPair("params.isMultiPackage", params.isMultiPackage);
         pw.printPair("params.isStaged", params.isStaged);
         pw.printPair("mParentSessionId", mParentSessionId);
-        pw.printPair("mChildSessionIds", mChildSessionIds);
+        pw.printPair("mChildSessionIds", getChildSessionIdsLocked());
         pw.printPair("mStagedSessionApplied", mStagedSessionApplied);
         pw.printPair("mStagedSessionFailed", mStagedSessionFailed);
         pw.printPair("mStagedSessionReady", mStagedSessionReady);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a726c8d..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.
@@ -2970,7 +2970,7 @@
             mHandler = new PackageHandler(mHandlerThread.getLooper());
             mProcessLoggingHandler = new ProcessLoggingHandler();
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
-            mInstantAppRegistry = new InstantAppRegistry(this);
+            mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager);
 
             ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
                     = systemConfig.getSharedLibraries();
@@ -3010,7 +3010,7 @@
             final int packageSettingCount = mSettings.mPackages.size();
             for (int i = packageSettingCount - 1; i >= 0; i--) {
                 PackageSetting ps = mSettings.mPackages.valueAt(i);
-                if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
+                if (!isExternal(ps) && (ps.getCodePath() == null || !ps.getCodePath().exists())
                         && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
                     mSettings.mPackages.removeAt(i);
                     mSettings.enableSystemPackageLPw(ps.name);
@@ -3175,11 +3175,11 @@
                             logCriticalInfo(Log.WARN,
                                     "Expecting better updated system app for " + ps.name
                                     + "; removing system app.  Last known"
-                                    + " codePath=" + ps.codePathString
+                                    + " codePath=" + ps.getCodePathString()
                                     + ", versionCode=" + ps.versionCode
                                     + "; scanned versionCode=" + scannedPkg.getLongVersionCode());
                             removePackageLI(scannedPkg, true);
-                            mExpectingBetter.put(ps.name, ps.codePath);
+                            mExpectingBetter.put(ps.name, ps.getCodePath());
                         }
 
                         continue;
@@ -3202,14 +3202,14 @@
                         // code path, but, changes the package name.
                         final PackageSetting disabledPs =
                                 mSettings.getDisabledSystemPkgLPr(ps.name);
-                        if (disabledPs.codePath == null || !disabledPs.codePath.exists()
+                        if (disabledPs.getCodePath() == null || !disabledPs.getCodePath().exists()
                                 || disabledPs.pkg == null) {
                             possiblyDeletedUpdatedSystemApps.add(ps.name);
                         } else {
                             // We're expecting that the system app should remain disabled, but add
                             // it to expecting better to recover in case the data version cannot
                             // be scanned.
-                            mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
+                            mExpectingBetter.put(disabledPs.name, disabledPs.getCodePath());
                         }
                     }
                 }
@@ -4385,14 +4385,13 @@
         final PackageUserState state = ps.readUserState(userId);
         AndroidPackage p = ps.pkg;
         if (p != null) {
-            final PermissionsState permissionsState = ps.getPermissionsState();
-
             // Compute GIDs only if requested
             final int[] gids = (flags & PackageManager.GET_GIDS) == 0
-                    ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
+                    ? EMPTY_INT_ARRAY : mPermissionManager.getPackageGids(ps.name, userId);
             // Compute granted permissions only if package has requested permissions
             final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions())
-                    ? Collections.emptySet() : permissionsState.getPermissions(userId);
+                    ? Collections.emptySet()
+                    : mPermissionManager.getGrantedPermissions(ps.name, userId);
 
             PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
                     ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps);
@@ -4863,13 +4862,13 @@
                 }
                 // TODO: Shouldn't this be checking for package installed state for userId and
                 // return null?
-                return ps.getPermissionsState().computeGids(userId);
+                return mPermissionManager.getPackageGids(packageName, userId);
             }
             if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps != null && ps.isMatch(flags)
                         && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
-                    return ps.getPermissionsState().computeGids(userId);
+                    return mPermissionManager.getPackageGids(packageName, userId);
                 }
             }
         }
@@ -8551,6 +8550,15 @@
             if (listUninstalled) {
                 list = new ArrayList<>(mSettings.mPackages.size());
                 for (PackageSetting ps : mSettings.mPackages.values()) {
+                    if (listFactory) {
+                        if (!ps.isSystem()) {
+                            continue;
+                        }
+                        PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+                        if (psDisabled != null) {
+                            ps = psDisabled;
+                        }
+                    }
                     if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                         continue;
                     }
@@ -8565,7 +8573,16 @@
             } else {
                 list = new ArrayList<>(mPackages.size());
                 for (AndroidPackage p : mPackages.values()) {
-                    final PackageSetting ps = getPackageSetting(p.getPackageName());
+                    PackageSetting ps = getPackageSetting(p.getPackageName());
+                    if (listFactory) {
+                        if (!p.isSystem()) {
+                            continue;
+                        }
+                        PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+                        if (psDisabled != null) {
+                            ps = psDisabled;
+                        }
+                    }
                     if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                         continue;
                     }
@@ -9150,7 +9167,7 @@
                 : getLastModifiedTime(parsedPackage);
         final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
         if (ps != null && !forceCollect
-                && ps.codePathString.equals(parsedPackage.getCodePath())
+                && ps.getCodePathString().equals(parsedPackage.getCodePath())
                 && ps.timeStamp == lastModifiedTime
                 && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
                 && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
@@ -9383,8 +9400,8 @@
             }
         }
 
-        final boolean newPkgChangedPaths =
-                pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath());
+        final boolean newPkgChangedPaths = pkgAlreadyExists
+                && !pkgSetting.getCodePathString().equals(parsedPackage.getCodePath());
         final boolean newPkgVersionGreater =
                 pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
         final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
@@ -9403,11 +9420,11 @@
                     "System package updated;"
                     + " name: " + pkgSetting.name
                     + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
-                    + "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath());
+                    + "; " + pkgSetting.getCodePathString()
+                            + " --> " + parsedPackage.getCodePath());
 
             final InstallArgs args = createInstallArgsForExisting(
-                    pkgSetting.codePathString,
-                    pkgSetting.resourcePathString, getAppDexInstructionSets(
+                    pkgSetting.getCodePathString(), getAppDexInstructionSets(
                             pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
             args.cleanUpResourcesLI();
             synchronized (mLock) {
@@ -9482,11 +9499,10 @@
                                 + " name: " + pkgSetting.name
                                 + "; " + pkgSetting.versionCode + " --> "
                                 + parsedPackage.getLongVersionCode()
-                                + "; " + pkgSetting.codePathString + " --> "
+                                + "; " + pkgSetting.getCodePathString() + " --> "
                                 + parsedPackage.getCodePath());
                 InstallArgs args = createInstallArgsForExisting(
-                        pkgSetting.codePathString,
-                        pkgSetting.resourcePathString, getAppDexInstructionSets(
+                        pkgSetting.getCodePathString(), getAppDexInstructionSets(
                                 pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
                 synchronized (mInstallLock) {
                     args.cleanUpResourcesLI();
@@ -9499,7 +9515,7 @@
                 logCriticalInfo(Log.INFO,
                         "System package disabled;"
                                 + " name: " + pkgSetting.name
-                                + "; old: " + pkgSetting.codePathString + " @ "
+                                + "; old: " + pkgSetting.getCodePathString() + " @ "
                                 + pkgSetting.versionCode
                                 + "; new: " + parsedPackage.getCodePath() + " @ "
                                 + parsedPackage.getCodePath());
@@ -10335,6 +10351,7 @@
                 mInstaller.rmPackageDir(codePath.getAbsolutePath());
                 if (codePathParent.getName().startsWith(RANDOM_DIR_PREFIX)) {
                     mInstaller.rmPackageDir(codePathParent.getAbsolutePath());
+                    removeCachedResult(codePathParent);
                 }
             } catch (InstallerException e) {
                 Slog.w(TAG, "Failed to remove code path", e);
@@ -10344,6 +10361,16 @@
         }
     }
 
+    private void removeCachedResult(@NonNull File codePath) {
+        if (mCacheDir == null) {
+            return;
+        }
+
+        final PackageCacher cacher = new PackageCacher(mCacheDir);
+        // Find and delete the cached result belong to the given codePath.
+        cacher.cleanCachedResult(codePath);
+    }
+
     private int[] resolveUserIds(int userId) {
         return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
     }
@@ -11325,7 +11352,7 @@
                         if (changedAbiCodePath == null) {
                             changedAbiCodePath = new ArrayList<>();
                         }
-                        changedAbiCodePath.add(ps.codePathString);
+                        changedAbiCodePath.add(ps.getCodePathString());
                     }
                 }
             }
@@ -11421,7 +11448,6 @@
 
         // Initialize package source and resource directories
         final File destCodeFile = new File(parsedPackage.getCodePath());
-        final File destResourceFile = new File(parsedPackage.getCodePath());
 
         // We keep references to the derived CPU Abis from settings in oder to reuse
         // them in the case where we're not upgrading or booting for the first time.
@@ -11479,7 +11505,7 @@
             // REMOVE SharedUserSetting from method; update in a separate call
             pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
                     originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
-                    destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(),
+                    destCodeFile, parsedPackage.getNativeLibraryRootDir(),
                     AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
                     AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
                     parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,
@@ -11497,7 +11523,7 @@
             // secondaryCpuAbi are not known at this point so we always update them
             // to null here, only to reset them at a later point.
             Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
-                    destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
+                    destCodeFile, parsedPackage.getNativeLibraryDir(),
                     AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
                     AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
                     PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
@@ -12066,15 +12092,13 @@
                     if (known != null) {
                         if (DEBUG_PACKAGE_SCANNING) {
                             Log.d(TAG, "Examining " + pkg.getCodePath()
-                                    + " and requiring known paths " + known.codePathString
-                                    + " & " + known.resourcePathString);
+                                    + " and requiring known path " + known.getCodePathString());
                         }
-                        if (!pkg.getCodePath().equals(known.codePathString)
-                                || !pkg.getCodePath().equals(known.resourcePathString)) {
+                        if (!pkg.getCodePath().equals(known.getCodePathString())) {
                             throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
                                     "Application package " + pkg.getPackageName()
                                     + " found at " + pkg.getCodePath()
-                                    + " but expected at " + known.codePathString
+                                    + " but expected at " + known.getCodePathString()
                                     + "; ignoring.");
                         }
                     } else {
@@ -14603,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 {
@@ -15586,9 +15610,8 @@
      * Create args that describe an existing installed package. Typically used
      * when cleaning up old installs, or used as a move source.
      */
-    private InstallArgs createInstallArgsForExisting(String codePath,
-            String resourcePath, String[] instructionSets) {
-        return new FileInstallArgs(codePath, resourcePath, instructionSets);
+    private InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) {
+        return new FileInstallArgs(codePath, instructionSets);
     }
 
     static abstract class InstallArgs {
@@ -15669,10 +15692,8 @@
         abstract boolean doRename(int status, ParsedPackage parsedPackage);
         abstract int doPostInstall(int status, int uid);
 
-        /** @see PackageSettingBase#codePathString */
+        /** @see PackageSettingBase#getCodePath() */
         abstract String getCodePath();
-        /** @see PackageSettingBase#resourcePathString */
-        abstract String getResourcePath();
 
         // Need installer lock especially for dex file removal.
         abstract void cleanUpResourcesLI();
@@ -15743,14 +15764,13 @@
         }
 
         /** Existing install */
-        FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
+        FileInstallArgs(String codePath, String[] instructionSets) {
             super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                     null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
                     PackageParser.SigningDetails.UNKNOWN,
                     PackageManager.INSTALL_REASON_UNKNOWN, false,
                     DataLoaderType.NONE);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
-            this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
         }
 
         int copyApk() {
@@ -15766,7 +15786,6 @@
             if (origin.staged) {
                 if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                 codeFile = origin.file;
-                resourceFile = origin.file;
                 return PackageManager.INSTALL_SUCCEEDED;
             }
 
@@ -15775,7 +15794,6 @@
                 final File tempDir =
                         mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
                 codeFile = tempDir;
-                resourceFile = tempDir;
             } catch (IOException e) {
                 Slog.w(TAG, "Failed to create copy file: " + e);
                 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -15846,7 +15864,6 @@
 
             // Reflect the rename internally
             codeFile = afterCodeFile;
-            resourceFile = afterCodeFile;
 
             // Reflect the rename in scanned details
             try {
@@ -15875,11 +15892,6 @@
             return (codeFile != null) ? codeFile.getAbsolutePath() : null;
         }
 
-        @Override
-        String getResourcePath() {
-            return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
-        }
-
         private boolean cleanUp() {
             if (codeFile == null || !codeFile.exists()) {
                 return false;
@@ -15892,10 +15904,6 @@
 
             removeCodePathLI(codeFile);
 
-            if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
-                resourceFile.delete();
-            }
-
             return true;
         }
 
@@ -15927,7 +15935,6 @@
      */
     class MoveInstallArgs extends InstallArgs {
         private File codeFile;
-        private File resourceFile;
 
         /** New install */
         MoveInstallArgs(InstallParams params) {
@@ -15950,7 +15957,6 @@
 
             final String toPathName = new File(move.fromCodePath).getName();
             codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName);
-            resourceFile = codeFile;
             if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile);
 
             return PackageManager.INSTALL_SUCCEEDED;
@@ -15987,11 +15993,6 @@
             return (codeFile != null) ? codeFile.getAbsolutePath() : null;
         }
 
-        @Override
-        String getResourcePath() {
-            return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
-        }
-
         private boolean cleanUp(String volumeUuid) {
             final String toPathName = new File(move.fromCodePath).getName();
             final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
@@ -16777,7 +16778,6 @@
                         // installed.  We need to make sure to delete the older one's .apk.
                         res.removedInfo.args = createInstallArgsForExisting(
                                 oldPackage.getCodePath(),
-                                oldPackage.getCodePath(),
                                 getAppDexInstructionSets(
                                         AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
                                                 deletedPkgSetting),
@@ -18558,7 +18558,6 @@
         // user handle installed state
         int[] allUsers;
         /** enabled state of the uninstalled application */
-        final int origEnabledState;
         synchronized (mLock) {
             uninstalledPs = mSettings.mPackages.get(packageName);
             if (uninstalledPs == null) {
@@ -18574,10 +18573,6 @@
             }
 
             disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
-            // Save the enabled state before we delete the package. When deleting a stub
-            // application we always set the enabled state to 'disabled'.
-            origEnabledState = uninstalledPs == null
-                    ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId);
             // Static shared libs can be declared by any package, so let us not
             // allow removing a package if it provides a lib others depend on.
             pkg = mPackages.get(packageName);
@@ -18656,20 +18651,32 @@
             if (stubPkg != null && stubPkg.isStub()) {
                 final PackageSetting stubPs;
                 synchronized (mLock) {
-                    // restore the enabled state of the stub; the state is overwritten when
-                    // the stub is uninstalled
                     stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
-                    if (stubPs != null) {
-                        stubPs.setEnabled(origEnabledState, userId, "android");
-                    }
                 }
-                if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT
-                        || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
-                    if (DEBUG_COMPRESSION) {
-                        Slog.i(TAG, "Enabling system stub after removal; pkg: "
-                                + stubPkg.getPackageName());
+
+                if (stubPs != null) {
+                    boolean enable = false;
+                    for (int aUserId : allUsers) {
+                        if (stubPs.getInstalled(aUserId)) {
+                            int enabled = stubPs.getEnabled(aUserId);
+                            if (enabled == COMPONENT_ENABLED_STATE_DEFAULT
+                                    || enabled == COMPONENT_ENABLED_STATE_ENABLED) {
+                                enable = true;
+                                break;
+                            }
+                        }
                     }
-                    enableCompressedPackage(stubPkg, stubPs);
+
+                    if (enable) {
+                        if (DEBUG_COMPRESSION) {
+                            Slog.i(TAG, "Enabling system stub after removal; pkg: "
+                                    + stubPkg.getPackageName());
+                        }
+                        enableCompressedPackage(stubPkg, stubPs);
+                    } else if (DEBUG_COMPRESSION) {
+                        Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+                                + "after removal; pkg: " + stubPkg.getPackageName());
+                    }
                 }
             }
         }
@@ -18723,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 */);
             }
         }
 
@@ -18998,7 +19005,7 @@
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
-            installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
+            installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles,
                     outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(),
                     writeSettings);
         } catch (PackageManagerException e) {
@@ -19013,8 +19020,15 @@
                 // and re-enable it afterward.
                 final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
                 if (stubPs != null) {
-                    stubPs.setEnabled(
-                            COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
+                    int userId = action.user == null
+                            ? UserHandle.USER_ALL : action.user.getIdentifier();
+                    if (userId == UserHandle.USER_ALL) {
+                        for (int aUserId : allUserHandles) {
+                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
+                        }
+                    } else if (userId >= UserHandle.USER_SYSTEM) {
+                        stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
+                    }
                 }
             }
         }
@@ -19123,7 +19137,7 @@
         // Delete application code and resources only for parent packages
         if (deleteCodeAndResources && (outInfo != null)) {
             outInfo.args = createInstallArgsForExisting(
-                    ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(
+                    ps.getCodePathString(), getAppDexInstructionSets(
                             ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
             if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
         }
@@ -19660,7 +19674,7 @@
 
         final String[] packageNames = { packageName };
         final long[] ceDataInodes = { ps.getCeDataInode(userId) };
-        final String[] codePaths = { ps.codePathString };
+        final String[] codePaths = { ps.getCodePathString() };
 
         try {
             mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
@@ -22582,11 +22596,11 @@
             synchronized (mInstallLock) {
                 final AndroidPackage pkg;
                 try {
-                    pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
+                    pkg = scanPackageTracedLI(ps.getCodePath(), parseFlags, SCAN_INITIAL, 0, null);
                     loaded.add(pkg);
 
                 } catch (PackageManagerException e) {
-                    Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
+                    Slog.w(TAG, "Failed to scan " + ps.getCodePath() + ": " + e.getMessage());
                 }
 
                 if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
@@ -22657,33 +22671,33 @@
 
         final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
         synchronized (mInstallLock) {
-        synchronized (mLock) {
-            final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
-            for (PackageSetting ps : packages) {
-                if (ps.pkg == null) continue;
+            synchronized (mLock) {
+                final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
+                for (PackageSetting ps : packages) {
+                    if (ps.pkg == null) continue;
 
-                final AndroidPackage pkg = ps.pkg;
-                final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
-                final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
+                    final AndroidPackage pkg = ps.pkg;
+                    final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
+                    final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
 
-                try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
-                        "unloadPrivatePackagesInner")) {
-                    if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
-                            false, null)) {
-                        unloaded.add(pkg);
-                    } else {
-                        Slog.w(TAG, "Failed to unload " + ps.codePath);
+                    try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
+                            "unloadPrivatePackagesInner")) {
+                        if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
+                                false, null)) {
+                            unloaded.add(pkg);
+                        } else {
+                            Slog.w(TAG, "Failed to unload " + ps.getCodePath());
+                        }
                     }
+
+                    // Try very hard to release any references to this package
+                    // so we don't risk the system server being killed due to
+                    // open FDs
+                    AttributeCache.instance().removePackage(ps.name);
                 }
 
-                // Try very hard to release any references to this package
-                // so we don't risk the system server being killed due to
-                // open FDs
-                AttributeCache.instance().removePackage(ps.name);
+                mSettings.writeLPr();
             }
-
-            mSettings.writeLPr();
-        }
         }
 
         if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
@@ -22726,7 +22740,7 @@
             final int packageCount = mSettings.mPackages.size();
             for (int i = 0; i < packageCount; i++) {
                 final PackageSetting ps = mSettings.mPackages.valueAt(i);
-                codePaths.add(ps.codePath.getAbsolutePath());
+                codePaths.add(ps.getCodePath().getAbsolutePath());
             }
             return codePaths;
         }
@@ -23643,8 +23657,20 @@
         }
     }
 
-    void onNewUserCreated(final int userId) {
-        mPermissionManager.onNewUserCreated(userId);
+    void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) {
+        if (DEBUG_PERMISSIONS) {
+            Slog.d(TAG, "onNewUserCreated(id=" + userId
+                    + ", convertedFromPreCreated=" + convertedFromPreCreated + ")");
+        }
+        if (!convertedFromPreCreated) {
+            mPermissionManager.onNewUserCreated(userId);
+            return;
+        }
+        if (!readPermissionStateForUser(userId)) {
+            // Could not read the existing permissions, re-grant them.
+            Slog.i(TAG, "re-granting permissions for pre-created user " + userId);
+            mPermissionManager.onNewUserCreated(userId);
+        }
     }
 
     boolean readPermissionStateForUser(@UserIdInt int userId) {
@@ -24932,9 +24958,7 @@
 
         @Override
         public int[] getPermissionGids(String permissionName, int userId) {
-            synchronized (mLock) {
-                return getPermissionGidsLocked(permissionName, userId);
-            }
+            return mPermissionManager.getPermissionGids(permissionName, userId);
         }
 
         @Override
@@ -25255,16 +25279,6 @@
         return null;
     }
 
-    @GuardedBy("mLock")
-    public int[] getPermissionGidsLocked(String permissionName, int userId) {
-        BasePermission perm
-                = mPermissionManager.getPermissionSettings().getPermission(permissionName);
-        if (perm != null) {
-            return perm.computeGids(userId);
-        }
-        return null;
-    }
-
     @Override
     public int getRuntimePermissionsVersion(@UserIdInt int userId) {
         Preconditions.checkArgumentNonnegative(userId);
@@ -25398,18 +25412,7 @@
         int mode = mInjector.getAppOpsManager().checkOpNoThrow(
                 AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
                 Binder.getCallingUid(), packageName);
-        if (mode == MODE_ALLOWED) {
-            return false;
-        } else if (mode == MODE_IGNORED) {
-            return true;
-        } else {
-            synchronized (mLock) {
-                boolean manifestWhitelisted =
-                        mPackages.get(packageName).getAutoRevokePermissions()
-                                == ApplicationInfo.AUTO_REVOKE_DISALLOWED;
-                return manifestWhitelisted;
-            }
-        }
+        return mode == MODE_IGNORED;
     }
 
     @Override
@@ -25697,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/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 668f375..7aeec6d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -423,13 +423,15 @@
             final List<ApplicationInfo> list;
             if (packageName == null) {
                 final ParceledListSlice<ApplicationInfo> packages =
-                        mInterface.getInstalledApplications(
-                                PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+                        mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY
+                                        | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                                UserHandle.USER_SYSTEM);
                 list = packages.getList();
             } else {
                 list = new ArrayList<>(1);
-                list.add(mInterface.getApplicationInfo(packageName,
-                        PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM));
+                list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY
+                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                        UserHandle.USER_SYSTEM));
             }
             for (ApplicationInfo info : list) {
                 if (info.isUpdatedSystemApp()) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 432d7f3..a3a7273 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -71,13 +71,13 @@
     private PackageStateUnserialized pkgState = new PackageStateUnserialized();
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public PackageSetting(String name, String realName, File codePath, File resourcePath,
+    public PackageSetting(String name, String realName, @NonNull File codePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             long pVersionCode, int pkgFlags, int privateFlags,
             int sharedUserId, String[] usesStaticLibraries,
             long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) {
-        super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
+        super(name, realName, codePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                 pVersionCode, pkgFlags, privateFlags,
                 usesStaticLibraries, usesStaticLibrariesVersions);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 834303c..6010344 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -64,10 +64,8 @@
      * this is path to single base APK file; for cluster packages this is
      * path to the cluster directory.
      */
-    File codePath;
-    String codePathString;
-    File resourcePath;
-    String resourcePathString;
+    private File mCodePath;
+    private String mCodePathString;
 
     String[] usesStaticLibraries;
     long[] usesStaticLibrariesVersions;
@@ -138,7 +136,7 @@
 
     boolean forceQueryableOverride;
 
-    PackageSettingBase(String name, String realName, File codePath, File resourcePath,
+    PackageSettingBase(String name, String realName, @NonNull File codePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             long pVersionCode, int pkgFlags, int pkgPrivateFlags,
@@ -148,10 +146,7 @@
         this.realName = realName;
         this.usesStaticLibraries = usesStaticLibraries;
         this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
-        this.codePath = codePath;
-        this.codePathString = codePath.toString();
-        this.resourcePath = resourcePath;
-        this.resourcePathString = resourcePath.toString();
+        setCodePath(codePath);
         this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
         this.primaryCpuAbiString = primaryCpuAbiString;
         this.secondaryCpuAbiString = secondaryCpuAbiString;
@@ -235,8 +230,7 @@
     }
 
     private void doCopy(PackageSettingBase orig) {
-        codePath = orig.codePath;
-        codePathString = orig.codePathString;
+        setCodePath(orig.getCodePath());
         cpuAbiOverrideString = orig.cpuAbiOverrideString;
         firstInstallTime = orig.firstInstallTime;
         installPermissionsFixed = orig.installPermissionsFixed;
@@ -246,8 +240,6 @@
         legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
         // Intentionally skip mOldCodePaths; it's not relevant for copies
         primaryCpuAbiString = orig.primaryCpuAbiString;
-        resourcePath = orig.resourcePath;
-        resourcePathString = orig.resourcePathString;
         secondaryCpuAbiString = orig.secondaryCpuAbiString;
         signatures = orig.signatures;
         timeStamp = orig.timeStamp;
@@ -705,6 +697,20 @@
         return userState.harmfulAppWarning;
     }
 
+    PackageSettingBase setCodePath(@NonNull File codePath) {
+        this.mCodePath = codePath;
+        this.mCodePathString = codePath.toString();
+        return this;
+    }
+
+    File getCodePath() {
+        return mCodePath;
+    }
+
+    String getCodePathString() {
+        return mCodePathString;
+    }
+
     /**
      * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer)
      *
@@ -727,10 +733,7 @@
 
     protected PackageSettingBase updateFrom(PackageSettingBase other) {
         super.copyFrom(other);
-        this.codePath = other.codePath;
-        this.codePathString = other.codePathString;
-        this.resourcePath = other.resourcePath;
-        this.resourcePathString = other.resourcePathString;
+        setCodePath(other.getCodePath());
         this.usesStaticLibraries = other.usesStaticLibraries;
         this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
         this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 1805713..3e3e3c5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -538,7 +538,7 @@
             return null;
         }
         p.getPkgState().setUpdatedSystemApp(false);
-        PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
+        PackageSetting ret = addPackageLPw(name, p.realName, p.getCodePath(),
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
@@ -558,7 +558,7 @@
         mDisabledSysPackages.remove(name);
     }
 
-    PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
+    PackageSetting addPackageLPw(String name, String realName, File codePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
             pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
@@ -572,10 +572,9 @@
                     "Adding duplicate package, keeping first: " + name);
             return null;
         }
-        p = new PackageSetting(name, realName, codePath, resourcePath,
-                legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
-                cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags,
-                0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
+        p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
+                primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
+                pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
                 mimeGroups);
         p.appId = uid;
         if (registerExistingAppIdLPw(uid, p, name)) {
@@ -635,7 +634,7 @@
      */
     static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg,
             PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
-            File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
+            File codePath, String legacyNativeLibraryPath, String primaryCpuAbi,
             String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
             UserHandle installUser, boolean allowInstall, boolean instantApp,
             boolean virtualPreload, UserManagerService userManager,
@@ -646,12 +645,11 @@
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
                     + pkgName + " is adopting original package " + originalPkg.name);
             pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
-            pkgSetting.codePath = codePath;
+            pkgSetting.setCodePath(codePath);
             pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
             pkgSetting.pkgFlags = pkgFlags;
             pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
             pkgSetting.primaryCpuAbiString = primaryCpuAbi;
-            pkgSetting.resourcePath = resourcePath;
             pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
             // NOTE: Create a deeper copy of the package signatures so we don't
             // overwrite the signatures in the original package setting.
@@ -662,7 +660,7 @@
             // Update new package state.
             pkgSetting.setTimeStamp(codePath.lastModified());
         } else {
-            pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
+            pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
                     legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
                     null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
                     0 /*sharedUserId*/, usesStaticLibraries,
@@ -756,10 +754,9 @@
      */
     static void updatePackageSetting(@NonNull PackageSetting pkgSetting,
             @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser,
-            @NonNull File codePath, File resourcePath,
-            @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi,
-            @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags,
-            @NonNull UserManagerService userManager,
+            @NonNull File codePath, @Nullable String legacyNativeLibraryPath,
+            @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
+            int pkgPrivateFlags, @NonNull UserManagerService userManager,
             @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
             @Nullable Set<String> mimeGroupNames)
                     throws PackageManagerException {
@@ -773,12 +770,12 @@
                     "Updating application package " + pkgName + " failed");
         }
 
-        if (!pkgSetting.codePath.equals(codePath)) {
+        if (!pkgSetting.getCodePath().equals(codePath)) {
             final boolean isSystem = pkgSetting.isSystem();
             Slog.i(PackageManagerService.TAG,
                     "Update" + (isSystem ? " system" : "")
                     + " package " + pkgName
-                    + " code path from " + pkgSetting.codePathString
+                    + " code path from " + pkgSetting.getCodePathString()
                     + " to " + codePath.toString()
                     + "; Retain data and using new");
             if (!isSystem) {
@@ -800,19 +797,7 @@
                 // internal to external storage or vice versa.
                 pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
             }
-            pkgSetting.codePath = codePath;
-            pkgSetting.codePathString = codePath.toString();
-        }
-        if (!pkgSetting.resourcePath.equals(resourcePath)) {
-            final boolean isSystem = pkgSetting.isSystem();
-            Slog.i(PackageManagerService.TAG,
-                    "Update" + (isSystem ? " system" : "")
-                    + " package " + pkgName
-                    + " resource path from " + pkgSetting.resourcePathString
-                    + " to " + resourcePath.toString()
-                    + "; Retain data and using new");
-            pkgSetting.resourcePath = resourcePath;
-            pkgSetting.resourcePathString = resourcePath.toString();
+            pkgSetting.setCodePath(codePath);
         }
         // If what we are scanning is a system (and possibly privileged) package,
         // then make it so, regardless of whether it was previously installed only
@@ -2710,7 +2695,7 @@
 
     private void writePackageListLPrInternal(int creatingUserId) {
         // Only derive GIDs for active users (not dying)
-        final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true);
+        final List<UserInfo> users = getActiveUsers(UserManagerService.getInstance(), true);
         int[] userIds = new int[users.size()];
         for (int i = 0; i < userIds.length; i++) {
             userIds[i] = users.get(i).id;
@@ -2812,14 +2797,11 @@
         if (pkg.realName != null) {
             serializer.attribute(null, "realName", pkg.realName);
         }
-        serializer.attribute(null, "codePath", pkg.codePathString);
+        serializer.attribute(null, "codePath", pkg.getCodePathString());
         serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
         serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
         serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
         serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
-        if (!pkg.resourcePathString.equals(pkg.codePathString)) {
-            serializer.attribute(null, "resourcePath", pkg.resourcePathString);
-        }
         if (pkg.legacyNativeLibraryPathString != null) {
             serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
         }
@@ -2857,10 +2839,7 @@
         if (pkg.realName != null) {
             serializer.attribute(null, "realName", pkg.realName);
         }
-        serializer.attribute(null, "codePath", pkg.codePathString);
-        if (!pkg.resourcePathString.equals(pkg.codePathString)) {
-            serializer.attribute(null, "resourcePath", pkg.resourcePathString);
-        }
+        serializer.attribute(null, "codePath", pkg.getCodePathString());
 
         if (pkg.legacyNativeLibraryPathString != null) {
             serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
@@ -3559,13 +3538,10 @@
         String name = parser.getAttributeValue(null, ATTR_NAME);
         String realName = parser.getAttributeValue(null, "realName");
         String codePathStr = parser.getAttributeValue(null, "codePath");
-        String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
 
         String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi");
         String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
 
-        String parentPackageName = parser.getAttributeValue(null, "parentPackageName");
-
         String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi");
         String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi");
         String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride");
@@ -3574,9 +3550,6 @@
             primaryCpuAbiStr = legacyCpuAbiStr;
         }
 
-        if (resourcePathStr == null) {
-            resourcePathStr = codePathStr;
-        }
         String version = parser.getAttributeValue(null, "version");
         long versionCode = 0;
         if (version != null) {
@@ -3593,9 +3566,8 @@
             pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
         }
         PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
-                new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
-                secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
-                0 /*sharedUserId*/, null, null, null);
+                legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
+                versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null);
         String timeStampStr = parser.getAttributeValue(null, "ft");
         if (timeStampStr != null) {
             try {
@@ -3666,7 +3638,6 @@
         String idStr = null;
         String sharedIdStr = null;
         String codePathStr = null;
-        String resourcePathStr = null;
         String legacyCpuAbiString = null;
         String legacyNativeLibraryPathStr = null;
         String primaryCpuAbiString = null;
@@ -3700,7 +3671,6 @@
             uidError = parser.getAttributeValue(null, "uidError");
             sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
             codePathStr = parser.getAttributeValue(null, "codePath");
-            resourcePathStr = parser.getAttributeValue(null, "resourcePath");
 
             legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
 
@@ -3818,9 +3788,6 @@
                         + " sharedUserId=" + sharedIdStr);
             final int userId = idStr != null ? Integer.parseInt(idStr) : 0;
             final int sharedUserId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
-            if (resourcePathStr == null) {
-                resourcePathStr = codePathStr;
-            }
             if (realName != null) {
                 realName = realName.intern();
             }
@@ -3834,10 +3801,10 @@
                                 + parser.getPositionDescription());
             } else if (userId > 0) {
                 packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
-                        new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
-                        secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
-                        pkgPrivateFlags, null /*usesStaticLibraries*/,
-                        null /*usesStaticLibraryVersions*/, null /*mimeGroups*/);
+                        legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
+                        cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
+                        null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/,
+                        null /*mimeGroups*/);
                 if (PackageManagerService.DEBUG_SETTINGS)
                     Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
                             + userId + " pkg=" + packageSetting);
@@ -3852,8 +3819,8 @@
                 }
             } else if (sharedIdStr != null) {
                 if (sharedUserId > 0) {
-                    packageSetting = new PackageSetting(name.intern(), realName, new File(
-                            codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
+                    packageSetting = new PackageSetting(name.intern(), realName,
+                            new File(codePathStr), legacyNativeLibraryPathStr,
                             primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                             versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
                             null /*usesStaticLibraries*/,
@@ -4456,25 +4423,43 @@
     }
 
     /**
-     * Return all users on the device, including partial or dying users.
+     * Returns all users on the device, including pre-created and dying users.
+     *
      * @param userManager UserManagerService instance
      * @return the list of users
      */
     private static List<UserInfo> getAllUsers(UserManagerService userManager) {
-        return getUsers(userManager, false);
+        return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false);
     }
 
     /**
-     * Return the list of users on the device. Clear the calling identity before calling into
-     * UserManagerService.
+     * Returns the list of users on the device, excluding pre-created ones.
+     *
      * @param userManager UserManagerService instance
      * @param excludeDying Indicates whether to exclude any users marked for deletion.
+     *
      * @return the list of users
      */
-    private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) {
+    private static List<UserInfo> getActiveUsers(UserManagerService userManager,
+            boolean excludeDying) {
+        return getUsers(userManager, excludeDying, /* excludePreCreated= */ true);
+    }
+
+    /**
+     * Returns the list of users on the device.
+     *
+     * @param userManager UserManagerService instance
+     * @param excludeDying Indicates whether to exclude any users marked for deletion.
+     * @param excludePreCreated Indicates whether to exclude any pre-created users.
+     *
+     * @return the list of users
+     */
+    private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying,
+            boolean excludePreCreated) {
         long id = Binder.clearCallingIdentity();
         try {
-            return userManager.getUsers(excludeDying);
+            return userManager.getUsers(/* excludePartial= */ true, excludeDying,
+                    excludePreCreated);
         } catch (NullPointerException npe) {
             // packagemanager not yet initialized
         } finally {
@@ -4657,9 +4642,9 @@
             pw.print(prefix); pw.print("  sharedUser="); pw.println(ps.sharedUser);
         }
         pw.print(prefix); pw.print("  pkg="); pw.println(pkg);
-        pw.print(prefix); pw.print("  codePath="); pw.println(ps.codePathString);
+        pw.print(prefix); pw.print("  codePath="); pw.println(ps.getCodePathString());
         if (permissionNames == null) {
-            pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.resourcePathString);
+            pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.getCodePathString());
             pw.print(prefix); pw.print("  legacyNativeLibraryDir=");
             pw.println(ps.legacyNativeLibraryPathString);
             pw.print(prefix); pw.print("  primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 89bdb3e..f9bf54a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -322,9 +322,6 @@
         }
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         if (activeVersion != session.params.requiredInstalledVersionCode) {
-            if (!mApexManager.abortStagedSession(session.sessionId)) {
-                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
-            }
             throw new PackageManagerException(
                     SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Installed version of APEX package " + activePackage.packageName
@@ -338,14 +335,11 @@
             throws PackageManagerException {
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         final long newVersionCode = newPackage.applicationInfo.longVersionCode;
-        boolean isAppDebuggable = (activePackage.applicationInfo.flags
+        final boolean isAppDebuggable = (activePackage.applicationInfo.flags
                 & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
         final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
                 session.params.installFlags, isAppDebuggable);
         if (activeVersion > newVersionCode && !allowsDowngrade) {
-            if (!mApexManager.abortStagedSession(session.sessionId)) {
-                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
-            }
             throw new PackageManagerException(
                     SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Downgrade of APEX package " + newPackage.packageName
@@ -835,37 +829,6 @@
         return null;
     }
 
-    private void verifyApksInSession(PackageInstallerSession session)
-            throws PackageManagerException {
-
-        final PackageInstallerSession apksToVerify = extractApksInSession(
-                session,  /* preReboot */ true);
-        if (apksToVerify == null) {
-            return;
-        }
-
-        final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
-                (Intent result) -> {
-                    int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                            PackageInstaller.STATUS_FAILURE);
-                    if (status != PackageInstaller.STATUS_SUCCESS) {
-                        final String errorMessage = result.getStringExtra(
-                                PackageInstaller.EXTRA_STATUS_MESSAGE);
-                        Slog.e(TAG, "Failure to verify APK staged session "
-                                + session.sessionId + " [" + errorMessage + "]");
-                        session.setStagedSessionFailed(
-                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
-                        mPreRebootVerificationHandler.onPreRebootVerificationComplete(
-                                session.sessionId);
-                        return;
-                    }
-                    mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
-                            session.sessionId);
-                });
-
-        apksToVerify.commit(receiver.getIntentSender(), false);
-    }
-
     private void installApksInSession(@NonNull PackageInstallerSession session)
             throws PackageManagerException {
 
@@ -908,10 +871,21 @@
         mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
     }
 
-    private int parentOrOwnSessionId(PackageInstallerSession session) {
+    private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
         return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
     }
 
+    private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) {
+        return session.hasParentSessionId()
+                ? getStagedSession(session.getParentSessionId())
+                : session;
+    }
+
+    private boolean isRollback(PackageInstallerSession session) {
+        final PackageInstallerSession root = getParentSessionOrSelf(session);
+        return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+    }
+
     /**
      * <p> Check if the session provided is non-overlapping with the active staged sessions.
      *
@@ -937,6 +911,8 @@
         boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
                 Context.STORAGE_SERVICE)).isCheckpointSupported();
 
+        final boolean isRollback = isRollback(session);
+
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
                 final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
@@ -951,8 +927,8 @@
                 }
                 // Check if stagedSession has an active parent session or not
                 if (stagedSession.hasParentSessionId()) {
-                    int parentId = stagedSession.getParentSessionId();
-                    PackageInstallerSession parentSession = mStagedSessions.get(parentId);
+                    final int parentId = stagedSession.getParentSessionId();
+                    final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
                     if (parentSession == null || parentSession.isStagedAndInTerminalState()
                             || parentSession.isDestroyed()) {
                         // Parent session has been abandoned or terminated already
@@ -968,21 +944,37 @@
                     continue;
                 }
 
-                // If session is not among the active sessions, then it cannot have same package
-                // name as any of the active sessions.
+                // New session cannot have same package name as one of the active sessions
                 if (session.getPackageName().equals(stagedSession.getPackageName())) {
-                    throw new PackageManagerException(
-                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
-                            "Package: " + session.getPackageName() + " in session: "
-                                    + session.sessionId + " has been staged already by session: "
-                                    + stagedSession.sessionId, null);
+                    if (isRollback) {
+                        // If the new session is a rollback, then it gets priority. The existing
+                        // session is failed to unblock rollback.
+                        final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
+                        if (!ensureActiveApexSessionIsAborted(root)) {
+                            Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
+                            // Safe to ignore active apex session abort failure since session
+                            // will be marked failed on next step and staging directory for session
+                            // will be deleted.
+                        }
+                        root.setStagedSessionFailed(
+                                SessionInfo.STAGED_SESSION_OTHER_ERROR,
+                                "Session was blocking rollback session: " + session.sessionId);
+                        Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
+                                + "blocking rollback session: " + session.sessionId);
+                    } else {
+                        throw new PackageManagerException(
+                                PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                                "Package: " + session.getPackageName() + " in session: "
+                                        + session.sessionId + " has been staged already by session:"
+                                        + " " + stagedSession.sessionId, null);
+                    }
                 }
 
                 // Staging multiple root sessions is not allowed if device doesn't support
                 // checkpoint. If session and stagedSession do not have common ancestor, they are
                 // from two different root sessions.
-                if (!supportsCheckpoint
-                        && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
+                if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
+                        != getSessionIdForParentOrSelf(stagedSession)) {
                     throw new PackageManagerException(
                             PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                             "Cannot stage multiple sessions without checkpoint support", null);
@@ -1042,23 +1034,11 @@
 
         // A session could be marked ready once its pre-reboot verification ends
         if (session.isStagedSessionReady()) {
-            if (sessionContainsApex(session)) {
-                try {
-                    ApexSessionInfo apexSession =
-                            mApexManager.getStagedSessionInfo(session.sessionId);
-                    if (apexSession == null || isApexSessionFinalized(apexSession)) {
-                        Slog.w(TAG,
-                                "Cannot abort session " + session.sessionId
-                                        + " because it is not active.");
-                    } else {
-                        mApexManager.abortStagedSession(session.sessionId);
-                    }
-                } catch (Exception e) {
-                    // Failed to contact apexd service. The apex might still be staged. We can still
-                    // safely cleanup the staged session since pre-reboot verification is complete.
-                    // Also, cleaning up the stageDir prevents the apex from being activated.
-                    Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId);
-                }
+            if (!ensureActiveApexSessionIsAborted(session)) {
+                // Failed to ensure apex session is aborted, so it can still be staged. We can still
+                // safely cleanup the staged session since pre-reboot verification is complete.
+                // Also, cleaning up the stageDir prevents the apex from being activated.
+                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
             }
         }
 
@@ -1068,6 +1048,22 @@
         return true;
     }
 
+    /**
+     * Ensure that there is no active apex session staged in apexd for the given session.
+     *
+     * @return returns true if it is ensured that there is no active apex session, otherwise false
+     */
+    private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
+        if (!sessionContainsApex(session)) {
+            return true;
+        }
+        final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
+        if (apexSession == null || isApexSessionFinalized(apexSession)) {
+            return true;
+        }
+        return mApexManager.abortStagedSession(session.sessionId);
+    }
+
     private boolean isApexSessionFinalized(ApexSessionInfo session) {
         /* checking if the session is in a final state, i.e., not active anymore */
         return session.isUnknown || session.isActivationFailed || session.isSuccess
@@ -1294,8 +1290,8 @@
                         + sessionId);
                 return;
             }
-            if (session.isDestroyed()) {
-                // No point in running verification on a destroyed session
+            if (session.isDestroyed() || session.isStagedSessionFailed()) {
+                // No point in running verification on a destroyed/failed session
                 onPreRebootVerificationComplete(sessionId);
                 return;
             }
@@ -1348,6 +1344,17 @@
             obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
         }
 
+        private void onPreRebootVerificationFailure(PackageInstallerSession session,
+                @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
+            if (!ensureActiveApexSessionIsAborted(session)) {
+                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
+                // Safe to ignore active apex session abortion failure since session will be marked
+                // failed on next step and staging directory for session will be deleted.
+            }
+            session.setStagedSessionFailed(errorCode, errorMessage);
+            onPreRebootVerificationComplete(session.sessionId);
+        }
+
         // Things to do when pre-reboot verification completes for a particular sessionId
         private void onPreRebootVerificationComplete(int sessionId) {
             // Remove it from mVerificationRunning so that verification is considered complete
@@ -1432,8 +1439,7 @@
                         validateApexSignature(apexPackages.get(i));
                     }
                 } catch (PackageManagerException e) {
-                    session.setStagedSessionFailed(e.error, e.getMessage());
-                    onPreRebootVerificationComplete(session.sessionId);
+                    onPreRebootVerificationFailure(session, e.error, e.getMessage());
                     return;
                 }
 
@@ -1460,16 +1466,42 @@
             try {
                 Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
                         + session.sessionId + " by performing a dry-run install");
-
                 // verifyApksInSession will notify the handler when APK verification is complete
                 verifyApksInSession(session);
-                // TODO(b/118865310): abort the session on apexd.
             } catch (PackageManagerException e) {
-                session.setStagedSessionFailed(e.error, e.getMessage());
-                onPreRebootVerificationComplete(session.sessionId);
+                onPreRebootVerificationFailure(session, e.error, e.getMessage());
             }
         }
 
+        private void verifyApksInSession(PackageInstallerSession session)
+                throws PackageManagerException {
+
+            final PackageInstallerSession apksToVerify = extractApksInSession(
+                    session,  /* preReboot */ true);
+            if (apksToVerify == null) {
+                return;
+            }
+
+            final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+                    (Intent result) -> {
+                        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                                PackageInstaller.STATUS_FAILURE);
+                        if (status != PackageInstaller.STATUS_SUCCESS) {
+                            final String errorMessage = result.getStringExtra(
+                                    PackageInstaller.EXTRA_STATUS_MESSAGE);
+                            Slog.e(TAG, "Failure to verify APK staged session "
+                                    + session.sessionId + " [" + errorMessage + "]");
+                            onPreRebootVerificationFailure(session,
+                                    SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
+                            return;
+                        }
+                        mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+                                session.sessionId);
+                    });
+
+            apksToVerify.commit(receiver.getIntentSender(), false);
+        }
+
         /**
          * Pre-reboot verification state for wrapping up:
          * <p><ul>
@@ -1487,9 +1519,8 @@
             } catch (Exception e) {
                 // Failed to get hold of StorageManager
                 Slog.e(TAG, "Failed to get hold of StorageManager", e);
-                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN,
                         "Failed to get hold of StorageManager");
-                onPreRebootVerificationComplete(session.sessionId);
                 return;
             }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8f11fd5..8b2ba9c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -134,6 +134,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
@@ -3497,7 +3498,7 @@
             }
 
             t.traceBegin("PM.onNewUserCreated-" + userId);
-            mPm.onNewUserCreated(userId);
+            mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
             t.traceEnd();
             if (preCreate) {
                 // Must start user (which will be stopped right away, through
@@ -3570,10 +3571,7 @@
             writeUserListLP();
         }
         updateUserIds();
-        if (!mPm.readPermissionStateForUser(preCreatedUser.id)) {
-            // Could not read the existing permissions, re-grant them.
-            mPm.onNewUserCreated(preCreatedUser.id);
-        }
+        mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
         dispatchUserAdded(preCreatedUser);
         return preCreatedUser;
     }
@@ -4787,6 +4785,7 @@
                     }
                 }
             }
+
             pw.println();
             pw.println("Device properties:");
             pw.println("  Device owner id:" + mDeviceOwnerUserId);
@@ -4804,8 +4803,23 @@
                 }
             }
             synchronized (mUserStates) {
-                pw.println("  Started users state: " + mUserStates);
+                pw.print("  Started users state: [");
+                final int size = mUserStates.states.size();
+                for (int i = 0; i < size; i++) {
+                    final int userId = mUserStates.states.keyAt(i);
+                    final int state = mUserStates.states.valueAt(i);
+                    pw.print(userId);
+                    pw.print('=');
+                    pw.print(UserState.stateToString(state));
+                    if (i != size - 1) pw.print(", ");
+                }
+                pw.println(']');
             }
+            synchronized (mUsersLock) {
+                pw.print("  Cached user IDs: ");
+                pw.println(Arrays.toString(mUserIds));
+            }
+
         } // synchronized (mPackagesLock)
 
         // Dump some capabilities
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 3c1d189..f7e9e34 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -139,6 +139,11 @@
             UserManager.DISALLOW_CONFIG_PRIVATE_DNS
     });
 
+    public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
+            UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+            UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
+    );
+
     /**
      * Set of user restriction which we don't want to persist.
      */
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 be93b8f..03771be 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2454,6 +2454,36 @@
         }
     }
 
+    @NonNull
+    private Set<String> getGrantedPermissions(@NonNull String packageName,
+            @UserIdInt int userId) {
+        final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
+        if (ps == null) {
+            return null;
+        }
+        final PermissionsState permissionsState = ps.getPermissionsState();
+        return permissionsState.getPermissions(userId);
+    }
+
+    @Nullable
+    private int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) {
+        BasePermission permission = mSettings.getPermission(permissionName);
+        if (permission == null) {
+            return null;
+        }
+        return permission.computeGids(userId);
+    }
+
+    @Nullable
+    private int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) {
+        final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
+        if (ps == null) {
+            return null;
+        }
+        final PermissionsState permissionsState = ps.getPermissionsState();
+        return permissionsState.computeGids(userId);
+    }
+
     /**
      * Restore the permission state for a package.
      *
@@ -4650,6 +4680,22 @@
         public void removeAllPermissions(AndroidPackage pkg, boolean chatty) {
             PermissionManagerService.this.removeAllPermissions(pkg, chatty);
         }
+        @NonNull
+        @Override
+        public Set<String> getGrantedPermissions(@NonNull String packageName,
+                @UserIdInt int userId) {
+            return PermissionManagerService.this.getGrantedPermissions(packageName, userId);
+        }
+        @Nullable
+        @Override
+        public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) {
+            return PermissionManagerService.this.getPermissionGids(permissionName, userId);
+        }
+        @Nullable
+        @Override
+        public int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) {
+            return PermissionManagerService.this.getPackageGids(packageName, userId);
+        }
         @Override
         public void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds,
                 String[] grantedPermissions, int callingUid) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 2e83b23..cfa371d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -263,6 +264,25 @@
     public abstract void addAllPermissionGroups(@NonNull AndroidPackage pkg, boolean chatty);
     public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
 
+    /**
+     * Get all the permissions granted to a package.
+     */
+    @NonNull
+    public abstract Set<String> getGrantedPermissions(@NonNull String packageName,
+            @UserIdInt int userId);
+
+    /**
+     * Get the GIDs of a permission.
+     */
+    @Nullable
+    public abstract int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId);
+
+    /**
+     * Get the GIDs computed from the permission state of a package.
+     */
+    @Nullable
+    public abstract int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId);
+
     /** Retrieve the packages that have requested the given app op permission */
     public abstract @Nullable String[] getAppOpPermissionPackages(
             @NonNull String permName, int callingUid);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 03868e9..548cd70 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -450,7 +450,6 @@
     volatile int mPendingWakeKey = PENDING_KEY_NULL;
 
     int mRecentAppsHeldModifiers;
-    boolean mLanguageSwitchKeyPressed;
 
     int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT;
     boolean mHaveBuiltInKeyboard;
@@ -933,6 +932,8 @@
             }
         }
 
+        final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
+
         GestureLauncherService gestureService = LocalServices.getService(
                 GestureLauncherService.class);
         boolean gesturedServiceIntercepted = false;
@@ -952,7 +953,8 @@
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
         mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
-                || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
+                || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted
+                || handledByPowerManager;
         if (!mPowerKeyHandled) {
             if (interactive) {
                 // When interactive, we're already awake.
@@ -2935,12 +2937,6 @@
             mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
             return -1;
         }
-        if (mLanguageSwitchKeyPressed && !down
-                && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH
-                        || keyCode == KeyEvent.KEYCODE_SPACE)) {
-            mLanguageSwitchKeyPressed = false;
-            return -1;
-        }
 
         if (isValidGlobalKey(keyCode)
                 && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index f9a49c9..882ed1b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -86,6 +86,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.KeyEvent;
 
 import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.annotations.VisibleForTesting;
@@ -892,19 +893,13 @@
                 || def == INVALID_BRIGHTNESS_IN_CONFIG) {
             mScreenBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessSettingMinimum),
-                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
-                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessSettingMinimum));
             mScreenBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessSettingMaximum),
-                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
-                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessSettingMaximum));
             mScreenBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessSettingDefault),
-                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
-                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessSettingDefault));
         } else {
             mScreenBrightnessMinimum = min;
             mScreenBrightnessMaximum = max;
@@ -913,18 +908,14 @@
         if (doze == INVALID_BRIGHTNESS_IN_CONFIG) {
             mScreenBrightnessDoze = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessDoze), PowerManager.BRIGHTNESS_OFF + 1,
-                    PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
-                    PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessDoze));
         } else {
             mScreenBrightnessDoze = doze;
         }
         if (dim == INVALID_BRIGHTNESS_IN_CONFIG) {
             mScreenBrightnessDim = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessDim), PowerManager.BRIGHTNESS_OFF + 1,
-                    PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
-                    PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessDim));
         } else {
             mScreenBrightnessDim = dim;
         }
@@ -939,19 +930,13 @@
                 || vrDef == INVALID_BRIGHTNESS_IN_CONFIG) {
             mScreenBrightnessMinimumVr = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessForVrSettingMinimum),
-                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
-                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessForVrSettingMinimum));
             mScreenBrightnessMaximumVr = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessForVrSettingMaximum),
-                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
-                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessForVrSettingMaximum));
             mScreenBrightnessDefaultVr = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
-                            .config_screenBrightnessForVrSettingDefault),
-                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
-                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+                            .config_screenBrightnessForVrSettingDefault));
         } else {
             mScreenBrightnessMinimumVr = vrMin;
             mScreenBrightnessMaximumVr = vrMax;
@@ -3590,8 +3575,7 @@
                 mDozeScreenStateOverrideFromDreamManager = screenState;
                 mDozeScreenBrightnessOverrideFromDreamManager = screenBrightness;
                 mDozeScreenBrightnessOverrideFromDreamManagerFloat =
-                        BrightnessSynchronizer.brightnessIntToFloat(mContext,
-                                mDozeScreenBrightnessOverrideFromDreamManager);
+                        BrightnessSynchronizer.brightnessIntToFloat(mDozeScreenBrightnessOverrideFromDreamManager);
                 mDirty |= DIRTY_SETTINGS;
                 updatePowerStateLocked();
             }
@@ -5393,6 +5377,29 @@
         }
     }
 
+    /**
+     * If the user presses power while the proximity sensor is enabled and keeping
+     * the screen off, then turn the screen back on by telling display manager to
+     * ignore the proximity sensor.  We don't turn off the proximity sensor because
+     * we still want it to be reenabled if it's state changes.
+     *
+     * @return True if the proximity sensor was successfully ignored and we should
+     * consume the key event.
+     */
+    private boolean interceptPowerKeyDownInternal(KeyEvent event) {
+        synchronized (mLock) {
+            // DisplayPowerController only reports proximity positive (near) if it's
+            // positive and the proximity wasn't already being ignored. So it reliably
+            // also tells us that we're not already ignoring the proximity sensor.
+            if (mDisplayPowerRequest.useProximitySensor && mProximityPositive) {
+                mDisplayManagerInternal.ignoreProximitySensorUntilChanged();
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     @VisibleForTesting
     final class LocalService extends PowerManagerInternal {
         @Override
@@ -5525,5 +5532,10 @@
         public WakeData getLastWakeup() {
             return getLastWakeupInternal();
         }
+
+        @Override
+        public boolean interceptPowerKeyDown(KeyEvent event) {
+            return interceptPowerKeyDownInternal(event);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 4349ca4..2a74b3d 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -28,6 +28,8 @@
 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;
@@ -77,6 +79,7 @@
 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 {
@@ -121,6 +124,7 @@
         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);
     }
 
@@ -478,10 +482,26 @@
         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<>();
@@ -490,10 +510,12 @@
             final ComponentName defaultLauncher = mPackageManagerInternal
                     .getHomeActivitiesAsUser(allHomeCandidates, userId);
 
-            ComponentName detected = null;
-            if (defaultLauncher != null) {
-                detected = defaultLauncher;
-            }
+            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,
@@ -517,12 +539,54 @@
                     lastPriority = ri.priority;
                 }
             }
-            return detected != null ? detected.getPackageName() : null;
+            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);
     }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 3ec61fd..7467439 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.app.timezonedetector.ITimeZoneConfigurationListener;
 import android.app.timezonedetector.ITimeZoneDetectorService;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
@@ -38,6 +37,7 @@
 import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -60,7 +60,8 @@
  * and making calls async, leaving the (consequently more testable) {@link TimeZoneDetectorStrategy}
  * implementation to deal with the logic around time zone detection.
  */
-public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
+public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
+        implements IBinder.DeathRecipient {
 
     private static final String TAG = "TimeZoneDetectorService";
 
@@ -104,9 +105,15 @@
     @NonNull
     private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
 
+    /**
+     * This sparse array acts as a map from userId to listeners running as that userId. User scoped
+     * as time zone detection configuration is partially user-specific, so different users can
+     * get different configuration.
+     */
     @GuardedBy("mConfigurationListeners")
     @NonNull
-    private final ArrayList<ConfigListenerInfo> mConfigurationListeners = new ArrayList<>();
+    private final SparseArray<ArrayList<ITimeZoneConfigurationListener>> mConfigurationListeners =
+            new SparseArray<>();
 
     private static TimeZoneDetectorService create(
             @NonNull Context context, @NonNull Handler handler,
@@ -188,18 +195,23 @@
         Objects.requireNonNull(listener);
         int userId = UserHandle.getCallingUserId();
 
-        ConfigListenerInfo listenerInfo = new ConfigListenerInfo(userId, listener);
-
         synchronized (mConfigurationListeners) {
-            if (mConfigurationListeners.contains(listenerInfo)) {
+            ArrayList<ITimeZoneConfigurationListener> listeners =
+                    mConfigurationListeners.get(userId);
+            if (listeners != null && listeners.contains(listener)) {
                 return;
             }
             try {
-                // Ensure the reference to the listener is removed if the client process dies.
-                listenerInfo.linkToDeath();
+                if (listeners == null) {
+                    listeners = new ArrayList<>(1);
+                    mConfigurationListeners.put(userId, listeners);
+                }
+
+                // Ensure the reference to the listener will be removed if the client process dies.
+                listener.asBinder().linkToDeath(this, 0 /* flags */);
 
                 // Only add the listener if we can linkToDeath().
-                mConfigurationListeners.add(listenerInfo);
+                listeners.add(listener);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e);
             }
@@ -213,21 +225,56 @@
         int userId = UserHandle.getCallingUserId();
 
         synchronized (mConfigurationListeners) {
-            ConfigListenerInfo toRemove = new ConfigListenerInfo(userId, listener);
-            Iterator<ConfigListenerInfo> listenerIterator = mConfigurationListeners.iterator();
-            while (listenerIterator.hasNext()) {
-                ConfigListenerInfo currentListenerInfo = listenerIterator.next();
-                if (currentListenerInfo.equals(toRemove)) {
-                    listenerIterator.remove();
+            boolean removedListener = false;
+            ArrayList<ITimeZoneConfigurationListener> userListeners =
+                    mConfigurationListeners.get(userId);
+            if (userListeners.remove(listener)) {
+                // Stop listening for the client process to die.
+                listener.asBinder().unlinkToDeath(this, 0 /* flags */);
+                removedListener = true;
+            }
+            if (!removedListener) {
+                Slog.w(TAG, "Client asked to remove listenener=" + listener
+                        + ", but no listeners were removed."
+                        + " mConfigurationListeners=" + mConfigurationListeners);
+            }
+        }
+    }
 
-                    // Stop listening for the client process to die.
-                    try {
-                        currentListenerInfo.unlinkToDeath();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to unlinkToDeath() for listener=" + listener, e);
+    @Override
+    public void binderDied() {
+        // Should not be used as binderDied(IBinder who) is overridden.
+        Slog.wtf(TAG, "binderDied() called unexpectedly.");
+    }
+
+    /**
+     * Called when one of the ITimeZoneConfigurationListener processes dies before calling
+     * {@link #removeConfigurationListener(ITimeZoneConfigurationListener)}.
+     */
+    @Override
+    public void binderDied(IBinder who) {
+        synchronized (mConfigurationListeners) {
+            boolean removedListener = false;
+            final int userCount = mConfigurationListeners.size();
+            for (int i = 0; i < userCount; i++) {
+                ArrayList<ITimeZoneConfigurationListener> userListeners =
+                        mConfigurationListeners.valueAt(i);
+                Iterator<ITimeZoneConfigurationListener> userListenerIterator =
+                        userListeners.iterator();
+                while (userListenerIterator.hasNext()) {
+                    ITimeZoneConfigurationListener userListener = userListenerIterator.next();
+                    if (userListener.asBinder().equals(who)) {
+                        userListenerIterator.remove();
+                        removedListener = true;
+                        break;
                     }
                 }
             }
+            if (!removedListener) {
+                Slog.w(TAG, "Notified of binder death for who=" + who
+                        + ", but did not remove any listeners."
+                        + " mConfigurationListeners=" + mConfigurationListeners);
+            }
         }
     }
 
@@ -243,14 +290,24 @@
         // problem.
 
         synchronized (mConfigurationListeners) {
-            for (ConfigListenerInfo listenerInfo : mConfigurationListeners) {
+            final int userCount = mConfigurationListeners.size();
+            for (int userIndex = 0; userIndex < userCount; userIndex++) {
+                int userId = mConfigurationListeners.keyAt(userIndex);
                 TimeZoneConfiguration configuration =
-                        mTimeZoneDetectorStrategy.getConfiguration(listenerInfo.getUserId());
-                try {
-                    listenerInfo.getListener().onChange(configuration);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Unable to notify listener="
-                            + listenerInfo + " of updated configuration=" + configuration, e);
+                        mTimeZoneDetectorStrategy.getConfiguration(userId);
+
+                ArrayList<ITimeZoneConfigurationListener> listeners =
+                        mConfigurationListeners.valueAt(userIndex);
+                final int listenerCount = listeners.size();
+                for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) {
+                    ITimeZoneConfigurationListener listener = listeners.get(listenerIndex);
+                    try {
+                        listener.onChange(configuration);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Unable to notify listener=" + listener
+                                + " for userId=" + userId
+                                + " of updated configuration=" + configuration, e);
+                    }
                 }
             }
         }
@@ -338,66 +395,5 @@
         (new TimeZoneDetectorShellCommand(this)).exec(
                 this, in, out, err, args, callback, resultReceiver);
     }
-
-    private class ConfigListenerInfo implements IBinder.DeathRecipient {
-        private final @UserIdInt int mUserId;
-        private final ITimeZoneConfigurationListener mListener;
-
-        ConfigListenerInfo(
-                @UserIdInt int userId, @NonNull ITimeZoneConfigurationListener listener) {
-            this.mUserId = userId;
-            this.mListener = Objects.requireNonNull(listener);
-        }
-
-        @UserIdInt int getUserId() {
-            return mUserId;
-        }
-
-        ITimeZoneConfigurationListener getListener() {
-            return mListener;
-        }
-
-        void linkToDeath() throws RemoteException {
-            mListener.asBinder().linkToDeath(this, 0 /* flags */);
-        }
-
-        void unlinkToDeath() throws RemoteException {
-            mListener.asBinder().unlinkToDeath(this, 0 /* flags */);
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mConfigurationListeners) {
-                Slog.i(TAG, "Configuration listener client died: " + this);
-                mConfigurationListeners.remove(this);
-            }
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            ConfigListenerInfo that = (ConfigListenerInfo) o;
-            return mUserId == that.mUserId
-                    && mListener.equals(that.mListener);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mUserId, mListener);
-        }
-
-        @Override
-        public String toString() {
-            return "ConfigListenerInfo{"
-                    + "mUserId=" + mUserId
-                    + ", mListener=" + mListener
-                    + '}';
-        }
-    }
 }
 
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/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ecba3f9..1536473 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -177,7 +177,7 @@
 
     public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) {
         WindowsForAccessibilityObserver observer = null;
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             final WindowsForAccessibilityObserver windowsForA11yObserver =
                     mWindowsForAccessibilityObserver.get(displayId);
             if (windowsForA11yObserver != null) {
@@ -266,7 +266,7 @@
         // Not relevant for the display magnifier.
 
         WindowsForAccessibilityObserver observer = null;
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             final WindowsForAccessibilityObserver windowsForA11yObserver =
                     mWindowsForAccessibilityObserver.get(displayId);
             if (windowsForA11yObserver != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9c0e2b2..5b80787 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -70,7 +70,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
 import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
@@ -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;
@@ -1675,7 +1675,8 @@
 
     static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) {
         int lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
-        if (aInfo.applicationInfo.isPrivilegedApp()
+        // Non-priv apps are not allowed to use always or never, fall back to default
+        if (!aInfo.applicationInfo.isPrivilegedApp()
                 && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
                 || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
             lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
@@ -1683,7 +1684,7 @@
         if (options != null) {
             final boolean useLockTask = options.getLockTaskMode();
             if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
-                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
             }
         }
         return lockTaskLaunchMode;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index ed1ea35..2c475e0 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -74,9 +74,9 @@
 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.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
+import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
 import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.Task.TAG_CLEANUP;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -130,6 +130,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.view.Display;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -788,7 +789,7 @@
             final LockTaskController lockTaskController = mService.getLockTaskController();
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
                     || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
-                    || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
+                    || (task.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED
                             && lockTaskController.getLockTaskModeState()
                                     == LOCK_TASK_MODE_LOCKED)) {
                 lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
@@ -1090,9 +1091,9 @@
         // Check if caller is already present on display
         final boolean uidPresentOnDisplay = displayContent.isUidPresent(callingUid);
 
-        final int displayOwnerUid = displayContent.mDisplay.getOwnerUid();
-        if (displayContent.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID) {
-            // Limit launching on virtual displays, because their contents can be read from Surface
+        final Display display = displayContent.mDisplay;
+        if (!display.isTrusted()) {
+            // Limit launching on untrusted displays because their contents can be read from Surface
             // by apps that created them.
             if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
                 if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
@@ -1116,7 +1117,7 @@
         }
 
         // Check if the caller is the owner of the display.
-        if (displayOwnerUid == callingUid) {
+        if (display.getOwnerUid() == callingUid) {
             if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                     + " allow launch for owner of the display");
             return true;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 777ddda..2dc22ec 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -528,8 +528,8 @@
     public abstract void onActiveUidsCleared();
     public abstract void onUidProcStateChanged(int uid, int procState);
 
-    public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag);
-    public abstract void onUidRemovedFromPendingTempWhitelist(int uid);
+    public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag);
+    public abstract void onUidRemovedFromPendingTempAllowlist(int uid);
 
     /** Handle app crash event in {@link android.app.IActivityController} if there is one. */
     public abstract boolean handleAppCrashInActivityController(String processName, int pid,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3edb690..31a9c5d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -183,7 +183,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -238,12 +237,9 @@
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ProcessMap;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.KeyguardDismissCallback;
 import com.android.internal.util.ArrayUtils;
@@ -384,7 +380,7 @@
     private AppOpsManager mAppOpsManager;
     /** All active uids in the system. */
     private final MirrorActiveUids mActiveUids = new MirrorActiveUids();
-    private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
+    private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>();
     /** All processes currently running that might have a window organized by name. */
     final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
     /** All processes we currently have running mapped by pid and uid */
@@ -1110,7 +1106,7 @@
 
     @Override
     public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
-            IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
+            IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
         enforceNotIsolatedCaller("startActivityIntentSender");
         // Refuse possible leaked file descriptors
@@ -1133,7 +1129,7 @@
                 mAppSwitchesAllowedTime = 0;
             }
         }
-        return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
+        return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
                 resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
     }
 
@@ -3003,13 +2999,7 @@
 
     @Override
     public void stopLockTaskModeByToken(IBinder token) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r == null) {
-                return;
-            }
-            stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
-        }
+        stopLockTaskModeInternal(token, false /* isSystemCaller */);
     }
 
     /**
@@ -3037,7 +3027,7 @@
         // system or a specific app.
         // * System-initiated requests will only start the pinned mode (screen pinning)
         // * App-initiated requests
-        //   - will put the device in fully locked mode (LockTask), if the app is whitelisted
+        //   - will put the device in fully locked mode (LockTask), if the app is allowlisted
         //   - will start the pinned mode, otherwise
         final int callingUid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
@@ -3051,11 +3041,19 @@
         }
     }
 
-    private void stopLockTaskModeInternal(@Nullable Task task, boolean isSystemCaller) {
+    private void stopLockTaskModeInternal(@Nullable IBinder token, boolean isSystemCaller) {
         final int callingUid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                Task task = null;
+                if (token != null) {
+                    final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                    if (r == null) {
+                        return;
+                    }
+                    task = r.getTask();
+                }
                 getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
             }
             // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
@@ -3077,7 +3075,7 @@
                     "updateLockTaskPackages()");
         }
         synchronized (mGlobalLock) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":"
+            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":"
                     + Arrays.toString(packages));
             getLockTaskController().updateLockTaskPackages(userId, packages);
         }
@@ -4095,10 +4093,6 @@
                         final Task stack = r.getRootTask();
                         stack.setPictureInPictureAspectRatio(aspectRatio);
                         stack.setPictureInPictureActions(actions);
-                        MetricsLoggerWrapper.logPictureInPictureEnter(mContext,
-                                r.info.applicationInfo.uid, r.shortComponentName,
-                                r.supportsEnterPipOnTaskSwitch);
-                        logPictureInPictureArgs(params);
                     }
                 };
 
@@ -4142,7 +4136,6 @@
                             r.pictureInPictureArgs.getAspectRatio());
                     stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
-                logPictureInPictureArgs(params);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4156,18 +4149,6 @@
         return 3;
     }
 
-    private void logPictureInPictureArgs(PictureInPictureParams params) {
-        if (params.hasSetActions()) {
-            MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
-                    params.getActions().size());
-        }
-        if (params.hasSetAspectRatio()) {
-            LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
-            lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
-            MetricsLogger.action(lm);
-        }
-    }
-
     /**
      * Checks the state of the system and the activity associated with the given {@param token} to
      * verify that picture-in-picture is supported for that activity.
@@ -5979,11 +5960,11 @@
     }
 
     /**
-     * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
-     * the whitelist
+     * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
+     * the allowlist
      */
-    String getPendingTempWhitelistTagForUidLocked(int uid) {
-        return mPendingTempWhitelist.get(uid);
+    String getPendingTempAllowlistTagForUidLocked(int uid) {
+        return mPendingTempAllowlist.get(uid);
     }
 
     void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
@@ -7325,16 +7306,16 @@
         }
 
         @Override
-        public void onUidAddedToPendingTempWhitelist(int uid, String tag) {
+        public void onUidAddedToPendingTempAllowlist(int uid, String tag) {
             synchronized (mGlobalLockWithoutBoost) {
-                mPendingTempWhitelist.put(uid, tag);
+                mPendingTempAllowlist.put(uid, tag);
             }
         }
 
         @Override
-        public void onUidRemovedFromPendingTempWhitelist(int uid) {
+        public void onUidRemovedFromPendingTempAllowlist(int uid) {
             synchronized (mGlobalLockWithoutBoost) {
-                mPendingTempWhitelist.remove(uid);
+                mPendingTempAllowlist.remove(uid);
             }
         }
 
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/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 5e1cbc3..e166bfc 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -10,6 +10,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
 
+import android.annotation.Nullable;
 import android.os.Build;
 import android.os.Debug;
 import android.os.IBinder;
@@ -173,23 +174,23 @@
      */
     @Override
     public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
-            String reason) {
+            @Nullable Integer pid, String reason) {
         final long startTime = SystemClock.uptimeMillis();
         try {
-            return notifyANRInner(inputApplicationHandle, token, reason);
+            return notifyANRInner(inputApplicationHandle, token, pid, reason);
         } finally {
             // Log the time because the method is called from InputDispatcher thread. It shouldn't
-            // take too long that may affect input response time.
+            // take too long because it blocks input while executing.
             Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms");
         }
     }
 
     private long notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token,
-            String reason) {
+            @Nullable Integer pid, String reason) {
         ActivityRecord activity = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
-        int windowPid = INVALID_PID;
+        int windowPid = pid != null ? pid : INVALID_PID;
 
         preDumpIfLockTooSlow();
 
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 9c978fd..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;
@@ -105,6 +105,10 @@
      * @return The state stripped of the necessary information.
      */
     InsetsState getInsetsForDispatch(@NonNull WindowState target) {
+        final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState();
+        if (rotatedState != null) {
+            return rotatedState;
+        }
         final InsetsSourceProvider provider = target.getControllableInsetProvider();
         final @InternalInsetsType int type = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index c7a438d..8ef57f7 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -33,11 +33,11 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_PINNABLE;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -264,12 +264,12 @@
     }
 
     /**
-     * @return whether the requested task is allowed to be locked (either whitelisted, or declares
+     * @return whether the requested task is allowed to be locked (either allowlisted, or declares
      * lockTaskMode="always" in the manifest).
      */
-    boolean isTaskWhitelisted(Task task) {
+    boolean isTaskAllowlisted(Task task) {
         switch(task.mLockTaskAuth) {
-            case LOCK_TASK_AUTH_WHITELISTED:
+            case LOCK_TASK_AUTH_ALLOWLISTED:
             case LOCK_TASK_AUTH_LAUNCHABLE:
             case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
                 return true;
@@ -311,7 +311,7 @@
 
     private boolean isLockTaskModeViolationInternal(Task task, boolean isNewClearTask) {
         // TODO: Double check what's going on here. If the task is already in lock task mode, it's
-        // likely whitelisted, so will return false below.
+        // likely allowlisted, so will return false below.
         if (isTaskLocked(task) && !isNewClearTask) {
             // If the task is already at the top and won't be cleared, then allow the operation
             return false;
@@ -327,7 +327,7 @@
             return false;
         }
 
-        return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
+        return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty());
     }
 
     private boolean isRecentsAllowed(int userId) {
@@ -356,7 +356,7 @@
                 return false;
             default:
         }
-        return isPackageWhitelisted(userId, packageName);
+        return isPackageAllowlisted(userId, packageName);
     }
 
     private boolean isEmergencyCallTask(Task task) {
@@ -556,7 +556,7 @@
         if (!isSystemCaller) {
             task.mLockTaskUid = callingUid;
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
-                // startLockTask() called by app, but app is not part of lock task whitelist. Show
+                // startLockTask() called by app, but app is not part of lock task allowlist. Show
                 // app pinning request. We will come back here with isSystemCaller true.
                 if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
                 StatusBarManagerInternal statusBarManager = LocalServices.getService(
@@ -649,8 +649,8 @@
 
     /**
      * Update packages that are allowed to be launched in lock task mode.
-     * @param userId Which user this whitelist is associated with
-     * @param packages The whitelist of packages allowed in lock task mode
+     * @param userId Which user this allowlist is associated with
+     * @param packages The allowlist of packages allowed in lock task mode
      * @see #mLockTaskPackages
      */
     void updateLockTaskPackages(int userId, String[] packages) {
@@ -659,19 +659,19 @@
         boolean taskChanged = false;
         for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final Task lockedTask = mLockTaskModeTasks.get(taskNdx);
-            final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
-                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
+            final boolean wasAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED;
             lockedTask.setLockTaskAuth();
-            final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
-                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
+            final boolean isAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED;
 
             if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED
                     || lockedTask.mUserId != userId
-                    || !wasWhitelisted || isWhitelisted) {
+                    || !wasAllowlisted || isAllowlisted) {
                 continue;
             }
 
-            // Terminate locked tasks that have recently lost whitelist authorization.
+            // Terminate locked tasks that have recently lost allowlist authorization.
             if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
                     lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
             removeLockedTask(lockedTask);
@@ -697,17 +697,17 @@
         }
     }
 
-    boolean isPackageWhitelisted(int userId, String pkg) {
+    boolean isPackageAllowlisted(int userId, String pkg) {
         if (pkg == null) {
             return false;
         }
-        String[] whitelist;
-        whitelist = mLockTaskPackages.get(userId);
-        if (whitelist == null) {
+        String[] allowlist;
+        allowlist = mLockTaskPackages.get(userId);
+        if (allowlist == null) {
             return false;
         }
-        for (String whitelistedPkg : whitelist) {
-            if (pkg.equals(whitelistedPkg)) {
+        for (String allowlistedPkg : allowlist) {
+            if (pkg.equals(allowlistedPkg)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java
index 0f92bc8..61b6e0b 100644
--- a/services/core/java/com/android/server/wm/PolicyControl.java
+++ b/services/core/java/com/android/server/wm/PolicyControl.java
@@ -196,40 +196,40 @@
         private static final String ALL = "*";
         private static final String APPS = "apps";
 
-        private final ArraySet<String> mWhitelist;
-        private final ArraySet<String> mBlacklist;
+        private final ArraySet<String> mAllowlist;
+        private final ArraySet<String> mDenylist;
 
-        private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
-            mWhitelist = whitelist;
-            mBlacklist = blacklist;
+        private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) {
+            mAllowlist = allowlist;
+            mDenylist = denylist;
         }
 
         boolean matches(LayoutParams attrs) {
             if (attrs == null) return false;
             boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
                     && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-            if (isApp && mBlacklist.contains(APPS)) return false;
-            if (onBlacklist(attrs.packageName)) return false;
-            if (isApp && mWhitelist.contains(APPS)) return true;
-            return onWhitelist(attrs.packageName);
+            if (isApp && mDenylist.contains(APPS)) return false;
+            if (onDenylist(attrs.packageName)) return false;
+            if (isApp && mAllowlist.contains(APPS)) return true;
+            return onAllowlist(attrs.packageName);
         }
 
         boolean matches(String packageName) {
-            return !onBlacklist(packageName) && onWhitelist(packageName);
+            return !onDenylist(packageName) && onAllowlist(packageName);
         }
 
-        private boolean onBlacklist(String packageName) {
-            return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
+        private boolean onDenylist(String packageName) {
+            return mDenylist.contains(packageName) || mDenylist.contains(ALL);
         }
 
-        private boolean onWhitelist(String packageName) {
-            return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
+        private boolean onAllowlist(String packageName) {
+            return mAllowlist.contains(ALL) || mAllowlist.contains(packageName);
         }
 
         void dump(PrintWriter pw) {
             pw.print("Filter[");
-            dump("whitelist", mWhitelist, pw); pw.print(',');
-            dump("blacklist", mBlacklist, pw); pw.print(']');
+            dump("allowlist", mAllowlist, pw); pw.print(',');
+            dump("denylist", mDenylist, pw); pw.print(']');
         }
 
         private void dump(String name, ArraySet<String> set, PrintWriter pw) {
@@ -253,18 +253,18 @@
         // e.g. "com.package1", or "apps, com.android.keyguard" or "*"
         static Filter parse(String value) {
             if (value == null) return null;
-            ArraySet<String> whitelist = new ArraySet<String>();
-            ArraySet<String> blacklist = new ArraySet<String>();
+            ArraySet<String> allowlist = new ArraySet<String>();
+            ArraySet<String> denylist = new ArraySet<String>();
             for (String token : value.split(",")) {
                 token = token.trim();
                 if (token.startsWith("-") && token.length() > 1) {
                     token = token.substring(1);
-                    blacklist.add(token);
+                    denylist.add(token);
                 } else {
-                    whitelist.add(token);
+                    allowlist.add(token);
                 }
             }
-            return new Filter(whitelist, blacklist);
+            return new Filter(allowlist, denylist);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index ba2c0b6..df53563 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -655,7 +655,8 @@
         }
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final Task task = mTasks.get(i);
-            if (task.mUserId == userId && !mService.getLockTaskController().isTaskWhitelisted(task)) {
+            if (task.mUserId == userId
+                    && !mService.getLockTaskController().isTaskAllowlisted(task)) {
                 remove(task);
             }
         }
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 ac96d144..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;
@@ -2571,7 +2571,7 @@
         mDisplayAccessUIDs.clear();
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent displayContent = getChildAt(displayNdx);
-            // Only bother calculating the whitelist for private displays
+            // Only bother calculating the allowlist for private displays
             if (displayContent.isPrivate()) {
                 mDisplayAccessUIDs.append(
                         displayContent.mDisplayId, displayContent.getPresentUIDs());
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index b71ecbb..ede6708 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -233,10 +233,10 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
+        // Check if someone tries to launch an unallowlisted activity into LockTask mode.
         final boolean lockTaskMode = options.getLockTaskMode();
         if (aInfo != null && lockTaskMode
-                && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
+                && !supervisor.mService.getLockTaskController().isPackageAllowlisted(
                         UserHandle.getUserId(callingUid), aInfo.packageName)) {
             final String msg = "Permission Denial: starting " + getIntentString(intent)
                     + " from " + callerApp + " (pid=" + callingPid
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 5cea786..d9365c5 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -16,20 +16,19 @@
 
 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;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.graphics.ColorSpace;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 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;
 
@@ -89,8 +88,7 @@
             if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
                 return;
             }
-            mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, buffer,
-                    screenshotBuffer.getColorSpace(), mLeash);
+            mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, screenshotBuffer, mLeash);
         }
     }
 
@@ -135,8 +133,12 @@
             cropBounds = new Rect(bounds);
             cropBounds.offsetTo(0, 0);
         }
-        return SurfaceControl.captureLayers(target, cropBounds, 1.f /* frameScale */,
-                PixelFormat.RGBA_8888);
+        SurfaceControl.LayerCaptureArgs captureArgs =
+                new SurfaceControl.LayerCaptureArgs.Builder(target)
+                        .setSourceCrop(cropBounds)
+                        .setCaptureSecureLayers(true)
+                        .build();
+        return SurfaceControl.captureLayers(captureArgs);
     }
 
     class Snapshot {
@@ -146,21 +148,23 @@
 
         /**
          * @param t Transaction to create the thumbnail in.
-         * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
+         * @param screenshotBuffer A thumbnail or placeholder for thumbnail to initialize with.
          */
         Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
-                HardwareBuffer thumbnailHeader, ColorSpace colorSpace, SurfaceControl parent) {
+                SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
             Surface drawSurface = surfaceFactory.get();
             // We can't use a delegating constructor since we need to
             // reference this::onAnimationFinished
-            final int width = thumbnailHeader.getWidth();
-            final int height = thumbnailHeader.getHeight();
+            HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+            final int width = hardwareBuffer.getWidth();
+            final int height = hardwareBuffer.getHeight();
 
             mSurfaceControl = mAnimatable.makeAnimationLeash()
                     .setName("snapshot anim: " + mAnimatable.toString())
                     .setBufferSize(width, height)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .setParent(parent)
+                    .setSecure(screenshotBuffer.containsSecureLayers())
                     .setCallsite("SurfaceFreezer.Snapshot")
                     .build();
 
@@ -168,7 +172,8 @@
 
             // Transfer the thumbnail to the surface
             drawSurface.copyFrom(mSurfaceControl);
-            drawSurface.attachAndQueueBufferWithColorSpace(thumbnailHeader, colorSpace);
+            drawSurface.attachAndQueueBufferWithColorSpace(hardwareBuffer,
+                    screenshotBuffer.getColorSpace());
             drawSurface.release();
             t.show(mSurfaceControl);
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f362005..ec7b1ee 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -45,7 +45,7 @@
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
@@ -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,7 +210,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
+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;
@@ -219,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;
@@ -411,7 +410,7 @@
     /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
     final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
     /** Can enter lockTask without user approval. Can start over existing lockTask task. */
-    final static int LOCK_TASK_AUTH_WHITELISTED = 3;
+    final static int LOCK_TASK_AUTH_ALLOWLISTED = 3;
     /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing
      * lockTask task. */
     final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
@@ -1686,7 +1685,7 @@
             getDisplayArea().addStackReferenceIfNeeded((Task) child);
         }
 
-        // Make sure the list of display UID whitelists is updated
+        // Make sure the list of display UID allowlists is updated
         // now that this record is in a new task.
         mRootWindowContainer.updateUIDsPresentOnDisplay();
 
@@ -1903,7 +1902,7 @@
             case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
             case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE";
             case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE";
-            case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED";
+            case LOCK_TASK_AUTH_ALLOWLISTED: return "LOCK_TASK_AUTH_ALLOWLISTED";
             case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV";
             default: return "unknown=" + mLockTaskAuth;
         }
@@ -1923,8 +1922,8 @@
         final LockTaskController lockTaskController = mAtmService.getLockTaskController();
         switch (r.lockTaskLaunchMode) {
             case LOCK_TASK_LAUNCH_MODE_DEFAULT:
-                mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg)
-                        ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
+                mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg)
+                        ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE;
                 break;
 
             case LOCK_TASK_LAUNCH_MODE_NEVER:
@@ -1935,8 +1934,8 @@
                 mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
                 break;
 
-            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
-                mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg)
+            case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED:
+                mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg)
                         ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
                 break;
         }
@@ -2366,7 +2365,6 @@
     private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
         if (mWmService.mDisableTransitionAnimation
                 || !isVisible()
-                || getDisplayContent().mAppTransition.isTransitionSet()
                 || getSurfaceControl() == null
                 || !isLeafTask()) {
             return false;
@@ -7058,17 +7056,21 @@
     /**
      * Reset local parameters because an app's activity died.
      * @param app The app of the activity that died.
+     * @return {@code true} if the process of the pausing activity is died.
      */
-    void handleAppDied(WindowProcessController app) {
+    boolean handleAppDied(WindowProcessController app) {
+        boolean isPausingDied = false;
         if (mPausingActivity != null && mPausingActivity.app == app) {
             if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
                     "App died while pausing: " + mPausingActivity);
             mPausingActivity = null;
+            isPausingDied = true;
         }
         if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
             mLastPausedActivity = null;
             mLastNoHistoryActivity = null;
         }
+        return isPausingDied;
     }
 
     boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
@@ -7372,8 +7374,6 @@
             getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
 
             mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
-            MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
-                    task.effectiveUid, task.realActivity.flattenToString());
         });
     }
 
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 f3c7a5d..e9ada6b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.graphics.Color.WHITE;
 import static android.graphics.Color.alpha;
 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
@@ -40,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;
@@ -82,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.
@@ -142,6 +143,7 @@
     private final Handler mHandler;
     private boolean mSizeMismatch;
     private final Paint mBackgroundPaint = new Paint();
+    private final int mActivityType;
     private final int mStatusBarColor;
     @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
     private final int mOrientationOnCreation;
@@ -173,6 +175,7 @@
         final int windowFlags;
         final int windowPrivateFlags;
         final int currentOrientation;
+        final int activityType;
         final InsetsState insetsState;
         synchronized (service.mGlobalLock) {
             final WindowState mainWindow = activity.findMainWindow();
@@ -241,6 +244,7 @@
             taskBounds = new Rect();
             task.getBounds(taskBounds);
             currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
+            activityType = activity.getActivityType();
 
             final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
                     .getInsetsPolicy();
@@ -261,7 +265,8 @@
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
                 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
-                windowFlags, windowPrivateFlags, taskBounds, currentOrientation, insetsState);
+                windowFlags, windowPrivateFlags, taskBounds, currentOrientation, activityType,
+                insetsState);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
@@ -282,7 +287,7 @@
     TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
             TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
             int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
-            int currentOrientation, InsetsState insetsState) {
+            int currentOrientation, int activityType, InsetsState insetsState) {
         mService = service;
         mSurface = service.mSurfaceFactory.get();
         mHandler = new Handler(mService.mH.getLooper());
@@ -298,6 +303,7 @@
                 windowPrivateFlags, sysUiVis, taskDescription, 1f, insetsState);
         mStatusBarColor = taskDescription.getStatusBarColor();
         mOrientationOnCreation = currentOrientation;
+        mActivityType = activityType;
         mTransaction = mService.mTransactionFactory.get();
     }
 
@@ -305,7 +311,9 @@
     public void remove() {
         synchronized (mService.mGlobalLock) {
             final long now = SystemClock.uptimeMillis();
-            if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
+            if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
+                    // Show the latest content as soon as possible for unlocking to home.
+                    && mActivityType != ACTIVITY_TYPE_HOME) {
                 mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
                         "Defer removing snapshot surface in %dms", (now - mShownTime));
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/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index a58c564..ab6e35b 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1177,12 +1177,19 @@
     boolean handleAppDied() {
         mAtm.mStackSupervisor.removeHistoryRecords(this);
 
-        final boolean isRemoved = isRemoved();
         boolean hasVisibleActivities = false;
         if (mInactiveActivities != null && !mInactiveActivities.isEmpty()) {
             // Make sure that all activities in this process are handled.
             mActivities.addAll(mInactiveActivities);
         }
+        if (isRemoved()) {
+            // The package of the died process should be force-stopped, so make its activities as
+            // finishing to prevent the process from being started again if the next top (or being
+            // visible) activity also resides in the same process. This must be done before removal.
+            for (int i = mActivities.size() - 1; i >= 0; i--) {
+                mActivities.get(i).makeFinishingLocked();
+            }
+        }
         for (int i = mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = mActivities.get(i);
             if (r.mVisibleRequested || r.isVisible()) {
@@ -1191,16 +1198,13 @@
                 // is not yet committed, so isVisible()=true.
                 hasVisibleActivities = true;
             }
-            if (isRemoved) {
-                // The package of the died process should be force-stopped, so make its activities
-                // as finishing to prevent the process from being started again if the next top (or
-                // being visible) activity also resides in the same process.
-                r.makeFinishingLocked();
-            }
 
             final Task rootTask = r.getRootTask();
             if (rootTask != null) {
-                rootTask.handleAppDied(this);
+                // There may be a pausing activity that hasn't shown any window and was requested
+                // to be hidden. But pausing is also a visible state, it should be regarded as
+                // visible, so the caller can know the next activity should be resumed.
+                hasVisibleActivities |= rootTask.handleAppDied(this);
             }
             r.handleAppDied();
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ab78e74..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;
@@ -1535,10 +1535,6 @@
     }
 
     InsetsState getInsetsState() {
-        final InsetsState insetsState = mToken.getFixedRotationTransformInsetsState();
-        if (insetsState != null) {
-            return insetsState;
-        }
         return getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
     }
 
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/Android.bp b/services/core/jni/Android.bp
index 0e1b2f2..4b5f38c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -99,6 +99,7 @@
         "libpowermanager",
         "libutils",
         "libui",
+        "libvibratorservice",
         "libinput",
         "libinputflinger",
         "libinputflinger_base",
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 05aa359..b6633ce 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -33,6 +33,8 @@
 #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;
@@ -47,6 +49,8 @@
 
 namespace android {
 
+static JavaVM* sJvm = nullptr;
+
 static jmethodID sMethodIdOnComplete;
 
 static struct {
@@ -226,8 +230,33 @@
     return val >= *iter.begin() && val <= *std::prev(iter.end());
 }
 
-static void vibratorInit(JNIEnv *env, jclass clazz)
-{
+static void callVibrationOnComplete(jobject vibration) {
+    if (vibration == nullptr) {
+        return;
+    }
+    auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+    jniEnv->CallVoidMethod(vibration, sMethodIdOnComplete);
+    jniEnv->DeleteGlobalRef(vibration);
+}
+
+static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
+    aidl::CompositeEffect effect;
+    effect.primitive = static_cast<aidl::CompositePrimitive>(
+            env->GetIntField(primitive, gPrimitiveClassInfo.id));
+    effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale));
+    effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, gPrimitiveClassInfo.delay));
+    return effect;
+}
+
+static void destroyVibratorController(void* rawVibratorController) {
+    vibrator::HalController* vibratorController =
+            reinterpret_cast<vibrator::HalController*>(rawVibratorController);
+    if (vibratorController) {
+        delete vibratorController;
+    }
+}
+
+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
@@ -236,125 +265,84 @@
     } else {
         halCall(&V1_0::IVibrator::ping).isOk();
     }
+    std::unique_ptr<vibrator::HalController> controller =
+            std::make_unique<vibrator::HalController>();
+    controller->init();
+    return reinterpret_cast<jlong>(controller.release());
 }
 
-static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */)
-{
-    bool ok;
+static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyVibratorController));
+}
 
-    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;
-        ok = hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk();
-    } else {
-        ok = halCall(&V1_0::IVibrator::ping).isOk();
+static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorExists failed because controller was not initialized");
+        return JNI_FALSE;
     }
-    return ok ? JNI_TRUE : JNI_FALSE;
+    return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE;
 }
 
-static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms)
-{
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr);
-        if (!status.isOk()) {
-            ALOGE("vibratorOn command failed: %s", status.toString8().string());
-        }
-    } else {
-        Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);
-        if (retStatus != Status::OK) {
-            ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
-        }
+static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong timeoutMs,
+                       jobject vibration) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorOn failed because controller was not initialized");
+        return;
     }
+    jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
+    auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
+    controller->on(std::chrono::milliseconds(timeoutMs), callback);
 }
 
-static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */)
-{
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        auto status = hal->call(&aidl::IVibrator::off);
-        if (!status.isOk()) {
-            ALOGE("vibratorOff command failed: %s", status.toString8().string());
-        }
-    } else {
-        Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);
-        if (retStatus != Status::OK) {
-            ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
-        }
+static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorOff failed because controller was not initialized");
+        return;
     }
+    controller->off();
 }
 
-static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        int32_t cap = 0;
-        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
-            return false;
-        }
-        return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0;
-    } else {
-        return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false);
+static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                 jint amplitude) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorSetAmplitude failed because controller was not initialized");
+        return;
     }
+    controller->setAmplitude(static_cast<int32_t>(amplitude));
 }
 
-static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        auto status = hal->call(&aidl::IVibrator::IVibrator::setAmplitude, static_cast<float>(amplitude) / UINT8_MAX);
-        if (!status.isOk()) {
-            ALOGE("Failed to set vibrator amplitude: %s", status.toString8().string());
-        }
-    } else {
-        Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude))
-            .withDefault(Status::UNKNOWN_ERROR);
-        if (status != Status::OK) {
-            ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").",
-                  static_cast<uint32_t>(status));
-        }
+static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                       jboolean enabled) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorSetExternalControl failed because controller was not initialized");
+        return;
     }
+    controller->setExternalControl(enabled);
 }
 
-static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        int32_t cap = 0;
-        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
-            return false;
-        }
-        return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0;
-    } else {
-        return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false);
-    }
-}
-
-static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        auto status = hal->call(&aidl::IVibrator::IVibrator::setExternalControl, enabled);
-        if (!status.isOk()) {
-            ALOGE("Failed to set vibrator external control: %s", status.toString8().string());
-        }
-    } else {
-        Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled))
-            .withDefault(Status::UNKNOWN_ERROR);
-        if (status != Status::OK) {
-            ALOGE("Failed to set vibrator external control (%" PRIu32 ").",
-                static_cast<uint32_t>(status));
-        }
-    }
-}
-
-static jintArray vibratorGetSupportedEffects(JNIEnv *env, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        std::vector<aidl::Effect> supportedEffects;
-        if (!hal->call(&aidl::IVibrator::getSupportedEffects, &supportedEffects).isOk()) {
-            return nullptr;
-        }
-        jintArray arr = env->NewIntArray(supportedEffects.size());
-        env->SetIntArrayRegion(arr, 0, supportedEffects.size(),
-                reinterpret_cast<jint*>(supportedEffects.data()));
-        return arr;
-    } else {
+static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorGetSupportedEffects failed because controller was not initialized");
         return nullptr;
     }
+    auto result = controller->getSupportedEffects();
+    if (!result.isOk()) {
+        return nullptr;
+    }
+    std::vector<aidl::Effect> supportedEffects = result.value();
+    jintArray effects = env->NewIntArray(supportedEffects.size());
+    env->SetIntArrayRegion(effects, 0, supportedEffects.size(),
+                           reinterpret_cast<jint*>(supportedEffects.data()));
+    return effects;
 }
 
-static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength,
+static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong effect, jlong strength,
                                    jobject vibration, jboolean withCallback) {
     if (auto hal = getHal<aidl::IVibrator>()) {
         int32_t lengthMs;
@@ -420,19 +408,11 @@
     return -1;
 }
 
-static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
-    aidl::CompositeEffect effect;
-    effect.primitive = static_cast<aidl::CompositePrimitive>(
-            env->GetIntField(primitive, gPrimitiveClassInfo.id));
-    effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale));
-    effect.delayMs = static_cast<int>(env->GetIntField(primitive, gPrimitiveClassInfo.delay));
-    return effect;
-}
-
-static void vibratorPerformComposedEffect(JNIEnv* env, jclass, jobjectArray composition,
-                                   jobject vibration) {
-    auto hal = getHal<aidl::IVibrator>();
-    if (!hal) {
+static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                          jobjectArray composition, jobject vibration) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorPerformComposedEffect failed because controller was not initialized");
         return;
     }
     size_t size = env->GetArrayLength(composition);
@@ -441,75 +421,77 @@
         jobject element = env->GetObjectArrayElement(composition, i);
         effects.push_back(effectFromJavaPrimitive(env, element));
     }
-    sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration);
-
-    auto status = hal->call(&aidl::IVibrator::compose, effects, effectCallback);
-    if (!status.isOk()) {
-        if (status.exceptionCode() != binder::Status::EX_UNSUPPORTED_OPERATION) {
-            ALOGE("Failed to play haptic effect composition");
-        }
-    }
+    jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
+    auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
+    controller->performComposedEffect(effects, callback);
 }
 
-static jlong vibratorGetCapabilities(JNIEnv*, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        int32_t cap = 0;
-        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
-            return 0;
-        }
-        return cap;
+static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorGetCapabilities failed because controller was not initialized");
+        return 0;
     }
-
-    return 0;
+    auto result = controller->getCapabilities();
+    return result.isOk() ? static_cast<jlong>(result.value()) : 0;
 }
 
-static void vibratorAlwaysOnEnable(JNIEnv* env, jclass, jlong id, jlong effect, jlong strength) {
-    auto status = halCall(&aidl::IVibrator::alwaysOnEnable, id,
-            static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength));
-    if (!status.isOk()) {
-        ALOGE("vibratortAlwaysOnEnable command failed (%s).", status.toString8().string());
+static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong id,
+                                   jlong effect, jlong strength) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorAlwaysOnEnable failed because controller was not initialized");
+        return;
     }
+    controller->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect),
+                               static_cast<aidl::EffectStrength>(strength));
 }
 
-static void vibratorAlwaysOnDisable(JNIEnv* env, jclass, jlong id) {
-    auto status = halCall(&aidl::IVibrator::alwaysOnDisable, id);
-    if (!status.isOk()) {
-        ALOGE("vibratorAlwaysOnDisable command failed (%s).", status.toString8().string());
+static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                    jlong id) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorAlwaysOnDisable failed because controller was not initialized");
+        return;
     }
+    controller->alwaysOnDisable(static_cast<int32_t>(id));
 }
 
 static const JNINativeMethod method_table[] = {
-        {"vibratorExists", "()Z", (void*)vibratorExists},
-        {"vibratorInit", "()V", (void*)vibratorInit},
-        {"vibratorOn", "(J)V", (void*)vibratorOn},
-        {"vibratorOff", "()V", (void*)vibratorOff},
-        {"vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
-        {"vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
+        {"vibratorInit", "()J", (void*)vibratorInit},
+        {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
+        {"vibratorExists", "(J)Z", (void*)vibratorExists},
+        {"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",
          (void*)vibratorPerformEffect},
         {"vibratorPerformComposedEffect",
-         "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
+         "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
          "VibratorService$Vibration;)V",
          (void*)vibratorPerformComposedEffect},
-        {"vibratorGetSupportedEffects", "()[I", (void*)vibratorGetSupportedEffects},
-        {"vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
-        {"vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
-        {"vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
-        {"vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
-        {"vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
+        {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
+        {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
+        {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities},
+        {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
+        {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
 };
 
-int register_android_server_VibratorService(JNIEnv *env) {
-    sMethodIdOnComplete = GetMethodIDOrDie(env,
-            FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
-            "onComplete", "()V");
-    jclass primitiveClass = FindClassOrDie(env,
-            "android/os/VibrationEffect$Composition$PrimitiveEffect");
+int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env) {
+    sJvm = vm;
+    sMethodIdOnComplete =
+            GetMethodIDOrDie(env,
+                             FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
+                             "onComplete", "()V");
+
+    jclass primitiveClass =
+            FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect");
     gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
     gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
     gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
-    return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
-            method_table, NELEM(method_table));
+
+    return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table,
+                                    NELEM(method_table));
 }
 
-};
+}; // namespace android
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 7843663..9751c46 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -75,6 +75,12 @@
 using android::base::ParseUint;
 using android::base::StringPrintf;
 
+// Maximum allowable delay value in a vibration pattern before
+// which the delay will be truncated.
+static constexpr std::chrono::duration MAX_VIBRATE_PATTERN_DELAY = 100s;
+static constexpr std::chrono::milliseconds MAX_VIBRATE_PATTERN_DELAY_MILLIS =
+        std::chrono::duration_cast<std::chrono::milliseconds>(MAX_VIBRATE_PATTERN_DELAY);
+
 namespace android {
 
 // The exponent used to calculate the pointer speed scaling factor.
@@ -415,6 +421,10 @@
         AutoMutex _l(mLock);
         mLocked.viewports = viewports;
         mLocked.pointerDisplayId = pointerDisplayId;
+        std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
+        if (controller != nullptr) {
+            controller->onDisplayViewportsUpdated(mLocked.viewports);
+        }
     } // release lock
 
     mInputManager->getReader()->requestRefreshConfiguration(
@@ -710,9 +720,8 @@
     jobject tokenObj = javaObjectForIBinder(env, token);
     jstring reasonObj = env->NewStringUTF(reason.c_str());
 
-    jlong newTimeout = env->CallLongMethod(mServiceObj,
-            gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
-                 reasonObj);
+    jlong newTimeout = env->CallLongMethod(mServiceObj, gServiceClassInfo.notifyANR,
+                                           inputApplicationHandleObj, tokenObj, reasonObj);
     if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
         newTimeout = 0; // abort dispatch
     } else {
@@ -813,8 +822,8 @@
     }
 
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
-    controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT
-                                               : PointerController::InactivityTimeout::NORMAL);
+    controller->setInactivityTimeout(lightsOut ? InactivityTimeout::SHORT
+                                               : InactivityTimeout::NORMAL);
 }
 
 void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -1637,17 +1646,19 @@
             patternObj, nullptr));
     jint* amplitudes = static_cast<jint*>(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr));
 
-    std::vector<VibrationElement> pattern(patternSize);
+    std::vector<VibrationElement> elements(patternSize);
     for (size_t i = 0; i < patternSize; i++) {
-        jlong duration =
-                max(min(patternMillis[i], (jlong)MAX_VIBRATE_PATTERN_DELAY_MSECS), (jlong)0);
-        pattern[i].duration = std::chrono::milliseconds(duration);
-        pattern[i].channels = {amplitudes[i]};
+        // VibrationEffect.validate guarantees duration > 0.
+        std::chrono::milliseconds duration(patternMillis[i]);
+        elements[i].duration = std::min(duration, MAX_VIBRATE_PATTERN_DELAY_MILLIS);
+        // TODO: (b/161629089) apply channel specific amplitudes from development API.
+        elements[i].channels = {static_cast<uint8_t>(amplitudes[i]),
+                                static_cast<uint8_t>(amplitudes[i])};
     }
     env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
     env->ReleasePrimitiveArrayCritical(amplitudesObj, amplitudes, JNI_ABORT);
 
-    im->getInputManager()->getReader()->vibrate(deviceId, pattern, repeat, token);
+    im->getInputManager()->getReader()->vibrate(deviceId, elements, repeat, token);
 }
 
 static void nativeCancelVibrate(JNIEnv* /* env */,
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index e904645..9e2bb45 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -314,31 +314,6 @@
     return result;
 }
 
-static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
-    JNIEnv* env;
-    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        return 0;
-    }
-    return env;
-}
-
-static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) {
-    JNIEnv* env = GetJNIEnvironment(jvm);
-    if (!env) {
-        int result = jvm->AttachCurrentThread(&env, nullptr);
-        CHECK_EQ(result, JNI_OK) << "thread attach failed";
-        struct VmDetacher {
-            VmDetacher(JavaVM* vm) : mVm(vm) {}
-            ~VmDetacher() { mVm->DetachCurrentThread(); }
-
-        private:
-            JavaVM* const mVm;
-        };
-        static thread_local VmDetacher detacher(jvm);
-    }
-    return env;
-}
-
 class PMSCDataLoader;
 
 struct OnTraceChanged {
@@ -415,7 +390,7 @@
     bool onPrepareImage(dataloader::DataLoaderInstallationFiles addedFiles) final {
         ALOGE("onPrepareImage: start.");
 
-        JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
+        JNIEnv* env = GetOrAttachJNIEnvironment(mJvm, JNI_VERSION_1_6);
         const auto& jni = jniIds(env);
 
         jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 6f24e3b..5df1ada 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -37,7 +37,7 @@
 int register_android_server_UsbMidiDevice(JNIEnv* env);
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_vr_VrManagerService(JNIEnv* env);
-int register_android_server_VibratorService(JNIEnv* env);
+int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env);
 int register_android_server_location_GnssLocationProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
 int register_android_server_TestNetworkService(JNIEnv* env);
@@ -90,7 +90,7 @@
     register_android_server_UsbAlsaJackDetector(env);
     register_android_server_UsbHostManager(env);
     register_android_server_vr_VrManagerService(env);
-    register_android_server_VibratorService(env);
+    register_android_server_VibratorService(vm, env);
     register_android_server_SystemServer(env);
     register_android_server_location_GnssLocationProvider(env);
     register_android_server_connectivity_Vpn(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
new file mode 100644
index 0000000..6fea2aa
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -0,0 +1,1116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.FactoryResetProtectionPolicy;
+import android.app.admin.PasswordPolicy;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import com.android.server.pm.UserRestrictionsUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+class ActiveAdmin {
+    private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
+    private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
+    private static final String TAG_DISABLE_CAMERA = "disable-camera";
+    private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+    private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
+    private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING =
+            "disable-bt-contacts-sharing";
+    private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
+    private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
+    private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time";
+    private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";
+    private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled";
+    private static final String TAG_ACCOUNT_TYPE = "account-type";
+    private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES =
+            "permitted-accessiblity-services";
+    private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
+    private static final String TAG_MANAGE_TRUST_AGENT_FEATURES = "manage-trust-agent-features";
+    private static final String TAG_TRUST_AGENT_COMPONENT_OPTIONS = "trust-agent-component-options";
+    private static final String TAG_TRUST_AGENT_COMPONENT = "component";
+    private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
+    private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
+    private static final String TAG_GLOBAL_PROXY_EXCLUSION_LIST = "global-proxy-exclusion-list";
+    private static final String TAG_GLOBAL_PROXY_SPEC = "global-proxy-spec";
+    private static final String TAG_SPECIFIES_GLOBAL_PROXY = "specifies-global-proxy";
+    private static final String TAG_PERMITTED_IMES = "permitted-imes";
+    private static final String TAG_PERMITTED_NOTIFICATION_LISTENERS =
+            "permitted-notification-listeners";
+    private static final String TAG_MAX_FAILED_PASSWORD_WIPE = "max-failed-password-wipe";
+    private static final String TAG_MAX_TIME_TO_UNLOCK = "max-time-to-unlock";
+    private static final String TAG_STRONG_AUTH_UNLOCK_TIMEOUT = "strong-auth-unlock-timeout";
+    private static final String TAG_MIN_PASSWORD_NONLETTER = "min-password-nonletter";
+    private static final String TAG_MIN_PASSWORD_SYMBOLS = "min-password-symbols";
+    private static final String TAG_MIN_PASSWORD_NUMERIC = "min-password-numeric";
+    private static final String TAG_MIN_PASSWORD_LETTERS = "min-password-letters";
+    private static final String TAG_MIN_PASSWORD_LOWERCASE = "min-password-lowercase";
+    private static final String TAG_MIN_PASSWORD_UPPERCASE = "min-password-uppercase";
+    private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length";
+    private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length";
+    private static final String TAG_PASSWORD_QUALITY = "password-quality";
+    private static final String TAG_POLICIES = "policies";
+    private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
+            "cross-profile-widget-providers";
+    private static final String TAG_PROVIDER = "provider";
+    private static final String TAG_PACKAGE_LIST_ITEM  = "item";
+    private static final String TAG_KEEP_UNINSTALLED_PACKAGES  = "keep-uninstalled-packages";
+    private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
+    private static final String TAG_DEFAULT_ENABLED_USER_RESTRICTIONS =
+            "default-enabled-user-restrictions";
+    private static final String TAG_RESTRICTION = "restriction";
+    private static final String TAG_SHORT_SUPPORT_MESSAGE = "short-support-message";
+    private static final String TAG_LONG_SUPPORT_MESSAGE = "long-support-message";
+    private static final String TAG_PARENT_ADMIN = "parent-admin";
+    private static final String TAG_ORGANIZATION_COLOR = "organization-color";
+    private static final String TAG_ORGANIZATION_NAME = "organization-name";
+    private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
+    private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
+    private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
+    private static final String TAG_METERED_DATA_DISABLED_PACKAGES =
+            "metered_data_disabled_packages";
+    private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
+            "cross-profile-calendar-packages";
+    private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL =
+            "cross-profile-calendar-packages-null";
+    private static final String TAG_CROSS_PROFILE_PACKAGES = "cross-profile-packages";
+    private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
+            "factory_reset_protection_policy";
+    private static final String TAG_SUSPEND_PERSONAL_APPS = "suspend-personal-apps";
+    private static final String TAG_PROFILE_MAXIMUM_TIME_OFF = "profile-max-time-off";
+    private static final String TAG_PROFILE_OFF_DEADLINE = "profile-off-deadline";
+    private static final String TAG_ALWAYS_ON_VPN_PACKAGE = "vpn-package";
+    private static final String TAG_ALWAYS_ON_VPN_LOCKDOWN = "vpn-lockdown";
+    private static final String TAG_COMMON_CRITERIA_MODE = "common-criteria-mode";
+    private static final String ATTR_VALUE = "value";
+    private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
+    private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
+
+    DeviceAdminInfo info;
+
+    static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
+    int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH;
+
+    @NonNull
+    PasswordPolicy mPasswordPolicy = new PasswordPolicy();
+
+    @Nullable
+    FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null;
+
+    static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
+    long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
+
+    long strongAuthUnlockTimeout = 0; // admin doesn't participate by default
+
+    static final int DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE = 0;
+    int maximumFailedPasswordsForWipe = DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE;
+
+    static final long DEF_PASSWORD_EXPIRATION_TIMEOUT = 0;
+    long passwordExpirationTimeout = DEF_PASSWORD_EXPIRATION_TIMEOUT;
+
+    static final long DEF_PASSWORD_EXPIRATION_DATE = 0;
+    long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
+
+    static final int DEF_KEYGUARD_FEATURES_DISABLED = 0; // none
+
+    int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED;
+
+    boolean encryptionRequested = false;
+    boolean testOnlyAdmin = false;
+    boolean disableCamera = false;
+    boolean disableCallerId = false;
+    boolean disableContactsSearch = false;
+    boolean disableBluetoothContactSharing = true;
+    boolean disableScreenCapture = false;
+    boolean requireAutoTime = false;
+    boolean forceEphemeralUsers = false;
+    boolean isNetworkLoggingEnabled = false;
+    boolean isLogoutEnabled = false;
+
+    // one notification after enabling + one more after reboots
+    static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
+    int numNetworkLoggingNotifications = 0;
+    long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch
+
+    ActiveAdmin parentAdmin;
+    final boolean isParent;
+
+    static class TrustAgentInfo {
+        public PersistableBundle options;
+        TrustAgentInfo(PersistableBundle bundle) {
+            options = bundle;
+        }
+    }
+
+    // The list of packages which are not allowed to use metered data.
+    List<String> meteredDisabledPackages;
+
+    final Set<String> accountTypesWithManagementDisabled = new ArraySet<>();
+
+    // The list of permitted accessibility services package namesas set by a profile
+    // or device owner. Null means all accessibility services are allowed, empty means
+    // none except system services are allowed.
+    List<String> permittedAccessiblityServices;
+
+    // The list of permitted input methods package names as set by a profile or device owner.
+    // Null means all input methods are allowed, empty means none except system imes are
+    // allowed.
+    List<String> permittedInputMethods;
+
+    // The list of packages allowed to use a NotificationListenerService to receive events for
+    // notifications from this user. Null means that all packages are allowed. Empty list means
+    // that only packages from the system are allowed.
+    List<String> permittedNotificationListeners;
+
+    // List of package names to keep cached.
+    List<String> keepUninstalledPackages;
+
+    // TODO: review implementation decisions with frameworks team
+    boolean specifiesGlobalProxy = false;
+    String globalProxySpec = null;
+    String globalProxyExclusionList = null;
+
+    @NonNull
+    ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>();
+
+    List<String> crossProfileWidgetProviders;
+
+    Bundle userRestrictions;
+
+    // User restrictions that have already been enabled by default for this admin (either when
+    // setting the device or profile owner, or during a system update if one of those "enabled
+    // by default" restrictions is newly added).
+    final Set<String> defaultEnabledRestrictionsAlreadySet = new ArraySet<>();
+
+    // Support text provided by the admin to display to the user.
+    CharSequence shortSupportMessage = null;
+    CharSequence longSupportMessage = null;
+
+    // Background color of confirm credentials screen. Default: teal.
+    static final int DEF_ORGANIZATION_COLOR = Color.parseColor("#00796B");
+    int organizationColor = DEF_ORGANIZATION_COLOR;
+
+    // Default title of confirm credentials screen
+    String organizationName = null;
+
+    // Message for user switcher
+    String startUserSessionMessage = null;
+    String endUserSessionMessage = null;
+
+    // The allow list of packages that can access cross profile calendar APIs.
+    // This allow list should be in default an empty list, which indicates that no package
+    // is allow listed.
+    List<String> mCrossProfileCalendarPackages = Collections.emptyList();
+
+    // The allow list of packages that the admin has enabled to be able to request consent from
+    // the user to communicate cross-profile. By default, no packages are allowed, which is
+    // represented as an empty list.
+    List<String> mCrossProfilePackages = Collections.emptyList();
+
+    // Whether the admin explicitly requires personal apps to be suspended
+    boolean mSuspendPersonalApps = false;
+    // Maximum time the profile owned by this admin can be off.
+    long mProfileMaximumTimeOffMillis = 0;
+    // Time by which the profile should be turned on according to System.currentTimeMillis().
+    long mProfileOffDeadline = 0;
+
+    public String mAlwaysOnVpnPackage;
+    public boolean mAlwaysOnVpnLockdown;
+    boolean mCommonCriteriaMode;
+
+    ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
+        this.info = info;
+        this.isParent = isParent;
+    }
+
+    ActiveAdmin getParentActiveAdmin() {
+        Preconditions.checkState(!isParent);
+
+        if (parentAdmin == null) {
+            parentAdmin = new ActiveAdmin(info, /* parent */ true);
+        }
+        return parentAdmin;
+    }
+
+    boolean hasParentActiveAdmin() {
+        return parentAdmin != null;
+    }
+
+    int getUid() {
+        return info.getActivityInfo().applicationInfo.uid;
+    }
+
+    public UserHandle getUserHandle() {
+        return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid));
+    }
+
+    void writeToXml(XmlSerializer out)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        out.startTag(null, TAG_POLICIES);
+        info.writePoliciesToXml(out);
+        out.endTag(null, TAG_POLICIES);
+        if (mPasswordPolicy.quality != PASSWORD_QUALITY_UNSPECIFIED) {
+            writeAttributeValueToXml(
+                    out, TAG_PASSWORD_QUALITY, mPasswordPolicy.quality);
+            if (mPasswordPolicy.length != PasswordPolicy.DEF_MINIMUM_LENGTH) {
+                writeAttributeValueToXml(
+                        out, TAG_MIN_PASSWORD_LENGTH, mPasswordPolicy.length);
+            }
+            if (mPasswordPolicy.upperCase != PasswordPolicy.DEF_MINIMUM_UPPER_CASE) {
+                writeAttributeValueToXml(
+                        out, TAG_MIN_PASSWORD_UPPERCASE, mPasswordPolicy.upperCase);
+            }
+            if (mPasswordPolicy.lowerCase != PasswordPolicy.DEF_MINIMUM_LOWER_CASE) {
+                writeAttributeValueToXml(
+                        out, TAG_MIN_PASSWORD_LOWERCASE, mPasswordPolicy.lowerCase);
+            }
+            if (mPasswordPolicy.letters != PasswordPolicy.DEF_MINIMUM_LETTERS) {
+                writeAttributeValueToXml(
+                        out, TAG_MIN_PASSWORD_LETTERS, mPasswordPolicy.letters);
+            }
+            if (mPasswordPolicy.numeric != PasswordPolicy.DEF_MINIMUM_NUMERIC) {
+                writeAttributeValueToXml(
+                        out, TAG_MIN_PASSWORD_NUMERIC, mPasswordPolicy.numeric);
+            }
+            if (mPasswordPolicy.symbols != PasswordPolicy.DEF_MINIMUM_SYMBOLS) {
+                writeAttributeValueToXml(
+                        out, TAG_MIN_PASSWORD_SYMBOLS, mPasswordPolicy.symbols);
+            }
+            if (mPasswordPolicy.nonLetter > PasswordPolicy.DEF_MINIMUM_NON_LETTER) {
+                writeAttributeValueToXml(
+                        out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter);
+            }
+        }
+        if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
+            writeAttributeValueToXml(
+                    out, TAG_PASSWORD_HISTORY_LENGTH, passwordHistoryLength);
+        }
+        if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) {
+            writeAttributeValueToXml(
+                    out, TAG_MAX_TIME_TO_UNLOCK, maximumTimeToUnlock);
+        }
+        if (strongAuthUnlockTimeout != DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
+            writeAttributeValueToXml(
+                    out, TAG_STRONG_AUTH_UNLOCK_TIMEOUT, strongAuthUnlockTimeout);
+        }
+        if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
+            writeAttributeValueToXml(
+                    out, TAG_MAX_FAILED_PASSWORD_WIPE, maximumFailedPasswordsForWipe);
+        }
+        if (specifiesGlobalProxy) {
+            writeAttributeValueToXml(
+                    out, TAG_SPECIFIES_GLOBAL_PROXY, specifiesGlobalProxy);
+            if (globalProxySpec != null) {
+                writeAttributeValueToXml(out, TAG_GLOBAL_PROXY_SPEC, globalProxySpec);
+            }
+            if (globalProxyExclusionList != null) {
+                writeAttributeValueToXml(
+                        out, TAG_GLOBAL_PROXY_EXCLUSION_LIST, globalProxyExclusionList);
+            }
+        }
+        if (passwordExpirationTimeout != DEF_PASSWORD_EXPIRATION_TIMEOUT) {
+            writeAttributeValueToXml(
+                    out, TAG_PASSWORD_EXPIRATION_TIMEOUT, passwordExpirationTimeout);
+        }
+        if (passwordExpirationDate != DEF_PASSWORD_EXPIRATION_DATE) {
+            writeAttributeValueToXml(
+                    out, TAG_PASSWORD_EXPIRATION_DATE, passwordExpirationDate);
+        }
+        if (encryptionRequested) {
+            writeAttributeValueToXml(
+                    out, TAG_ENCRYPTION_REQUESTED, encryptionRequested);
+        }
+        if (testOnlyAdmin) {
+            writeAttributeValueToXml(
+                    out, TAG_TEST_ONLY_ADMIN, testOnlyAdmin);
+        }
+        if (disableCamera) {
+            writeAttributeValueToXml(
+                    out, TAG_DISABLE_CAMERA, disableCamera);
+        }
+        if (disableCallerId) {
+            writeAttributeValueToXml(
+                    out, TAG_DISABLE_CALLER_ID, disableCallerId);
+        }
+        if (disableContactsSearch) {
+            writeAttributeValueToXml(
+                    out, TAG_DISABLE_CONTACTS_SEARCH, disableContactsSearch);
+        }
+        if (!disableBluetoothContactSharing) {
+            writeAttributeValueToXml(
+                    out, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING, disableBluetoothContactSharing);
+        }
+        if (disableScreenCapture) {
+            writeAttributeValueToXml(
+                    out, TAG_DISABLE_SCREEN_CAPTURE, disableScreenCapture);
+        }
+        if (requireAutoTime) {
+            writeAttributeValueToXml(
+                    out, TAG_REQUIRE_AUTO_TIME, requireAutoTime);
+        }
+        if (forceEphemeralUsers) {
+            writeAttributeValueToXml(
+                    out, TAG_FORCE_EPHEMERAL_USERS, forceEphemeralUsers);
+        }
+        if (isNetworkLoggingEnabled) {
+            out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
+            out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled));
+            out.attribute(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS,
+                    Integer.toString(numNetworkLoggingNotifications));
+            out.attribute(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION,
+                    Long.toString(lastNetworkLoggingNotificationTimeMs));
+            out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
+        }
+        if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
+            writeAttributeValueToXml(
+                    out, TAG_DISABLE_KEYGUARD_FEATURES, disabledKeyguardFeatures);
+        }
+        if (!accountTypesWithManagementDisabled.isEmpty()) {
+            writeAttributeValuesToXml(
+                    out, TAG_DISABLE_ACCOUNT_MANAGEMENT, TAG_ACCOUNT_TYPE,
+                    accountTypesWithManagementDisabled);
+        }
+        if (!trustAgentInfos.isEmpty()) {
+            Set<Map.Entry<String, TrustAgentInfo>> set = trustAgentInfos.entrySet();
+            out.startTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
+            for (Map.Entry<String, TrustAgentInfo> entry : set) {
+                TrustAgentInfo trustAgentInfo = entry.getValue();
+                out.startTag(null, TAG_TRUST_AGENT_COMPONENT);
+                out.attribute(null, ATTR_VALUE, entry.getKey());
+                if (trustAgentInfo.options != null) {
+                    out.startTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
+                    try {
+                        trustAgentInfo.options.saveToXml(out);
+                    } catch (XmlPullParserException e) {
+                        Log.e(DevicePolicyManagerService.LOG_TAG,
+                                "Failed to save TrustAgent options", e);
+                    }
+                    out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
+                }
+                out.endTag(null, TAG_TRUST_AGENT_COMPONENT);
+            }
+            out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
+        }
+        if (crossProfileWidgetProviders != null && !crossProfileWidgetProviders.isEmpty()) {
+            writeAttributeValuesToXml(
+                    out, TAG_CROSS_PROFILE_WIDGET_PROVIDERS, TAG_PROVIDER,
+                    crossProfileWidgetProviders);
+        }
+        writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
+                permittedAccessiblityServices);
+        writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods);
+        writePackageListToXml(out, TAG_PERMITTED_NOTIFICATION_LISTENERS,
+                permittedNotificationListeners);
+        writePackageListToXml(out, TAG_KEEP_UNINSTALLED_PACKAGES, keepUninstalledPackages);
+        writePackageListToXml(out, TAG_METERED_DATA_DISABLED_PACKAGES, meteredDisabledPackages);
+        if (hasUserRestrictions()) {
+            UserRestrictionsUtils.writeRestrictions(
+                    out, userRestrictions, TAG_USER_RESTRICTIONS);
+        }
+        if (!defaultEnabledRestrictionsAlreadySet.isEmpty()) {
+            writeAttributeValuesToXml(out, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS,
+                    TAG_RESTRICTION,
+                    defaultEnabledRestrictionsAlreadySet);
+        }
+        if (!TextUtils.isEmpty(shortSupportMessage)) {
+            writeTextToXml(out, TAG_SHORT_SUPPORT_MESSAGE, shortSupportMessage.toString());
+        }
+        if (!TextUtils.isEmpty(longSupportMessage)) {
+            writeTextToXml(out, TAG_LONG_SUPPORT_MESSAGE, longSupportMessage.toString());
+        }
+        if (parentAdmin != null) {
+            out.startTag(null, TAG_PARENT_ADMIN);
+            parentAdmin.writeToXml(out);
+            out.endTag(null, TAG_PARENT_ADMIN);
+        }
+        if (organizationColor != DEF_ORGANIZATION_COLOR) {
+            writeAttributeValueToXml(out, TAG_ORGANIZATION_COLOR, organizationColor);
+        }
+        if (organizationName != null) {
+            writeTextToXml(out, TAG_ORGANIZATION_NAME, organizationName);
+        }
+        if (isLogoutEnabled) {
+            writeAttributeValueToXml(out, TAG_IS_LOGOUT_ENABLED, isLogoutEnabled);
+        }
+        if (startUserSessionMessage != null) {
+            writeTextToXml(out, TAG_START_USER_SESSION_MESSAGE, startUserSessionMessage);
+        }
+        if (endUserSessionMessage != null) {
+            writeTextToXml(out, TAG_END_USER_SESSION_MESSAGE, endUserSessionMessage);
+        }
+        if (mCrossProfileCalendarPackages == null) {
+            out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
+            out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
+        } else {
+            writePackageListToXml(out, TAG_CROSS_PROFILE_CALENDAR_PACKAGES,
+                    mCrossProfileCalendarPackages);
+        }
+        writePackageListToXml(out, TAG_CROSS_PROFILE_PACKAGES, mCrossProfilePackages);
+        if (mFactoryResetProtectionPolicy != null) {
+            out.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+            mFactoryResetProtectionPolicy.writeToXml(out);
+            out.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+        }
+        if (mSuspendPersonalApps) {
+            writeAttributeValueToXml(out, TAG_SUSPEND_PERSONAL_APPS, mSuspendPersonalApps);
+        }
+        if (mProfileMaximumTimeOffMillis != 0) {
+            writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF,
+                    mProfileMaximumTimeOffMillis);
+        }
+        if (mProfileMaximumTimeOffMillis != 0) {
+            writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline);
+        }
+        if (!TextUtils.isEmpty(mAlwaysOnVpnPackage)) {
+            writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_PACKAGE, mAlwaysOnVpnPackage);
+        }
+        if (mAlwaysOnVpnLockdown) {
+            writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_LOCKDOWN, mAlwaysOnVpnLockdown);
+        }
+        if (mCommonCriteriaMode) {
+            writeAttributeValueToXml(out, TAG_COMMON_CRITERIA_MODE, mCommonCriteriaMode);
+        }
+    }
+
+    void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
+        out.startTag(null, tag);
+        out.text(text);
+        out.endTag(null, tag);
+    }
+
+    void writePackageListToXml(XmlSerializer out, String outerTag,
+            List<String> packageList)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        if (packageList == null) {
+            return;
+        }
+        writeAttributeValuesToXml(out, outerTag, TAG_PACKAGE_LIST_ITEM, packageList);
+    }
+
+    void writeAttributeValueToXml(XmlSerializer out, String tag, String value)
+            throws IOException {
+        out.startTag(null, tag);
+        out.attribute(null, ATTR_VALUE, value);
+        out.endTag(null, tag);
+    }
+
+    void writeAttributeValueToXml(XmlSerializer out, String tag, int value)
+            throws IOException {
+        out.startTag(null, tag);
+        out.attribute(null, ATTR_VALUE, Integer.toString(value));
+        out.endTag(null, tag);
+    }
+
+    void writeAttributeValueToXml(XmlSerializer out, String tag, long value)
+            throws IOException {
+        out.startTag(null, tag);
+        out.attribute(null, ATTR_VALUE, Long.toString(value));
+        out.endTag(null, tag);
+    }
+
+    void writeAttributeValueToXml(XmlSerializer out, String tag, boolean value)
+            throws IOException {
+        out.startTag(null, tag);
+        out.attribute(null, ATTR_VALUE, Boolean.toString(value));
+        out.endTag(null, tag);
+    }
+
+    void writeAttributeValuesToXml(XmlSerializer out, String outerTag, String innerTag,
+            @NonNull Collection<String> values) throws IOException {
+        out.startTag(null, outerTag);
+        for (String value : values) {
+            out.startTag(null, innerTag);
+            out.attribute(null, ATTR_VALUE, value);
+            out.endTag(null, innerTag);
+        }
+        out.endTag(null, outerTag);
+    }
+
+    void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != END_DOCUMENT
+               && (type != END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == END_TAG || type == TEXT) {
+                continue;
+            }
+            String tag = parser.getName();
+            if (TAG_POLICIES.equals(tag)) {
+                if (shouldOverridePolicies) {
+                    Log.d(DevicePolicyManagerService.LOG_TAG,
+                            "Overriding device admin policies from XML.");
+                    info.readPoliciesFromXml(parser);
+                }
+            } else if (TAG_PASSWORD_QUALITY.equals(tag)) {
+                mPasswordPolicy.quality = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) {
+                mPasswordPolicy.length = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) {
+                passwordHistoryLength = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) {
+                mPasswordPolicy.upperCase = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) {
+                mPasswordPolicy.lowerCase = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) {
+                mPasswordPolicy.letters = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) {
+                mPasswordPolicy.numeric = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) {
+                mPasswordPolicy.symbols = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
+                mPasswordPolicy.nonLetter = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
+                maximumTimeToUnlock = Long.parseLong(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) {
+                strongAuthUnlockTimeout = Long.parseLong(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_MAX_FAILED_PASSWORD_WIPE.equals(tag)) {
+                maximumFailedPasswordsForWipe = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_SPECIFIES_GLOBAL_PROXY.equals(tag)) {
+                specifiesGlobalProxy = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_GLOBAL_PROXY_SPEC.equals(tag)) {
+                globalProxySpec =
+                    parser.getAttributeValue(null, ATTR_VALUE);
+            } else if (TAG_GLOBAL_PROXY_EXCLUSION_LIST.equals(tag)) {
+                globalProxyExclusionList =
+                    parser.getAttributeValue(null, ATTR_VALUE);
+            } else if (TAG_PASSWORD_EXPIRATION_TIMEOUT.equals(tag)) {
+                passwordExpirationTimeout = Long.parseLong(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_PASSWORD_EXPIRATION_DATE.equals(tag)) {
+                passwordExpirationDate = Long.parseLong(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) {
+                encryptionRequested = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_TEST_ONLY_ADMIN.equals(tag)) {
+                testOnlyAdmin = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_DISABLE_CAMERA.equals(tag)) {
+                disableCamera = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
+                disableCallerId = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) {
+                disableContactsSearch = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) {
+                disableBluetoothContactSharing = Boolean.parseBoolean(parser
+                        .getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) {
+                disableScreenCapture = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_REQUIRE_AUTO_TIME.equals(tag)) {
+                requireAutoTime = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) {
+                forceEphemeralUsers = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) {
+                isNetworkLoggingEnabled = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+                lastNetworkLoggingNotificationTimeMs = Long.parseLong(
+                        parser.getAttributeValue(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION));
+                numNetworkLoggingNotifications = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS));
+            } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
+                disabledKeyguardFeatures = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
+                readAttributeValues(
+                        parser, TAG_ACCOUNT_TYPE, accountTypesWithManagementDisabled);
+            } else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) {
+                trustAgentInfos = getAllTrustAgentInfos(parser, tag);
+            } else if (TAG_CROSS_PROFILE_WIDGET_PROVIDERS.equals(tag)) {
+                crossProfileWidgetProviders = new ArrayList<>();
+                readAttributeValues(parser, TAG_PROVIDER, crossProfileWidgetProviders);
+            } else if (TAG_PERMITTED_ACCESSIBILITY_SERVICES.equals(tag)) {
+                permittedAccessiblityServices = readPackageList(parser, tag);
+            } else if (TAG_PERMITTED_IMES.equals(tag)) {
+                permittedInputMethods = readPackageList(parser, tag);
+            } else if (TAG_PERMITTED_NOTIFICATION_LISTENERS.equals(tag)) {
+                permittedNotificationListeners = readPackageList(parser, tag);
+            } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) {
+                keepUninstalledPackages = readPackageList(parser, tag);
+            } else if (TAG_METERED_DATA_DISABLED_PACKAGES.equals(tag)) {
+                meteredDisabledPackages = readPackageList(parser, tag);
+            } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
+                userRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+            } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) {
+                readAttributeValues(
+                        parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet);
+            } else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) {
+                type = parser.next();
+                if (type == XmlPullParser.TEXT) {
+                    shortSupportMessage = parser.getText();
+                } else {
+                    Log.w(DevicePolicyManagerService.LOG_TAG,
+                            "Missing text when loading short support message");
+                }
+            } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
+                type = parser.next();
+                if (type == XmlPullParser.TEXT) {
+                    longSupportMessage = parser.getText();
+                } else {
+                    Log.w(DevicePolicyManagerService.LOG_TAG,
+                            "Missing text when loading long support message");
+                }
+            } else if (TAG_PARENT_ADMIN.equals(tag)) {
+                Preconditions.checkState(!isParent);
+                parentAdmin = new ActiveAdmin(info, /* parent */ true);
+                parentAdmin.readFromXml(parser, shouldOverridePolicies);
+            } else if (TAG_ORGANIZATION_COLOR.equals(tag)) {
+                organizationColor = Integer.parseInt(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_ORGANIZATION_NAME.equals(tag)) {
+                type = parser.next();
+                if (type == XmlPullParser.TEXT) {
+                    organizationName = parser.getText();
+                } else {
+                    Log.w(DevicePolicyManagerService.LOG_TAG,
+                            "Missing text when loading organization name");
+                }
+            } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
+                isLogoutEnabled = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) {
+                type = parser.next();
+                if (type == XmlPullParser.TEXT) {
+                    startUserSessionMessage = parser.getText();
+                } else {
+                    Log.w(DevicePolicyManagerService.LOG_TAG,
+                            "Missing text when loading start session message");
+                }
+            } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
+                type = parser.next();
+                if (type == XmlPullParser.TEXT) {
+                    endUserSessionMessage = parser.getText();
+                } else {
+                    Log.w(DevicePolicyManagerService.LOG_TAG,
+                            "Missing text when loading end session message");
+                }
+            } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
+                mCrossProfileCalendarPackages = readPackageList(parser, tag);
+            } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL.equals(tag)) {
+                mCrossProfileCalendarPackages = null;
+            } else if (TAG_CROSS_PROFILE_PACKAGES.equals(tag)) {
+                mCrossProfilePackages = readPackageList(parser, tag);
+            } else if (TAG_FACTORY_RESET_PROTECTION_POLICY.equals(tag)) {
+                mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml(
+                            parser);
+            } else if (TAG_SUSPEND_PERSONAL_APPS.equals(tag)) {
+                mSuspendPersonalApps = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) {
+                mProfileMaximumTimeOffMillis =
+                        Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) {
+                mProfileOffDeadline =
+                        Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_ALWAYS_ON_VPN_PACKAGE.equals(tag)) {
+                mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE);
+            } else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) {
+                mAlwaysOnVpnLockdown = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_COMMON_CRITERIA_MODE.equals(tag)) {
+                mCommonCriteriaMode = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
+            } else {
+                Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
+    private List<String> readPackageList(XmlPullParser parser,
+            String tag) throws XmlPullParserException, IOException {
+        List<String> result = new ArrayList<String>();
+        int outerDepth = parser.getDepth();
+        int outerType;
+        while ((outerType = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (outerType != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (outerType == XmlPullParser.END_TAG || outerType == XmlPullParser.TEXT) {
+                continue;
+            }
+            String outerTag = parser.getName();
+            if (TAG_PACKAGE_LIST_ITEM.equals(outerTag)) {
+                String packageName = parser.getAttributeValue(null, ATTR_VALUE);
+                if (packageName != null) {
+                    result.add(packageName);
+                } else {
+                    Slog.w(DevicePolicyManagerService.LOG_TAG,
+                            "Package name missing under " + outerTag);
+                }
+            } else {
+                Slog.w(DevicePolicyManagerService.LOG_TAG,
+                        "Unknown tag under " + tag +  ": " + outerTag);
+            }
+        }
+        return result;
+    }
+
+    private void readAttributeValues(
+            XmlPullParser parser, String tag, Collection<String> result)
+            throws XmlPullParserException, IOException {
+        result.clear();
+        int outerDepthDAM = parser.getDepth();
+        int typeDAM;
+        while ((typeDAM = parser.next()) != END_DOCUMENT
+                && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
+            if (typeDAM == END_TAG || typeDAM == TEXT) {
+                continue;
+            }
+            String tagDAM = parser.getName();
+            if (tag.equals(tagDAM)) {
+                result.add(parser.getAttributeValue(null, ATTR_VALUE));
+            } else {
+                Slog.e(DevicePolicyManagerService.LOG_TAG,
+                        "Expected tag " + tag +  " but found " + tagDAM);
+            }
+        }
+    }
+
+    @NonNull
+    private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos(
+            XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
+        int outerDepthDAM = parser.getDepth();
+        int typeDAM;
+        final ArrayMap<String, TrustAgentInfo> result = new ArrayMap<>();
+        while ((typeDAM = parser.next()) != END_DOCUMENT
+                && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
+            if (typeDAM == END_TAG || typeDAM == TEXT) {
+                continue;
+            }
+            String tagDAM = parser.getName();
+            if (TAG_TRUST_AGENT_COMPONENT.equals(tagDAM)) {
+                final String component = parser.getAttributeValue(null, ATTR_VALUE);
+                final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
+                result.put(component, trustAgentInfo);
+            } else {
+                Slog.w(DevicePolicyManagerService.LOG_TAG,
+                        "Unknown tag under " + tag +  ": " + tagDAM);
+            }
+        }
+        return result;
+    }
+
+    private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag)
+            throws XmlPullParserException, IOException  {
+        int outerDepthDAM = parser.getDepth();
+        int typeDAM;
+        TrustAgentInfo result = new TrustAgentInfo(null);
+        while ((typeDAM = parser.next()) != END_DOCUMENT
+                && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
+            if (typeDAM == END_TAG || typeDAM == TEXT) {
+                continue;
+            }
+            String tagDAM = parser.getName();
+            if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
+                result.options = PersistableBundle.restoreFromXml(parser);
+            } else {
+                Slog.w(DevicePolicyManagerService.LOG_TAG,
+                        "Unknown tag under " + tag +  ": " + tagDAM);
+            }
+        }
+        return result;
+    }
+
+    boolean hasUserRestrictions() {
+        return userRestrictions != null && userRestrictions.size() > 0;
+    }
+
+    Bundle ensureUserRestrictions() {
+        if (userRestrictions == null) {
+            userRestrictions = new Bundle();
+        }
+        return userRestrictions;
+    }
+
+    public void transfer(DeviceAdminInfo deviceAdminInfo) {
+        if (hasParentActiveAdmin()) {
+            parentAdmin.info = deviceAdminInfo;
+        }
+        info = deviceAdminInfo;
+    }
+
+    Bundle addSyntheticRestrictions(Bundle restrictions) {
+        if (disableCamera) {
+            restrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
+        }
+        if (requireAutoTime) {
+            restrictions.putBoolean(UserManager.DISALLOW_CONFIG_DATE_TIME, true);
+        }
+        return restrictions;
+    }
+
+    static Bundle removeDeprecatedRestrictions(Bundle restrictions) {
+        for (String deprecatedRestriction: UserRestrictionsUtils.DEPRECATED_USER_RESTRICTIONS) {
+            restrictions.remove(deprecatedRestriction);
+        }
+        return restrictions;
+    }
+
+    static Bundle filterRestrictions(Bundle restrictions, Predicate<String> filter) {
+        Bundle result = new Bundle();
+        for (String key : restrictions.keySet()) {
+            if (!restrictions.getBoolean(key)) {
+                continue;
+            }
+            if (filter.test(key)) {
+                result.putBoolean(key, true);
+            }
+        }
+        return result;
+    }
+
+    Bundle getEffectiveRestrictions() {
+        return addSyntheticRestrictions(
+                removeDeprecatedRestrictions(new Bundle(ensureUserRestrictions())));
+    }
+
+    Bundle getLocalUserRestrictions(int adminType) {
+        return filterRestrictions(getEffectiveRestrictions(),
+                key -> UserRestrictionsUtils.isLocal(adminType, key));
+    }
+
+    Bundle getGlobalUserRestrictions(int adminType) {
+        return filterRestrictions(getEffectiveRestrictions(),
+                key -> UserRestrictionsUtils.isGlobal(adminType, key));
+    }
+
+    void dump(IndentingPrintWriter pw) {
+        pw.print("uid=");
+        pw.println(getUid());
+
+        pw.print("testOnlyAdmin=");
+        pw.println(testOnlyAdmin);
+
+        pw.println("policies:");
+        ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
+        if (pols != null) {
+            pw.increaseIndent();
+            for (int i = 0; i < pols.size(); i++) {
+                pw.println(pols.get(i).tag);
+            }
+            pw.decreaseIndent();
+        }
+
+        pw.print("passwordQuality=0x");
+        pw.println(Integer.toHexString(mPasswordPolicy.quality));
+
+        pw.print("minimumPasswordLength=");
+        pw.println(mPasswordPolicy.length);
+
+        pw.print("passwordHistoryLength=");
+        pw.println(passwordHistoryLength);
+
+        pw.print("minimumPasswordUpperCase=");
+        pw.println(mPasswordPolicy.upperCase);
+
+        pw.print("minimumPasswordLowerCase=");
+        pw.println(mPasswordPolicy.lowerCase);
+
+        pw.print("minimumPasswordLetters=");
+        pw.println(mPasswordPolicy.letters);
+
+        pw.print("minimumPasswordNumeric=");
+        pw.println(mPasswordPolicy.numeric);
+
+        pw.print("minimumPasswordSymbols=");
+        pw.println(mPasswordPolicy.symbols);
+
+        pw.print("minimumPasswordNonLetter=");
+        pw.println(mPasswordPolicy.nonLetter);
+
+        pw.print("maximumTimeToUnlock=");
+        pw.println(maximumTimeToUnlock);
+
+        pw.print("strongAuthUnlockTimeout=");
+        pw.println(strongAuthUnlockTimeout);
+
+        pw.print("maximumFailedPasswordsForWipe=");
+        pw.println(maximumFailedPasswordsForWipe);
+
+        pw.print("specifiesGlobalProxy=");
+        pw.println(specifiesGlobalProxy);
+
+        pw.print("passwordExpirationTimeout=");
+        pw.println(passwordExpirationTimeout);
+
+        pw.print("passwordExpirationDate=");
+        pw.println(passwordExpirationDate);
+
+        if (globalProxySpec != null) {
+            pw.print("globalProxySpec=");
+            pw.println(globalProxySpec);
+        }
+        if (globalProxyExclusionList != null) {
+            pw.print("globalProxyEclusionList=");
+            pw.println(globalProxyExclusionList);
+        }
+        pw.print("encryptionRequested=");
+        pw.println(encryptionRequested);
+
+        pw.print("disableCamera=");
+        pw.println(disableCamera);
+
+        pw.print("disableCallerId=");
+        pw.println(disableCallerId);
+
+        pw.print("disableContactsSearch=");
+        pw.println(disableContactsSearch);
+
+        pw.print("disableBluetoothContactSharing=");
+        pw.println(disableBluetoothContactSharing);
+
+        pw.print("disableScreenCapture=");
+        pw.println(disableScreenCapture);
+
+        pw.print("requireAutoTime=");
+        pw.println(requireAutoTime);
+
+        pw.print("forceEphemeralUsers=");
+        pw.println(forceEphemeralUsers);
+
+        pw.print("isNetworkLoggingEnabled=");
+        pw.println(isNetworkLoggingEnabled);
+
+        pw.print("disabledKeyguardFeatures=");
+        pw.println(disabledKeyguardFeatures);
+
+        pw.print("crossProfileWidgetProviders=");
+        pw.println(crossProfileWidgetProviders);
+
+        if (permittedAccessiblityServices != null) {
+            pw.print("permittedAccessibilityServices=");
+            pw.println(permittedAccessiblityServices);
+        }
+
+        if (permittedInputMethods != null) {
+            pw.print("permittedInputMethods=");
+            pw.println(permittedInputMethods);
+        }
+
+        if (permittedNotificationListeners != null) {
+            pw.print("permittedNotificationListeners=");
+            pw.println(permittedNotificationListeners);
+        }
+
+        if (keepUninstalledPackages != null) {
+            pw.print("keepUninstalledPackages=");
+            pw.println(keepUninstalledPackages);
+        }
+
+        pw.print("organizationColor=");
+        pw.println(organizationColor);
+
+        if (organizationName != null) {
+            pw.print("organizationName=");
+            pw.println(organizationName);
+        }
+
+        pw.println("userRestrictions:");
+        UserRestrictionsUtils.dumpRestrictions(pw, "  ", userRestrictions);
+
+        pw.print("defaultEnabledRestrictionsAlreadySet=");
+        pw.println(defaultEnabledRestrictionsAlreadySet);
+
+        pw.print("isParent=");
+        pw.println(isParent);
+
+        if (parentAdmin != null) {
+            pw.println("parentAdmin:");
+            pw.increaseIndent();
+            parentAdmin.dump(pw);
+            pw.decreaseIndent();
+        }
+
+        if (mCrossProfileCalendarPackages != null) {
+            pw.print("mCrossProfileCalendarPackages=");
+            pw.println(mCrossProfileCalendarPackages);
+        }
+
+        pw.print("mCrossProfilePackages=");
+        pw.println(mCrossProfilePackages);
+
+        pw.print("mSuspendPersonalApps=");
+        pw.println(mSuspendPersonalApps);
+
+        pw.print("mProfileMaximumTimeOffMillis=");
+        pw.println(mProfileMaximumTimeOffMillis);
+
+        pw.print("mProfileOffDeadline=");
+        pw.println(mProfileOffDeadline);
+
+        pw.print("mAlwaysOnVpnPackage=");
+        pw.println(mAlwaysOnVpnPackage);
+
+        pw.print("mAlwaysOnVpnLockdown=");
+        pw.println(mAlwaysOnVpnLockdown);
+
+        pw.print("mCommonCriteriaMode=");
+        pw.println(mCommonCriteriaMode);
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
new file mode 100644
index 0000000..130cfd5
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.os.FileUtils;
+import android.os.PersistableBundle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+
+class DevicePolicyData {
+    private static final String TAG_ACCEPTED_CA_CERTIFICATES = "accepted-ca-certificate";
+    private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component";
+    private static final String TAG_LOCK_TASK_FEATURES = "lock-task-features";
+    private static final String TAG_STATUS_BAR = "statusbar";
+    private static final String TAG_APPS_SUSPENDED = "apps-suspended";
+    private static final String TAG_SECONDARY_LOCK_SCREEN = "secondary-lock-screen";
+    private static final String TAG_DO_NOT_ASK_CREDENTIALS_ON_BOOT =
+            "do-not-ask-credentials-on-boot";
+    private static final String TAG_AFFILIATION_ID = "affiliation-id";
+    private static final String TAG_LAST_SECURITY_LOG_RETRIEVAL = "last-security-log-retrieval";
+    private static final String TAG_LAST_BUG_REPORT_REQUEST = "last-bug-report-request";
+    private static final String TAG_LAST_NETWORK_LOG_RETRIEVAL = "last-network-log-retrieval";
+    private static final String TAG_ADMIN_BROADCAST_PENDING = "admin-broadcast-pending";
+    private static final String TAG_CURRENT_INPUT_METHOD_SET = "current-ime-set";
+    private static final String TAG_OWNER_INSTALLED_CA_CERT = "owner-installed-ca-cert";
+    private static final String TAG_INITIALIZATION_BUNDLE = "initialization-bundle";
+    private static final String TAG_PASSWORD_VALIDITY = "password-validity";
+    private static final String TAG_PASSWORD_TOKEN_HANDLE = "password-token";
+    private static final String TAG_PROTECTED_PACKAGES = "protected-packages";
+    private static final String ATTR_VALUE = "value";
+    private static final String ATTR_ALIAS = "alias";
+    private static final String ATTR_ID = "id";
+    private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_DISABLED = "disabled";
+    private static final String ATTR_SETUP_COMPLETE = "setup-complete";
+    private static final String ATTR_PROVISIONING_STATE = "provisioning-state";
+    private static final String ATTR_PERMISSION_POLICY = "permission-policy";
+    private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
+            "device-provisioning-config-applied";
+    private static final String ATTR_DEVICE_PAIRED = "device-paired";
+
+    int mFailedPasswordAttempts = 0;
+    boolean mPasswordValidAtLastCheckpoint = true;
+
+    int mUserHandle;
+    int mPasswordOwner = -1;
+    long mLastMaximumTimeToLock = -1;
+    boolean mUserSetupComplete = false;
+    boolean mPaired = false;
+    int mUserProvisioningState;
+    int mPermissionPolicy;
+
+    boolean mDeviceProvisioningConfigApplied = false;
+
+    final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
+    final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
+    final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
+
+    // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
+    final ArraySet<String> mAcceptedCaCertificates = new ArraySet<>();
+
+    // This is the list of component allowed to start lock task mode.
+    List<String> mLockTaskPackages = new ArrayList<>();
+
+    // List of packages protected by device owner
+    List<String> mUserControlDisabledPackages = new ArrayList<>();
+
+    // Bitfield of feature flags to be enabled during LockTask mode.
+    // We default on the power button menu, in order to be consistent with pre-P behaviour.
+    int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+
+    boolean mStatusBarDisabled = false;
+
+    ComponentName mRestrictionsProvider;
+
+    // Map of delegate package to delegation scopes
+    final ArrayMap<String, List<String>> mDelegationMap = new ArrayMap<>();
+
+    boolean mDoNotAskCredentialsOnBoot = false;
+
+    Set<String> mAffiliationIds = new ArraySet<>();
+
+    long mLastSecurityLogRetrievalTime = -1;
+
+    long mLastBugReportRequestTime = -1;
+
+    long mLastNetworkLogsRetrievalTime = -1;
+
+    boolean mCurrentInputMethodSet = false;
+
+    boolean mSecondaryLockscreenEnabled = false;
+
+    // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
+    Set<String> mOwnerInstalledCaCerts = new ArraySet<>();
+
+    // Used for initialization of users created by createAndManageUser.
+    boolean mAdminBroadcastPending = false;
+    PersistableBundle mInitBundle = null;
+
+    long mPasswordTokenHandle = 0;
+
+    // Whether user's apps are suspended. This flag should only be written AFTER all the needed
+    // apps were suspended or unsuspended.
+    boolean mAppsSuspended = false;
+
+    DevicePolicyData(int userHandle) {
+        mUserHandle = userHandle;
+    }
+
+    /**
+     * Serializes DevicePolicyData object as XML.
+     */
+    static boolean store(DevicePolicyData policyData, JournaledFile file, boolean isFdeDevice) {
+        FileOutputStream stream = null;
+        try {
+            stream = new FileOutputStream(file.chooseForWrite(), false);
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+
+            out.startTag(null, "policies");
+            if (policyData.mRestrictionsProvider != null) {
+                out.attribute(null, ATTR_PERMISSION_PROVIDER,
+                        policyData.mRestrictionsProvider.flattenToString());
+            }
+            if (policyData.mUserSetupComplete) {
+                out.attribute(null, ATTR_SETUP_COMPLETE,
+                        Boolean.toString(true));
+            }
+            if (policyData.mPaired) {
+                out.attribute(null, ATTR_DEVICE_PAIRED,
+                        Boolean.toString(true));
+            }
+            if (policyData.mDeviceProvisioningConfigApplied) {
+                out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED,
+                        Boolean.toString(true));
+            }
+            if (policyData.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
+                out.attribute(null, ATTR_PROVISIONING_STATE,
+                        Integer.toString(policyData.mUserProvisioningState));
+            }
+            if (policyData.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
+                out.attribute(null, ATTR_PERMISSION_POLICY,
+                        Integer.toString(policyData.mPermissionPolicy));
+            }
+
+            // Serialize delegations.
+            for (int i = 0; i < policyData.mDelegationMap.size(); ++i) {
+                final String delegatePackage = policyData.mDelegationMap.keyAt(i);
+                final List<String> scopes = policyData.mDelegationMap.valueAt(i);
+
+                // Every "delegation" tag serializes the information of one delegate-scope pair.
+                for (String scope : scopes) {
+                    out.startTag(null, "delegation");
+                    out.attribute(null, "delegatePackage", delegatePackage);
+                    out.attribute(null, "scope", scope);
+                    out.endTag(null, "delegation");
+                }
+            }
+
+            final int n = policyData.mAdminList.size();
+            for (int i = 0; i < n; i++) {
+                ActiveAdmin ap = policyData.mAdminList.get(i);
+                if (ap != null) {
+                    out.startTag(null, "admin");
+                    out.attribute(null, "name", ap.info.getComponent().flattenToString());
+                    ap.writeToXml(out);
+                    out.endTag(null, "admin");
+                }
+            }
+
+            if (policyData.mPasswordOwner >= 0) {
+                out.startTag(null, "password-owner");
+                out.attribute(null, "value", Integer.toString(policyData.mPasswordOwner));
+                out.endTag(null, "password-owner");
+            }
+
+            if (policyData.mFailedPasswordAttempts != 0) {
+                out.startTag(null, "failed-password-attempts");
+                out.attribute(null, "value", Integer.toString(policyData.mFailedPasswordAttempts));
+                out.endTag(null, "failed-password-attempts");
+            }
+
+            // For FDE devices only, we save this flag so we can report on password sufficiency
+            // before the user enters their password for the first time after a reboot.  For
+            // security reasons, we don't want to store the full set of active password metrics.
+            if (isFdeDevice) {
+                out.startTag(null, TAG_PASSWORD_VALIDITY);
+                out.attribute(null, ATTR_VALUE,
+                        Boolean.toString(policyData.mPasswordValidAtLastCheckpoint));
+                out.endTag(null, TAG_PASSWORD_VALIDITY);
+            }
+
+            for (int i = 0; i < policyData.mAcceptedCaCertificates.size(); i++) {
+                out.startTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
+                out.attribute(null, ATTR_NAME, policyData.mAcceptedCaCertificates.valueAt(i));
+                out.endTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
+            }
+
+            for (int i = 0; i < policyData.mLockTaskPackages.size(); i++) {
+                String component = policyData.mLockTaskPackages.get(i);
+                out.startTag(null, TAG_LOCK_TASK_COMPONENTS);
+                out.attribute(null, "name", component);
+                out.endTag(null, TAG_LOCK_TASK_COMPONENTS);
+            }
+
+            if (policyData.mLockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
+                out.startTag(null, TAG_LOCK_TASK_FEATURES);
+                out.attribute(null, ATTR_VALUE, Integer.toString(policyData.mLockTaskFeatures));
+                out.endTag(null, TAG_LOCK_TASK_FEATURES);
+            }
+
+            if (policyData.mSecondaryLockscreenEnabled) {
+                out.startTag(null, TAG_SECONDARY_LOCK_SCREEN);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(true));
+                out.endTag(null, TAG_SECONDARY_LOCK_SCREEN);
+            }
+
+            if (policyData.mStatusBarDisabled) {
+                out.startTag(null, TAG_STATUS_BAR);
+                out.attribute(null, ATTR_DISABLED, Boolean.toString(policyData.mStatusBarDisabled));
+                out.endTag(null, TAG_STATUS_BAR);
+            }
+
+            if (policyData.mDoNotAskCredentialsOnBoot) {
+                out.startTag(null, TAG_DO_NOT_ASK_CREDENTIALS_ON_BOOT);
+                out.endTag(null, TAG_DO_NOT_ASK_CREDENTIALS_ON_BOOT);
+            }
+
+            for (String id : policyData.mAffiliationIds) {
+                out.startTag(null, TAG_AFFILIATION_ID);
+                out.attribute(null, ATTR_ID, id);
+                out.endTag(null, TAG_AFFILIATION_ID);
+            }
+
+            if (policyData.mLastSecurityLogRetrievalTime >= 0) {
+                out.startTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL);
+                out.attribute(null, ATTR_VALUE,
+                        Long.toString(policyData.mLastSecurityLogRetrievalTime));
+                out.endTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL);
+            }
+
+            if (policyData.mLastBugReportRequestTime >= 0) {
+                out.startTag(null, TAG_LAST_BUG_REPORT_REQUEST);
+                out.attribute(null, ATTR_VALUE,
+                        Long.toString(policyData.mLastBugReportRequestTime));
+                out.endTag(null, TAG_LAST_BUG_REPORT_REQUEST);
+            }
+
+            if (policyData.mLastNetworkLogsRetrievalTime >= 0) {
+                out.startTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL);
+                out.attribute(null, ATTR_VALUE,
+                        Long.toString(policyData.mLastNetworkLogsRetrievalTime));
+                out.endTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL);
+            }
+
+            if (policyData.mAdminBroadcastPending) {
+                out.startTag(null, TAG_ADMIN_BROADCAST_PENDING);
+                out.attribute(null, ATTR_VALUE,
+                        Boolean.toString(policyData.mAdminBroadcastPending));
+                out.endTag(null, TAG_ADMIN_BROADCAST_PENDING);
+            }
+
+            if (policyData.mInitBundle != null) {
+                out.startTag(null, TAG_INITIALIZATION_BUNDLE);
+                policyData.mInitBundle.saveToXml(out);
+                out.endTag(null, TAG_INITIALIZATION_BUNDLE);
+            }
+
+            if (policyData.mPasswordTokenHandle != 0) {
+                out.startTag(null, TAG_PASSWORD_TOKEN_HANDLE);
+                out.attribute(null, ATTR_VALUE,
+                        Long.toString(policyData.mPasswordTokenHandle));
+                out.endTag(null, TAG_PASSWORD_TOKEN_HANDLE);
+            }
+
+            if (policyData.mCurrentInputMethodSet) {
+                out.startTag(null, TAG_CURRENT_INPUT_METHOD_SET);
+                out.endTag(null, TAG_CURRENT_INPUT_METHOD_SET);
+            }
+
+            for (final String cert : policyData.mOwnerInstalledCaCerts) {
+                out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT);
+                out.attribute(null, ATTR_ALIAS, cert);
+                out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
+            }
+
+            for (int i = 0, size = policyData.mUserControlDisabledPackages.size(); i < size; i++) {
+                String packageName = policyData.mUserControlDisabledPackages.get(i);
+                out.startTag(null, TAG_PROTECTED_PACKAGES);
+                out.attribute(null, ATTR_NAME, packageName);
+                out.endTag(null, TAG_PROTECTED_PACKAGES);
+            }
+
+            if (policyData.mAppsSuspended) {
+                out.startTag(null, TAG_APPS_SUSPENDED);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(policyData.mAppsSuspended));
+                out.endTag(null, TAG_APPS_SUSPENDED);
+            }
+
+            out.endTag(null, "policies");
+
+            out.endDocument();
+            stream.flush();
+            FileUtils.sync(stream);
+            stream.close();
+            file.commit();
+            return true;
+        } catch (XmlPullParserException | IOException e) {
+            Slog.w(DevicePolicyManagerService.LOG_TAG, "failed writing file", e);
+            try {
+                if (stream != null) {
+                    stream.close();
+                }
+            } catch (IOException ex) {
+                // Ignore
+            }
+            file.rollback();
+            return false;
+        }
+    }
+
+    /**
+     * @param adminInfoSupplier function that queries DeviceAdminInfo from PackageManager
+     * @param ownerComponent device or profile owner component if any.
+     */
+    static boolean load(DevicePolicyData policy, boolean isFdeDevice, JournaledFile journaledFile,
+            Function<ComponentName, DeviceAdminInfo> adminInfoSupplier,
+            ComponentName ownerComponent) {
+        FileInputStream stream = null;
+        File file = journaledFile.chooseForRead();
+        boolean needsRewrite = false;
+        try {
+            stream = new FileInputStream(file);
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+            String tag = parser.getName();
+            if (!"policies".equals(tag)) {
+                throw new XmlPullParserException(
+                        "Settings do not start with policies tag: found " + tag);
+            }
+
+            // Extract the permission provider component name if available
+            String permissionProvider = parser.getAttributeValue(null, ATTR_PERMISSION_PROVIDER);
+            if (permissionProvider != null) {
+                policy.mRestrictionsProvider =
+                        ComponentName.unflattenFromString(permissionProvider);
+            }
+            String userSetupComplete = parser.getAttributeValue(null, ATTR_SETUP_COMPLETE);
+            if (Boolean.toString(true).equals(userSetupComplete)) {
+                policy.mUserSetupComplete = true;
+            }
+            String paired = parser.getAttributeValue(null, ATTR_DEVICE_PAIRED);
+            if (Boolean.toString(true).equals(paired)) {
+                policy.mPaired = true;
+            }
+            String deviceProvisioningConfigApplied = parser.getAttributeValue(null,
+                    ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED);
+            if (Boolean.toString(true).equals(deviceProvisioningConfigApplied)) {
+                policy.mDeviceProvisioningConfigApplied = true;
+            }
+            String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
+            if (!TextUtils.isEmpty(provisioningState)) {
+                policy.mUserProvisioningState = Integer.parseInt(provisioningState);
+            }
+            String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
+            if (!TextUtils.isEmpty(permissionPolicy)) {
+                policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
+            }
+
+            parser.next();
+            int outerDepth = parser.getDepth();
+            policy.mLockTaskPackages.clear();
+            policy.mAdminList.clear();
+            policy.mAdminMap.clear();
+            policy.mAffiliationIds.clear();
+            policy.mOwnerInstalledCaCerts.clear();
+            policy.mUserControlDisabledPackages.clear();
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                   && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+                tag = parser.getName();
+                if ("admin".equals(tag)) {
+                    String name = parser.getAttributeValue(null, "name");
+                    try {
+                        DeviceAdminInfo dai = adminInfoSupplier.apply(
+                                ComponentName.unflattenFromString(name));
+
+                        if (dai != null) {
+                            // b/123415062: If DA, overwrite with the stored policies that were
+                            // agreed by the user to prevent apps from sneaking additional policies
+                            // into updates.
+                            boolean overwritePolicies = !dai.getComponent().equals(ownerComponent);
+                            ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
+                            ap.readFromXml(parser, overwritePolicies);
+                            policy.mAdminMap.put(ap.info.getComponent(), ap);
+                        }
+                    } catch (RuntimeException e) {
+                        Slog.w(DevicePolicyManagerService.LOG_TAG,
+                                "Failed loading admin " + name, e);
+                    }
+                } else if ("delegation".equals(tag)) {
+                    // Parse delegation info.
+                    final String delegatePackage = parser.getAttributeValue(null,
+                            "delegatePackage");
+                    final String scope = parser.getAttributeValue(null, "scope");
+
+                    // Get a reference to the scopes list for the delegatePackage.
+                    List<String> scopes = policy.mDelegationMap.get(delegatePackage);
+                    // Or make a new list if none was found.
+                    if (scopes == null) {
+                        scopes = new ArrayList<>();
+                        policy.mDelegationMap.put(delegatePackage, scopes);
+                    }
+                    // Add the new scope to the list of delegatePackage if it's not already there.
+                    if (!scopes.contains(scope)) {
+                        scopes.add(scope);
+                    }
+                } else if ("failed-password-attempts".equals(tag)) {
+                    policy.mFailedPasswordAttempts = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("password-owner".equals(tag)) {
+                    policy.mPasswordOwner = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
+                    policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
+                } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
+                    policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
+                } else if (TAG_LOCK_TASK_FEATURES.equals(tag)) {
+                    policy.mLockTaskFeatures = Integer.parseInt(
+                            parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_SECONDARY_LOCK_SCREEN.equals(tag)) {
+                    policy.mSecondaryLockscreenEnabled = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_STATUS_BAR.equals(tag)) {
+                    policy.mStatusBarDisabled = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_DISABLED));
+                } else if (TAG_DO_NOT_ASK_CREDENTIALS_ON_BOOT.equals(tag)) {
+                    policy.mDoNotAskCredentialsOnBoot = true;
+                } else if (TAG_AFFILIATION_ID.equals(tag)) {
+                    policy.mAffiliationIds.add(parser.getAttributeValue(null, ATTR_ID));
+                } else if (TAG_LAST_SECURITY_LOG_RETRIEVAL.equals(tag)) {
+                    policy.mLastSecurityLogRetrievalTime = Long.parseLong(
+                            parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_LAST_BUG_REPORT_REQUEST.equals(tag)) {
+                    policy.mLastBugReportRequestTime = Long.parseLong(
+                            parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_LAST_NETWORK_LOG_RETRIEVAL.equals(tag)) {
+                    policy.mLastNetworkLogsRetrievalTime = Long.parseLong(
+                            parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_ADMIN_BROADCAST_PENDING.equals(tag)) {
+                    String pending = parser.getAttributeValue(null, ATTR_VALUE);
+                    policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
+                } else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) {
+                    policy.mInitBundle = PersistableBundle.restoreFromXml(parser);
+                } else if ("active-password".equals(tag)) {
+                    // Remove password metrics from saved settings, as we no longer wish to store
+                    // these on disk
+                    needsRewrite = true;
+                } else if (TAG_PASSWORD_VALIDITY.equals(tag)) {
+                    if (isFdeDevice) {
+                        // This flag is only used for FDE devices
+                        policy.mPasswordValidAtLastCheckpoint = Boolean.parseBoolean(
+                                parser.getAttributeValue(null, ATTR_VALUE));
+                    }
+                } else if (TAG_PASSWORD_TOKEN_HANDLE.equals(tag)) {
+                    policy.mPasswordTokenHandle = Long.parseLong(
+                            parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_CURRENT_INPUT_METHOD_SET.equals(tag)) {
+                    policy.mCurrentInputMethodSet = true;
+                } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
+                    policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
+                } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
+                    policy.mUserControlDisabledPackages.add(
+                            parser.getAttributeValue(null, ATTR_NAME));
+                } else if (TAG_APPS_SUSPENDED.equals(tag)) {
+                    policy.mAppsSuspended =
+                            Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
+                } else {
+                    Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown tag: " + tag);
+                    XmlUtils.skipCurrentTag(parser);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Don't be noisy, this is normal if we haven't defined any policies.
+        } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
+                | IndexOutOfBoundsException e) {
+            Slog.w(DevicePolicyManagerService.LOG_TAG, "failed parsing " + file, e);
+        }
+        try {
+            if (stream != null) {
+                stream.close();
+            }
+        } catch (IOException e) {
+            // Ignore
+        }
+
+        // Generate a list of admins from the admin map
+        policy.mAdminList.addAll(policy.mAdminMap.values());
+        return needsRewrite;
+    }
+
+    void validatePasswordOwner() {
+        if (mPasswordOwner >= 0) {
+            boolean haveOwner = false;
+            for (int i = mAdminList.size() - 1; i >= 0; i--) {
+                if (mAdminList.get(i).getUid() == mPasswordOwner) {
+                    haveOwner = true;
+                    break;
+                }
+            }
+            if (!haveOwner) {
+                Slog.w(DevicePolicyManagerService.LOG_TAG, "Previous password owner "
+                        + mPasswordOwner + " no longer active; disabling");
+                mPasswordOwner = -1;
+            }
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9a2bef8..cafd56e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -106,10 +106,6 @@
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.TEXT;
-
 import android.Manifest.permission;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accounts.Account;
@@ -189,7 +185,6 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.location.LocationManager;
 import android.media.AudioManager;
 import android.media.IAudioService;
@@ -204,7 +199,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -248,7 +242,6 @@
 import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -280,7 +273,6 @@
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.StatLogger;
-import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.internal.widget.LockscreenCredential;
@@ -290,8 +282,7 @@
 import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
-import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.RestrictionsSet;
@@ -328,7 +319,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -350,55 +340,8 @@
     private static final String TRANSFER_OWNERSHIP_PARAMETERS_XML =
             "transfer-ownership-parameters.xml";
 
-    private static final String TAG_ACCEPTED_CA_CERTIFICATES = "accepted-ca-certificate";
-
-    private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component";
-
-    private static final String TAG_LOCK_TASK_FEATURES = "lock-task-features";
-
-    private static final String TAG_STATUS_BAR = "statusbar";
-
-    private static final String ATTR_DISABLED = "disabled";
-
-    private static final String ATTR_NAME = "name";
-
-    private static final String DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML =
-            "do-not-ask-credentials-on-boot";
-
-    private static final String TAG_AFFILIATION_ID = "affiliation-id";
-
-    private static final String TAG_LAST_SECURITY_LOG_RETRIEVAL = "last-security-log-retrieval";
-
-    private static final String TAG_LAST_BUG_REPORT_REQUEST = "last-bug-report-request";
-
-    private static final String TAG_LAST_NETWORK_LOG_RETRIEVAL = "last-network-log-retrieval";
-
-    private static final String TAG_ADMIN_BROADCAST_PENDING = "admin-broadcast-pending";
-
-    private static final String TAG_CURRENT_INPUT_METHOD_SET = "current-ime-set";
-
-    private static final String TAG_OWNER_INSTALLED_CA_CERT = "owner-installed-ca-cert";
-
-    private static final String ATTR_ID = "id";
-
-    private static final String ATTR_VALUE = "value";
-
-    private static final String ATTR_ALIAS = "alias";
-
-    private static final String TAG_INITIALIZATION_BUNDLE = "initialization-bundle";
-
-    private static final String TAG_PASSWORD_TOKEN_HANDLE = "password-token";
-
-    private static final String TAG_PASSWORD_VALIDITY = "password-validity";
-
     private static final String TAG_TRANSFER_OWNERSHIP_BUNDLE = "transfer-ownership-bundle";
 
-    private static final String TAG_PROTECTED_PACKAGES = "protected-packages";
-
-    private static final String TAG_SECONDARY_LOCK_SCREEN = "secondary-lock-screen";
-
-    private static final String TAG_APPS_SUSPENDED = "apps-suspended";
-
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final int REQUEST_PROFILE_OFF_DEADLINE = 5572;
@@ -423,17 +366,6 @@
     static final String ACTION_PROFILE_OFF_DEADLINE =
             "com.android.server.ACTION_PROFILE_OFF_DEADLINE";
 
-    private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
-    private static final String ATTR_SETUP_COMPLETE = "setup-complete";
-    private static final String ATTR_PROVISIONING_STATE = "provisioning-state";
-    private static final String ATTR_PERMISSION_POLICY = "permission-policy";
-    private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
-            "device-provisioning-config-applied";
-    private static final String ATTR_DEVICE_PAIRED = "device-paired";
-    private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
-    private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
-            = "application-restrictions-manager";
-
     private static final String CALLED_FROM_PARENT = "calledFromParent";
     private static final String NOT_CALLED_FROM_PARENT = "notCalledFromParent";
 
@@ -488,7 +420,6 @@
     private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
     private static final Set<Integer> DA_DISALLOWED_POLICIES;
     // A collection of user restrictions that are deprecated and should simply be ignored.
-    private static final Set<String> DEPRECATED_USER_RESTRICTIONS;
     private static final String AB_DEVICE_KEY = "ro.build.ab_update";
 
     static {
@@ -532,10 +463,6 @@
         DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
         DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
         DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
-
-        DEPRECATED_USER_RESTRICTIONS = Sets.newHashSet(
-                UserManager.DISALLOW_ADD_MANAGED_PROFILE,
-                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
     }
 
     /**
@@ -791,76 +718,6 @@
         }
     }
 
-    public static class DevicePolicyData {
-        int mFailedPasswordAttempts = 0;
-        boolean mPasswordValidAtLastCheckpoint = true;
-
-        int mUserHandle;
-        int mPasswordOwner = -1;
-        long mLastMaximumTimeToLock = -1;
-        boolean mUserSetupComplete = false;
-        boolean mPaired = false;
-        int mUserProvisioningState;
-        int mPermissionPolicy;
-
-        boolean mDeviceProvisioningConfigApplied = false;
-
-        final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
-        final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
-        final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
-
-        // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
-        final ArraySet<String> mAcceptedCaCertificates = new ArraySet<>();
-
-        // This is the list of component allowed to start lock task mode.
-        List<String> mLockTaskPackages = new ArrayList<>();
-
-        // List of packages protected by device owner
-        List<String> mUserControlDisabledPackages = new ArrayList<>();
-
-        // Bitfield of feature flags to be enabled during LockTask mode.
-        // We default on the power button menu, in order to be consistent with pre-P behaviour.
-        int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
-
-        boolean mStatusBarDisabled = false;
-
-        ComponentName mRestrictionsProvider;
-
-        // Map of delegate package to delegation scopes
-        final ArrayMap<String, List<String>> mDelegationMap = new ArrayMap<>();
-
-        boolean doNotAskCredentialsOnBoot = false;
-
-        Set<String> mAffiliationIds = new ArraySet<>();
-
-        long mLastSecurityLogRetrievalTime = -1;
-
-        long mLastBugReportRequestTime = -1;
-
-        long mLastNetworkLogsRetrievalTime = -1;
-
-        boolean mCurrentInputMethodSet = false;
-
-        boolean mSecondaryLockscreenEnabled = false;
-
-        // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
-        Set<String> mOwnerInstalledCaCerts = new ArraySet<>();
-
-        // Used for initialization of users created by createAndManageUser.
-        boolean mAdminBroadcastPending = false;
-        PersistableBundle mInitBundle = null;
-
-        long mPasswordTokenHandle = 0;
-
-        // Whether user's apps are suspended. This flag should only be written AFTER all the needed
-        // apps were suspended or unsuspended.
-        boolean mAppsSuspended = false;
-
-        public DevicePolicyData(int userHandle) {
-            mUserHandle = userHandle;
-        }
-    }
-
     @GuardedBy("getLockObject()")
     final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
 
@@ -1045,1002 +902,6 @@
         }
     }
 
-    static class ActiveAdmin {
-        private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
-        private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
-        private static final String TAG_DISABLE_CAMERA = "disable-camera";
-        private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
-        private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
-        private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING
-                = "disable-bt-contacts-sharing";
-        private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
-        private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
-        private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time";
-        private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";
-        private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled";
-        private static final String TAG_ACCOUNT_TYPE = "account-type";
-        private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES
-                = "permitted-accessiblity-services";
-        private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
-        private static final String TAG_MANAGE_TRUST_AGENT_FEATURES = "manage-trust-agent-features";
-        private static final String TAG_TRUST_AGENT_COMPONENT_OPTIONS = "trust-agent-component-options";
-        private static final String TAG_TRUST_AGENT_COMPONENT = "component";
-        private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
-        private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
-        private static final String TAG_GLOBAL_PROXY_EXCLUSION_LIST = "global-proxy-exclusion-list";
-        private static final String TAG_GLOBAL_PROXY_SPEC = "global-proxy-spec";
-        private static final String TAG_SPECIFIES_GLOBAL_PROXY = "specifies-global-proxy";
-        private static final String TAG_PERMITTED_IMES = "permitted-imes";
-        private static final String TAG_PERMITTED_NOTIFICATION_LISTENERS =
-                "permitted-notification-listeners";
-        private static final String TAG_MAX_FAILED_PASSWORD_WIPE = "max-failed-password-wipe";
-        private static final String TAG_MAX_TIME_TO_UNLOCK = "max-time-to-unlock";
-        private static final String TAG_STRONG_AUTH_UNLOCK_TIMEOUT = "strong-auth-unlock-timeout";
-        private static final String TAG_MIN_PASSWORD_NONLETTER = "min-password-nonletter";
-        private static final String TAG_MIN_PASSWORD_SYMBOLS = "min-password-symbols";
-        private static final String TAG_MIN_PASSWORD_NUMERIC = "min-password-numeric";
-        private static final String TAG_MIN_PASSWORD_LETTERS = "min-password-letters";
-        private static final String TAG_MIN_PASSWORD_LOWERCASE = "min-password-lowercase";
-        private static final String TAG_MIN_PASSWORD_UPPERCASE = "min-password-uppercase";
-        private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length";
-        private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length";
-        private static final String ATTR_VALUE = "value";
-        private static final String TAG_PASSWORD_QUALITY = "password-quality";
-        private static final String TAG_POLICIES = "policies";
-        private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
-                "cross-profile-widget-providers";
-        private static final String TAG_PROVIDER = "provider";
-        private static final String TAG_PACKAGE_LIST_ITEM  = "item";
-        private static final String TAG_KEEP_UNINSTALLED_PACKAGES  = "keep-uninstalled-packages";
-        private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
-        private static final String TAG_DEFAULT_ENABLED_USER_RESTRICTIONS =
-                "default-enabled-user-restrictions";
-        private static final String TAG_RESTRICTION = "restriction";
-        private static final String TAG_SHORT_SUPPORT_MESSAGE = "short-support-message";
-        private static final String TAG_LONG_SUPPORT_MESSAGE = "long-support-message";
-        private static final String TAG_PARENT_ADMIN = "parent-admin";
-        private static final String TAG_ORGANIZATION_COLOR = "organization-color";
-        private static final String TAG_ORGANIZATION_NAME = "organization-name";
-        private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
-        private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
-        private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
-        private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
-        private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
-        private static final String TAG_METERED_DATA_DISABLED_PACKAGES =
-                "metered_data_disabled_packages";
-        private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
-                "cross-profile-calendar-packages";
-        private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL =
-                "cross-profile-calendar-packages-null";
-        private static final String TAG_CROSS_PROFILE_PACKAGES = "cross-profile-packages";
-        private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
-                "factory_reset_protection_policy";
-        private static final String TAG_SUSPEND_PERSONAL_APPS = "suspend-personal-apps";
-        private static final String TAG_PROFILE_MAXIMUM_TIME_OFF = "profile-max-time-off";
-        private static final String TAG_PROFILE_OFF_DEADLINE = "profile-off-deadline";
-        private static final String TAG_ALWAYS_ON_VPN_PACKAGE = "vpn-package";
-        private static final String TAG_ALWAYS_ON_VPN_LOCKDOWN = "vpn-lockdown";
-        private static final String TAG_COMMON_CRITERIA_MODE = "common-criteria-mode";
-        DeviceAdminInfo info;
-
-
-        static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
-        int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH;
-
-        @NonNull
-        PasswordPolicy mPasswordPolicy = new PasswordPolicy();
-
-        @Nullable
-        FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null;
-
-        static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
-        long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
-
-        long strongAuthUnlockTimeout = 0; // admin doesn't participate by default
-
-        static final int DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE = 0;
-        int maximumFailedPasswordsForWipe = DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE;
-
-        static final long DEF_PASSWORD_EXPIRATION_TIMEOUT = 0;
-        long passwordExpirationTimeout = DEF_PASSWORD_EXPIRATION_TIMEOUT;
-
-        static final long DEF_PASSWORD_EXPIRATION_DATE = 0;
-        long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
-
-        static final int DEF_KEYGUARD_FEATURES_DISABLED = 0; // none
-
-        int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED;
-
-        boolean encryptionRequested = false;
-        boolean testOnlyAdmin = false;
-        boolean disableCamera = false;
-        boolean disableCallerId = false;
-        boolean disableContactsSearch = false;
-        boolean disableBluetoothContactSharing = true;
-        boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
-        boolean requireAutoTime = false; // Can only be set by a device owner.
-        boolean forceEphemeralUsers = false; // Can only be set by a device owner.
-        boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
-        boolean isLogoutEnabled = false; // Can only be set by a device owner.
-
-        // one notification after enabling + one more after reboots
-        static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
-        int numNetworkLoggingNotifications = 0;
-        long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch
-
-        ActiveAdmin parentAdmin;
-        final boolean isParent;
-
-        static class TrustAgentInfo {
-            public PersistableBundle options;
-            TrustAgentInfo(PersistableBundle bundle) {
-                options = bundle;
-            }
-        }
-
-        // The list of packages which are not allowed to use metered data.
-        List<String> meteredDisabledPackages;
-
-        final Set<String> accountTypesWithManagementDisabled = new ArraySet<>();
-
-        // The list of permitted accessibility services package namesas set by a profile
-        // or device owner. Null means all accessibility services are allowed, empty means
-        // none except system services are allowed.
-        List<String> permittedAccessiblityServices;
-
-        // The list of permitted input methods package names as set by a profile or device owner.
-        // Null means all input methods are allowed, empty means none except system imes are
-        // allowed.
-        List<String> permittedInputMethods;
-
-        // The list of packages allowed to use a NotificationListenerService to receive events for
-        // notifications from this user. Null means that all packages are allowed. Empty list means
-        // that only packages from the system are allowed.
-        List<String> permittedNotificationListeners;
-
-        // List of package names to keep cached.
-        List<String> keepUninstalledPackages;
-
-        // TODO: review implementation decisions with frameworks team
-        boolean specifiesGlobalProxy = false;
-        String globalProxySpec = null;
-        String globalProxyExclusionList = null;
-
-        @NonNull ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>();
-
-        List<String> crossProfileWidgetProviders;
-
-        Bundle userRestrictions;
-
-        // User restrictions that have already been enabled by default for this admin (either when
-        // setting the device or profile owner, or during a system update if one of those "enabled
-        // by default" restrictions is newly added).
-        final Set<String> defaultEnabledRestrictionsAlreadySet = new ArraySet<>();
-
-        // Support text provided by the admin to display to the user.
-        CharSequence shortSupportMessage = null;
-        CharSequence longSupportMessage = null;
-
-        // Background color of confirm credentials screen. Default: teal.
-        static final int DEF_ORGANIZATION_COLOR = Color.parseColor("#00796B");
-        int organizationColor = DEF_ORGANIZATION_COLOR;
-
-        // Default title of confirm credentials screen
-        String organizationName = null;
-
-        // Message for user switcher
-        String startUserSessionMessage = null;
-        String endUserSessionMessage = null;
-
-        // The whitelist of packages that can access cross profile calendar APIs.
-        // This whitelist should be in default an empty list, which indicates that no package
-        // is whitelisted.
-        List<String> mCrossProfileCalendarPackages = Collections.emptyList();
-
-        // The whitelist of packages that the admin has enabled to be able to request consent from
-        // the user to communicate cross-profile. By default, no packages are whitelisted, which is
-        // represented as an empty list.
-        List<String> mCrossProfilePackages = Collections.emptyList();
-
-        // Whether the admin explicitly requires personal apps to be suspended
-        boolean mSuspendPersonalApps = false;
-        // Maximum time the profile owned by this admin can be off.
-        long mProfileMaximumTimeOffMillis = 0;
-        // Time by which the profile should be turned on according to System.currentTimeMillis().
-        long mProfileOffDeadline = 0;
-
-        public String mAlwaysOnVpnPackage;
-        public boolean mAlwaysOnVpnLockdown;
-        boolean mCommonCriteriaMode;
-
-        ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
-            info = _info;
-            isParent = parent;
-        }
-
-        ActiveAdmin getParentActiveAdmin() {
-            Preconditions.checkState(!isParent);
-
-            if (parentAdmin == null) {
-                parentAdmin = new ActiveAdmin(info, /* parent */ true);
-            }
-            return parentAdmin;
-        }
-
-        boolean hasParentActiveAdmin() {
-            return parentAdmin != null;
-        }
-
-        int getUid() { return info.getActivityInfo().applicationInfo.uid; }
-
-        public UserHandle getUserHandle() {
-            return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid));
-        }
-
-        void writeToXml(XmlSerializer out)
-                throws IllegalArgumentException, IllegalStateException, IOException {
-            out.startTag(null, TAG_POLICIES);
-            info.writePoliciesToXml(out);
-            out.endTag(null, TAG_POLICIES);
-            if (mPasswordPolicy.quality != PASSWORD_QUALITY_UNSPECIFIED) {
-                writeAttributeValueToXml(
-                        out, TAG_PASSWORD_QUALITY, mPasswordPolicy.quality);
-                if (mPasswordPolicy.length != PasswordPolicy.DEF_MINIMUM_LENGTH) {
-                    writeAttributeValueToXml(
-                            out, TAG_MIN_PASSWORD_LENGTH, mPasswordPolicy.length);
-                }
-                if (mPasswordPolicy.upperCase != PasswordPolicy.DEF_MINIMUM_UPPER_CASE) {
-                    writeAttributeValueToXml(
-                            out, TAG_MIN_PASSWORD_UPPERCASE, mPasswordPolicy.upperCase);
-                }
-                if (mPasswordPolicy.lowerCase != PasswordPolicy.DEF_MINIMUM_LOWER_CASE) {
-                    writeAttributeValueToXml(
-                            out, TAG_MIN_PASSWORD_LOWERCASE, mPasswordPolicy.lowerCase);
-                }
-                if (mPasswordPolicy.letters != PasswordPolicy.DEF_MINIMUM_LETTERS) {
-                    writeAttributeValueToXml(
-                            out, TAG_MIN_PASSWORD_LETTERS, mPasswordPolicy.letters);
-                }
-                if (mPasswordPolicy.numeric != PasswordPolicy.DEF_MINIMUM_NUMERIC) {
-                    writeAttributeValueToXml(
-                            out, TAG_MIN_PASSWORD_NUMERIC, mPasswordPolicy.numeric);
-                }
-                if (mPasswordPolicy.symbols != PasswordPolicy.DEF_MINIMUM_SYMBOLS) {
-                    writeAttributeValueToXml(
-                            out, TAG_MIN_PASSWORD_SYMBOLS, mPasswordPolicy.symbols);
-                }
-                if (mPasswordPolicy.nonLetter > PasswordPolicy.DEF_MINIMUM_NON_LETTER) {
-                    writeAttributeValueToXml(
-                            out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter);
-                }
-            }
-            if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
-                writeAttributeValueToXml(
-                        out, TAG_PASSWORD_HISTORY_LENGTH, passwordHistoryLength);
-            }
-            if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) {
-                writeAttributeValueToXml(
-                        out, TAG_MAX_TIME_TO_UNLOCK, maximumTimeToUnlock);
-            }
-            if (strongAuthUnlockTimeout != DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
-                writeAttributeValueToXml(
-                        out, TAG_STRONG_AUTH_UNLOCK_TIMEOUT, strongAuthUnlockTimeout);
-            }
-            if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
-                writeAttributeValueToXml(
-                        out, TAG_MAX_FAILED_PASSWORD_WIPE, maximumFailedPasswordsForWipe);
-            }
-            if (specifiesGlobalProxy) {
-                writeAttributeValueToXml(
-                        out, TAG_SPECIFIES_GLOBAL_PROXY, specifiesGlobalProxy);
-                if (globalProxySpec != null) {
-                    writeAttributeValueToXml(out, TAG_GLOBAL_PROXY_SPEC, globalProxySpec);
-                }
-                if (globalProxyExclusionList != null) {
-                    writeAttributeValueToXml(
-                            out, TAG_GLOBAL_PROXY_EXCLUSION_LIST, globalProxyExclusionList);
-                }
-            }
-            if (passwordExpirationTimeout != DEF_PASSWORD_EXPIRATION_TIMEOUT) {
-                writeAttributeValueToXml(
-                        out, TAG_PASSWORD_EXPIRATION_TIMEOUT, passwordExpirationTimeout);
-            }
-            if (passwordExpirationDate != DEF_PASSWORD_EXPIRATION_DATE) {
-                writeAttributeValueToXml(
-                        out, TAG_PASSWORD_EXPIRATION_DATE, passwordExpirationDate);
-            }
-            if (encryptionRequested) {
-                writeAttributeValueToXml(
-                        out, TAG_ENCRYPTION_REQUESTED, encryptionRequested);
-            }
-            if (testOnlyAdmin) {
-                writeAttributeValueToXml(
-                        out, TAG_TEST_ONLY_ADMIN, testOnlyAdmin);
-            }
-            if (disableCamera) {
-                writeAttributeValueToXml(
-                        out, TAG_DISABLE_CAMERA, disableCamera);
-            }
-            if (disableCallerId) {
-                writeAttributeValueToXml(
-                        out, TAG_DISABLE_CALLER_ID, disableCallerId);
-            }
-            if (disableContactsSearch) {
-                writeAttributeValueToXml(
-                        out, TAG_DISABLE_CONTACTS_SEARCH, disableContactsSearch);
-            }
-            if (!disableBluetoothContactSharing) {
-                writeAttributeValueToXml(
-                        out, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING, disableBluetoothContactSharing);
-            }
-            if (disableScreenCapture) {
-                writeAttributeValueToXml(
-                        out, TAG_DISABLE_SCREEN_CAPTURE, disableScreenCapture);
-            }
-            if (requireAutoTime) {
-                writeAttributeValueToXml(
-                        out, TAG_REQUIRE_AUTO_TIME, requireAutoTime);
-            }
-            if (forceEphemeralUsers) {
-                writeAttributeValueToXml(
-                        out, TAG_FORCE_EPHEMERAL_USERS, forceEphemeralUsers);
-            }
-            if (isNetworkLoggingEnabled) {
-                out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
-                out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled));
-                out.attribute(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS,
-                        Integer.toString(numNetworkLoggingNotifications));
-                out.attribute(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION,
-                        Long.toString(lastNetworkLoggingNotificationTimeMs));
-                out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
-            }
-            if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
-                writeAttributeValueToXml(
-                        out, TAG_DISABLE_KEYGUARD_FEATURES, disabledKeyguardFeatures);
-            }
-            if (!accountTypesWithManagementDisabled.isEmpty()) {
-                writeAttributeValuesToXml(
-                        out, TAG_DISABLE_ACCOUNT_MANAGEMENT, TAG_ACCOUNT_TYPE,
-                        accountTypesWithManagementDisabled);
-            }
-            if (!trustAgentInfos.isEmpty()) {
-                Set<Entry<String, TrustAgentInfo>> set = trustAgentInfos.entrySet();
-                out.startTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
-                for (Entry<String, TrustAgentInfo> entry : set) {
-                    TrustAgentInfo trustAgentInfo = entry.getValue();
-                    out.startTag(null, TAG_TRUST_AGENT_COMPONENT);
-                    out.attribute(null, ATTR_VALUE, entry.getKey());
-                    if (trustAgentInfo.options != null) {
-                        out.startTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
-                        try {
-                            trustAgentInfo.options.saveToXml(out);
-                        } catch (XmlPullParserException e) {
-                            Log.e(LOG_TAG, "Failed to save TrustAgent options", e);
-                        }
-                        out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
-                    }
-                    out.endTag(null, TAG_TRUST_AGENT_COMPONENT);
-                }
-                out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
-            }
-            if (crossProfileWidgetProviders != null && !crossProfileWidgetProviders.isEmpty()) {
-                writeAttributeValuesToXml(
-                        out, TAG_CROSS_PROFILE_WIDGET_PROVIDERS, TAG_PROVIDER,
-                        crossProfileWidgetProviders);
-            }
-            writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
-                    permittedAccessiblityServices);
-            writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods);
-            writePackageListToXml(out, TAG_PERMITTED_NOTIFICATION_LISTENERS,
-                    permittedNotificationListeners);
-            writePackageListToXml(out, TAG_KEEP_UNINSTALLED_PACKAGES, keepUninstalledPackages);
-            writePackageListToXml(out, TAG_METERED_DATA_DISABLED_PACKAGES, meteredDisabledPackages);
-            if (hasUserRestrictions()) {
-                UserRestrictionsUtils.writeRestrictions(
-                        out, userRestrictions, TAG_USER_RESTRICTIONS);
-            }
-            if (!defaultEnabledRestrictionsAlreadySet.isEmpty()) {
-                writeAttributeValuesToXml(out, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS,
-                        TAG_RESTRICTION,
-                        defaultEnabledRestrictionsAlreadySet);
-            }
-            if (!TextUtils.isEmpty(shortSupportMessage)) {
-                writeTextToXml(out, TAG_SHORT_SUPPORT_MESSAGE, shortSupportMessage.toString());
-            }
-            if (!TextUtils.isEmpty(longSupportMessage)) {
-                writeTextToXml(out, TAG_LONG_SUPPORT_MESSAGE, longSupportMessage.toString());
-            }
-            if (parentAdmin != null) {
-                out.startTag(null, TAG_PARENT_ADMIN);
-                parentAdmin.writeToXml(out);
-                out.endTag(null, TAG_PARENT_ADMIN);
-            }
-            if (organizationColor != DEF_ORGANIZATION_COLOR) {
-                writeAttributeValueToXml(out, TAG_ORGANIZATION_COLOR, organizationColor);
-            }
-            if (organizationName != null) {
-                writeTextToXml(out, TAG_ORGANIZATION_NAME, organizationName);
-            }
-            if (isLogoutEnabled) {
-                writeAttributeValueToXml(out, TAG_IS_LOGOUT_ENABLED, isLogoutEnabled);
-            }
-            if (startUserSessionMessage != null) {
-                writeTextToXml(out, TAG_START_USER_SESSION_MESSAGE, startUserSessionMessage);
-            }
-            if (endUserSessionMessage != null) {
-                writeTextToXml(out, TAG_END_USER_SESSION_MESSAGE, endUserSessionMessage);
-            }
-            if (mCrossProfileCalendarPackages == null) {
-                out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
-                out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
-            } else {
-                writePackageListToXml(out, TAG_CROSS_PROFILE_CALENDAR_PACKAGES,
-                        mCrossProfileCalendarPackages);
-            }
-            writePackageListToXml(out, TAG_CROSS_PROFILE_PACKAGES, mCrossProfilePackages);
-            if (mFactoryResetProtectionPolicy != null) {
-                out.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
-                mFactoryResetProtectionPolicy.writeToXml(out);
-                out.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
-            }
-            if (mSuspendPersonalApps) {
-                writeAttributeValueToXml(out, TAG_SUSPEND_PERSONAL_APPS, mSuspendPersonalApps);
-            }
-            if (mProfileMaximumTimeOffMillis != 0) {
-                writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF,
-                        mProfileMaximumTimeOffMillis);
-            }
-            if (mProfileMaximumTimeOffMillis != 0) {
-                writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline);
-            }
-            if (!TextUtils.isEmpty(mAlwaysOnVpnPackage)) {
-                writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_PACKAGE, mAlwaysOnVpnPackage);
-            }
-            if (mAlwaysOnVpnLockdown) {
-                writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_LOCKDOWN, mAlwaysOnVpnLockdown);
-            }
-            if (mCommonCriteriaMode) {
-                writeAttributeValueToXml(out, TAG_COMMON_CRITERIA_MODE, mCommonCriteriaMode);
-            }
-        }
-
-        void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
-            out.startTag(null, tag);
-            out.text(text);
-            out.endTag(null, tag);
-        }
-
-        void writePackageListToXml(XmlSerializer out, String outerTag,
-                List<String> packageList)
-                throws IllegalArgumentException, IllegalStateException, IOException {
-            if (packageList == null) {
-                return;
-            }
-            writeAttributeValuesToXml(out, outerTag, TAG_PACKAGE_LIST_ITEM, packageList);
-        }
-
-        void writeAttributeValueToXml(XmlSerializer out, String tag, String value)
-                throws IOException {
-            out.startTag(null, tag);
-            out.attribute(null, ATTR_VALUE, value);
-            out.endTag(null, tag);
-        }
-
-        void writeAttributeValueToXml(XmlSerializer out, String tag, int value)
-                throws IOException {
-            out.startTag(null, tag);
-            out.attribute(null, ATTR_VALUE, Integer.toString(value));
-            out.endTag(null, tag);
-        }
-
-        void writeAttributeValueToXml(XmlSerializer out, String tag, long value)
-                throws IOException {
-            out.startTag(null, tag);
-            out.attribute(null, ATTR_VALUE, Long.toString(value));
-            out.endTag(null, tag);
-        }
-
-        void writeAttributeValueToXml(XmlSerializer out, String tag, boolean value)
-                throws IOException {
-            out.startTag(null, tag);
-            out.attribute(null, ATTR_VALUE, Boolean.toString(value));
-            out.endTag(null, tag);
-        }
-
-        void writeAttributeValuesToXml(XmlSerializer out, String outerTag, String innerTag,
-                @NonNull Collection<String> values) throws IOException {
-            out.startTag(null, outerTag);
-            for (String value : values) {
-                out.startTag(null, innerTag);
-                out.attribute(null, ATTR_VALUE, value);
-                out.endTag(null, innerTag);
-            }
-            out.endTag(null, outerTag);
-        }
-
-        void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies)
-                throws XmlPullParserException, IOException {
-            int outerDepth = parser.getDepth();
-            int type;
-            while ((type=parser.next()) != END_DOCUMENT
-                   && (type != END_TAG || parser.getDepth() > outerDepth)) {
-                if (type == END_TAG || type == TEXT) {
-                    continue;
-                }
-                String tag = parser.getName();
-                if (TAG_POLICIES.equals(tag)) {
-                    if (shouldOverridePolicies) {
-                        Log.d(LOG_TAG, "Overriding device admin policies from XML.");
-                        info.readPoliciesFromXml(parser);
-                    }
-                } else if (TAG_PASSWORD_QUALITY.equals(tag)) {
-                    mPasswordPolicy.quality = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) {
-                    mPasswordPolicy.length = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) {
-                    passwordHistoryLength = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) {
-                    mPasswordPolicy.upperCase = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) {
-                    mPasswordPolicy.lowerCase = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) {
-                    mPasswordPolicy.letters = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) {
-                    mPasswordPolicy.numeric = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) {
-                    mPasswordPolicy.symbols = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
-                    mPasswordPolicy.nonLetter = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                }else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
-                    maximumTimeToUnlock = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) {
-                    strongAuthUnlockTimeout = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_MAX_FAILED_PASSWORD_WIPE.equals(tag)) {
-                    maximumFailedPasswordsForWipe = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_SPECIFIES_GLOBAL_PROXY.equals(tag)) {
-                    specifiesGlobalProxy = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_GLOBAL_PROXY_SPEC.equals(tag)) {
-                    globalProxySpec =
-                        parser.getAttributeValue(null, ATTR_VALUE);
-                } else if (TAG_GLOBAL_PROXY_EXCLUSION_LIST.equals(tag)) {
-                    globalProxyExclusionList =
-                        parser.getAttributeValue(null, ATTR_VALUE);
-                } else if (TAG_PASSWORD_EXPIRATION_TIMEOUT.equals(tag)) {
-                    passwordExpirationTimeout = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_PASSWORD_EXPIRATION_DATE.equals(tag)) {
-                    passwordExpirationDate = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) {
-                    encryptionRequested = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_TEST_ONLY_ADMIN.equals(tag)) {
-                    testOnlyAdmin = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_DISABLE_CAMERA.equals(tag)) {
-                    disableCamera = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
-                    disableCallerId = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) {
-                    disableContactsSearch = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) {
-                    disableBluetoothContactSharing = Boolean.parseBoolean(parser
-                            .getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) {
-                    disableScreenCapture = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_REQUIRE_AUTO_TIME.equals(tag)) {
-                    requireAutoTime = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) {
-                    forceEphemeralUsers = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) {
-                    isNetworkLoggingEnabled = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                    lastNetworkLoggingNotificationTimeMs = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION));
-                    numNetworkLoggingNotifications = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS));
-                } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
-                    disabledKeyguardFeatures = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
-                    readAttributeValues(
-                            parser, TAG_ACCOUNT_TYPE, accountTypesWithManagementDisabled);
-                } else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) {
-                    trustAgentInfos = getAllTrustAgentInfos(parser, tag);
-                } else if (TAG_CROSS_PROFILE_WIDGET_PROVIDERS.equals(tag)) {
-                    crossProfileWidgetProviders = new ArrayList<>();
-                    readAttributeValues(parser, TAG_PROVIDER, crossProfileWidgetProviders);
-                } else if (TAG_PERMITTED_ACCESSIBILITY_SERVICES.equals(tag)) {
-                    permittedAccessiblityServices = readPackageList(parser, tag);
-                } else if (TAG_PERMITTED_IMES.equals(tag)) {
-                    permittedInputMethods = readPackageList(parser, tag);
-                } else if (TAG_PERMITTED_NOTIFICATION_LISTENERS.equals(tag)) {
-                    permittedNotificationListeners = readPackageList(parser, tag);
-                } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) {
-                    keepUninstalledPackages = readPackageList(parser, tag);
-                } else if (TAG_METERED_DATA_DISABLED_PACKAGES.equals(tag)) {
-                    meteredDisabledPackages = readPackageList(parser, tag);
-                } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
-                    userRestrictions = UserRestrictionsUtils.readRestrictions(parser);
-                } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) {
-                    readAttributeValues(
-                            parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet);
-                } else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) {
-                    type = parser.next();
-                    if (type == XmlPullParser.TEXT) {
-                        shortSupportMessage = parser.getText();
-                    } else {
-                        Log.w(LOG_TAG, "Missing text when loading short support message");
-                    }
-                } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
-                    type = parser.next();
-                    if (type == XmlPullParser.TEXT) {
-                        longSupportMessage = parser.getText();
-                    } else {
-                        Log.w(LOG_TAG, "Missing text when loading long support message");
-                    }
-                } else if (TAG_PARENT_ADMIN.equals(tag)) {
-                    Preconditions.checkState(!isParent);
-                    parentAdmin = new ActiveAdmin(info, /* parent */ true);
-                    parentAdmin.readFromXml(parser, shouldOverridePolicies);
-                } else if (TAG_ORGANIZATION_COLOR.equals(tag)) {
-                    organizationColor = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_ORGANIZATION_NAME.equals(tag)) {
-                    type = parser.next();
-                    if (type == XmlPullParser.TEXT) {
-                        organizationName = parser.getText();
-                    } else {
-                        Log.w(LOG_TAG, "Missing text when loading organization name");
-                    }
-                } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
-                    isLogoutEnabled = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) {
-                    type = parser.next();
-                    if (type == XmlPullParser.TEXT) {
-                        startUserSessionMessage = parser.getText();
-                    } else {
-                        Log.w(LOG_TAG, "Missing text when loading start session message");
-                    }
-                } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
-                    type = parser.next();
-                    if (type == XmlPullParser.TEXT) {
-                        endUserSessionMessage = parser.getText();
-                    } else {
-                        Log.w(LOG_TAG, "Missing text when loading end session message");
-                    }
-                } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
-                    mCrossProfileCalendarPackages = readPackageList(parser, tag);
-                } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL.equals(tag)) {
-                    mCrossProfileCalendarPackages = null;
-                } else if (TAG_CROSS_PROFILE_PACKAGES.equals(tag)) {
-                    mCrossProfilePackages = readPackageList(parser, tag);
-                } else if (TAG_FACTORY_RESET_PROTECTION_POLICY.equals(tag)) {
-                    mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml(
-                                parser);
-                } else if (TAG_SUSPEND_PERSONAL_APPS.equals(tag)) {
-                    mSuspendPersonalApps = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) {
-                    mProfileMaximumTimeOffMillis =
-                            Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) {
-                    mProfileOffDeadline =
-                            Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_ALWAYS_ON_VPN_PACKAGE.equals(tag)) {
-                    mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE);
-                } else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) {
-                    mAlwaysOnVpnLockdown = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_COMMON_CRITERIA_MODE.equals(tag)) {
-                    mCommonCriteriaMode = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else {
-                    Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
-                    XmlUtils.skipCurrentTag(parser);
-                }
-            }
-        }
-
-        private List<String> readPackageList(XmlPullParser parser,
-                String tag) throws XmlPullParserException, IOException {
-            List<String> result = new ArrayList<String>();
-            int outerDepth = parser.getDepth();
-            int outerType;
-            while ((outerType=parser.next()) != XmlPullParser.END_DOCUMENT
-                    && (outerType != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-                if (outerType == XmlPullParser.END_TAG || outerType == XmlPullParser.TEXT) {
-                    continue;
-                }
-                String outerTag = parser.getName();
-                if (TAG_PACKAGE_LIST_ITEM.equals(outerTag)) {
-                    String packageName = parser.getAttributeValue(null, ATTR_VALUE);
-                    if (packageName != null) {
-                        result.add(packageName);
-                    } else {
-                        Slog.w(LOG_TAG, "Package name missing under " + outerTag);
-                    }
-                } else {
-                    Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + outerTag);
-                }
-            }
-            return result;
-        }
-
-        private void readAttributeValues(
-                XmlPullParser parser, String tag, Collection<String> result)
-                throws XmlPullParserException, IOException {
-            result.clear();
-            int outerDepthDAM = parser.getDepth();
-            int typeDAM;
-            while ((typeDAM=parser.next()) != END_DOCUMENT
-                    && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
-                if (typeDAM == END_TAG || typeDAM == TEXT) {
-                    continue;
-                }
-                String tagDAM = parser.getName();
-                if (tag.equals(tagDAM)) {
-                    result.add(parser.getAttributeValue(null, ATTR_VALUE));
-                } else {
-                    Slog.e(LOG_TAG, "Expected tag " + tag +  " but found " + tagDAM);
-                }
-            }
-        }
-
-        @NonNull
-        private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos(
-                XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
-            int outerDepthDAM = parser.getDepth();
-            int typeDAM;
-            final ArrayMap<String, TrustAgentInfo> result = new ArrayMap<>();
-            while ((typeDAM=parser.next()) != END_DOCUMENT
-                    && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
-                if (typeDAM == END_TAG || typeDAM == TEXT) {
-                    continue;
-                }
-                String tagDAM = parser.getName();
-                if (TAG_TRUST_AGENT_COMPONENT.equals(tagDAM)) {
-                    final String component = parser.getAttributeValue(null, ATTR_VALUE);
-                    final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
-                    result.put(component, trustAgentInfo);
-                } else {
-                    Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
-                }
-            }
-            return result;
-        }
-
-        private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag)
-                throws XmlPullParserException, IOException  {
-            int outerDepthDAM = parser.getDepth();
-            int typeDAM;
-            TrustAgentInfo result = new TrustAgentInfo(null);
-            while ((typeDAM=parser.next()) != END_DOCUMENT
-                    && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
-                if (typeDAM == END_TAG || typeDAM == TEXT) {
-                    continue;
-                }
-                String tagDAM = parser.getName();
-                if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
-                    result.options = PersistableBundle.restoreFromXml(parser);
-                } else {
-                    Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
-                }
-            }
-            return result;
-        }
-
-        boolean hasUserRestrictions() {
-            return userRestrictions != null && userRestrictions.size() > 0;
-        }
-
-        Bundle ensureUserRestrictions() {
-            if (userRestrictions == null) {
-                userRestrictions = new Bundle();
-            }
-            return userRestrictions;
-        }
-
-        public void transfer(DeviceAdminInfo deviceAdminInfo) {
-            if (hasParentActiveAdmin()) {
-                parentAdmin.info = deviceAdminInfo;
-            }
-            info = deviceAdminInfo;
-        }
-
-        Bundle addSyntheticRestrictions(Bundle restrictions) {
-            if (disableCamera) {
-                restrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
-            }
-            if (requireAutoTime) {
-                restrictions.putBoolean(UserManager.DISALLOW_CONFIG_DATE_TIME, true);
-            }
-            return restrictions;
-        }
-
-        static Bundle removeDeprecatedRestrictions(Bundle restrictions) {
-            for (String deprecatedRestriction: DEPRECATED_USER_RESTRICTIONS) {
-                restrictions.remove(deprecatedRestriction);
-            }
-            return restrictions;
-        }
-
-        static Bundle filterRestrictions(Bundle restrictions, Predicate<String> filter) {
-            Bundle result = new Bundle();
-            for (String key : restrictions.keySet()) {
-                if (!restrictions.getBoolean(key)) {
-                    continue;
-                }
-                if (filter.test(key)) {
-                    result.putBoolean(key, true);
-                }
-            }
-            return result;
-        }
-
-        Bundle getEffectiveRestrictions() {
-            return addSyntheticRestrictions(
-                    removeDeprecatedRestrictions(new Bundle(ensureUserRestrictions())));
-        }
-
-        Bundle getLocalUserRestrictions(int adminType) {
-            return filterRestrictions(getEffectiveRestrictions(),
-                    key -> UserRestrictionsUtils.isLocal(adminType, key));
-        }
-
-        Bundle getGlobalUserRestrictions(int adminType) {
-            return filterRestrictions(getEffectiveRestrictions(),
-                    key -> UserRestrictionsUtils.isGlobal(adminType, key));
-        }
-
-        void dump(IndentingPrintWriter pw) {
-            pw.print("uid="); pw.println(getUid());
-            pw.print("testOnlyAdmin=");
-            pw.println(testOnlyAdmin);
-            pw.println("policies:");
-            ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
-            if (pols != null) {
-                pw.increaseIndent();
-                for (int i=0; i<pols.size(); i++) {
-                    pw.println(pols.get(i).tag);
-                }
-                pw.decreaseIndent();
-            }
-            pw.print("passwordQuality=0x");
-                    pw.println(Integer.toHexString(mPasswordPolicy.quality));
-            pw.print("minimumPasswordLength=");
-                    pw.println(mPasswordPolicy.length);
-            pw.print("passwordHistoryLength=");
-                    pw.println(passwordHistoryLength);
-            pw.print("minimumPasswordUpperCase=");
-                    pw.println(mPasswordPolicy.upperCase);
-            pw.print("minimumPasswordLowerCase=");
-                    pw.println(mPasswordPolicy.lowerCase);
-            pw.print("minimumPasswordLetters=");
-                    pw.println(mPasswordPolicy.letters);
-            pw.print("minimumPasswordNumeric=");
-                    pw.println(mPasswordPolicy.numeric);
-            pw.print("minimumPasswordSymbols=");
-                    pw.println(mPasswordPolicy.symbols);
-            pw.print("minimumPasswordNonLetter=");
-                    pw.println(mPasswordPolicy.nonLetter);
-            pw.print("maximumTimeToUnlock=");
-                    pw.println(maximumTimeToUnlock);
-            pw.print("strongAuthUnlockTimeout=");
-                    pw.println(strongAuthUnlockTimeout);
-            pw.print("maximumFailedPasswordsForWipe=");
-                    pw.println(maximumFailedPasswordsForWipe);
-            pw.print("specifiesGlobalProxy=");
-                    pw.println(specifiesGlobalProxy);
-            pw.print("passwordExpirationTimeout=");
-                    pw.println(passwordExpirationTimeout);
-            pw.print("passwordExpirationDate=");
-                    pw.println(passwordExpirationDate);
-            if (globalProxySpec != null) {
-                pw.print("globalProxySpec=");
-                        pw.println(globalProxySpec);
-            }
-            if (globalProxyExclusionList != null) {
-                pw.print("globalProxyEclusionList=");
-                        pw.println(globalProxyExclusionList);
-            }
-            pw.print("encryptionRequested=");
-                    pw.println(encryptionRequested);
-            pw.print("disableCamera=");
-                    pw.println(disableCamera);
-            pw.print("disableCallerId=");
-                    pw.println(disableCallerId);
-            pw.print("disableContactsSearch=");
-                    pw.println(disableContactsSearch);
-            pw.print("disableBluetoothContactSharing=");
-                    pw.println(disableBluetoothContactSharing);
-            pw.print("disableScreenCapture=");
-                    pw.println(disableScreenCapture);
-            pw.print("requireAutoTime=");
-                    pw.println(requireAutoTime);
-            pw.print("forceEphemeralUsers=");
-                    pw.println(forceEphemeralUsers);
-            pw.print("isNetworkLoggingEnabled=");
-                    pw.println(isNetworkLoggingEnabled);
-            pw.print("disabledKeyguardFeatures=");
-                    pw.println(disabledKeyguardFeatures);
-            pw.print("crossProfileWidgetProviders=");
-                    pw.println(crossProfileWidgetProviders);
-            if (permittedAccessiblityServices != null) {
-                pw.print("permittedAccessibilityServices=");
-                    pw.println(permittedAccessiblityServices);
-            }
-            if (permittedInputMethods != null) {
-                pw.print("permittedInputMethods=");
-                    pw.println(permittedInputMethods);
-            }
-            if (permittedNotificationListeners != null) {
-                pw.print("permittedNotificationListeners=");
-                pw.println(permittedNotificationListeners);
-            }
-            if (keepUninstalledPackages != null) {
-                pw.print("keepUninstalledPackages=");
-                    pw.println(keepUninstalledPackages);
-            }
-            pw.print("organizationColor=");
-                    pw.println(organizationColor);
-            if (organizationName != null) {
-                pw.print("organizationName=");
-                    pw.println(organizationName);
-            }
-            pw.println("userRestrictions:");
-            UserRestrictionsUtils.dumpRestrictions(pw, "  ", userRestrictions);
-            pw.print("defaultEnabledRestrictionsAlreadySet=");
-                    pw.println(defaultEnabledRestrictionsAlreadySet);
-            pw.print("isParent=");
-                    pw.println(isParent);
-            if (parentAdmin != null) {
-                pw.println("parentAdmin:");
-                pw.increaseIndent();
-                parentAdmin.dump(pw);
-                pw.decreaseIndent();
-            }
-            if (mCrossProfileCalendarPackages != null) {
-                pw.print("mCrossProfileCalendarPackages=");
-                pw.println(mCrossProfileCalendarPackages);
-            }
-            pw.print("mCrossProfilePackages=");
-                pw.println(mCrossProfilePackages);
-            pw.print("mSuspendPersonalApps=");
-                pw.println(mSuspendPersonalApps);
-            pw.print("mProfileMaximumTimeOffMillis=");
-                pw.println(mProfileMaximumTimeOffMillis);
-            pw.print("mProfileOffDeadline=");
-                pw.println(mProfileOffDeadline);
-            pw.print("mAlwaysOnVpnPackage=");
-            pw.println(mAlwaysOnVpnPackage);
-            pw.print("mAlwaysOnVpnLockdown=");
-            pw.println(mAlwaysOnVpnLockdown);
-            pw.print("mCommonCriteriaMode=");
-            pw.println(mCommonCriteriaMode);
-        }
-    }
-
     private void handlePackagesChanged(@Nullable String packageName, int userHandle) {
         boolean removedAdmin = false;
         if (VERBOSE_LOG) {
@@ -2073,7 +934,7 @@
                 }
             }
             if (removedAdmin) {
-                validatePasswordOwnerLocked(policy);
+                policy.validatePasswordOwner();
             }
 
             boolean removedDelegate = false;
@@ -3514,13 +2375,8 @@
         }
     }
 
-
-    public DeviceAdminInfo findAdmin(final ComponentName adminName, final int userHandle,
+    private DeviceAdminInfo findAdmin(final ComponentName adminName, final int userHandle,
             boolean throwForMissingPermission) {
-        if (!mHasFeature) {
-            return null;
-        }
-        enforceFullCrossUsersPermission(userHandle);
         final ActivityInfo ai = mInjector.binderWithCleanCallingIdentity(() -> {
             try {
                 return mIPackageManager.getReceiverInfo(adminName,
@@ -3583,213 +2439,11 @@
     }
 
     private void saveSettingsLocked(int userHandle) {
-        DevicePolicyData policy = getUserData(userHandle);
-        JournaledFile journal = makeJournaledFile(userHandle);
-        FileOutputStream stream = null;
-        try {
-            stream = new FileOutputStream(journal.chooseForWrite(), false);
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(stream, StandardCharsets.UTF_8.name());
-            out.startDocument(null, true);
-
-            out.startTag(null, "policies");
-            if (policy.mRestrictionsProvider != null) {
-                out.attribute(null, ATTR_PERMISSION_PROVIDER,
-                        policy.mRestrictionsProvider.flattenToString());
-            }
-            if (policy.mUserSetupComplete) {
-                out.attribute(null, ATTR_SETUP_COMPLETE,
-                        Boolean.toString(true));
-            }
-            if (policy.mPaired) {
-                out.attribute(null, ATTR_DEVICE_PAIRED,
-                        Boolean.toString(true));
-            }
-            if (policy.mDeviceProvisioningConfigApplied) {
-                out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED,
-                        Boolean.toString(true));
-            }
-            if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
-                out.attribute(null, ATTR_PROVISIONING_STATE,
-                        Integer.toString(policy.mUserProvisioningState));
-            }
-            if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
-                out.attribute(null, ATTR_PERMISSION_POLICY,
-                        Integer.toString(policy.mPermissionPolicy));
-            }
-
-            // Serialize delegations.
-            for (int i = 0; i < policy.mDelegationMap.size(); ++i) {
-                final String delegatePackage = policy.mDelegationMap.keyAt(i);
-                final List<String> scopes = policy.mDelegationMap.valueAt(i);
-
-                // Every "delegation" tag serializes the information of one delegate-scope pair.
-                for (String scope : scopes) {
-                    out.startTag(null, "delegation");
-                    out.attribute(null, "delegatePackage", delegatePackage);
-                    out.attribute(null, "scope", scope);
-                    out.endTag(null, "delegation");
-                }
-            }
-
-            final int N = policy.mAdminList.size();
-            for (int i=0; i<N; i++) {
-                ActiveAdmin ap = policy.mAdminList.get(i);
-                if (ap != null) {
-                    out.startTag(null, "admin");
-                    out.attribute(null, "name", ap.info.getComponent().flattenToString());
-                    ap.writeToXml(out);
-                    out.endTag(null, "admin");
-                }
-            }
-
-            if (policy.mPasswordOwner >= 0) {
-                out.startTag(null, "password-owner");
-                out.attribute(null, "value", Integer.toString(policy.mPasswordOwner));
-                out.endTag(null, "password-owner");
-            }
-
-            if (policy.mFailedPasswordAttempts != 0) {
-                out.startTag(null, "failed-password-attempts");
-                out.attribute(null, "value", Integer.toString(policy.mFailedPasswordAttempts));
-                out.endTag(null, "failed-password-attempts");
-            }
-
-            // For FDE devices only, we save this flag so we can report on password sufficiency
-            // before the user enters their password for the first time after a reboot.  For
-            // security reasons, we don't want to store the full set of active password metrics.
-            if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
-                out.startTag(null, TAG_PASSWORD_VALIDITY);
-                out.attribute(null, ATTR_VALUE,
-                        Boolean.toString(policy.mPasswordValidAtLastCheckpoint));
-                out.endTag(null, TAG_PASSWORD_VALIDITY);
-            }
-
-            for (int i = 0; i < policy.mAcceptedCaCertificates.size(); i++) {
-                out.startTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
-                out.attribute(null, ATTR_NAME, policy.mAcceptedCaCertificates.valueAt(i));
-                out.endTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
-            }
-
-            for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
-                String component = policy.mLockTaskPackages.get(i);
-                out.startTag(null, TAG_LOCK_TASK_COMPONENTS);
-                out.attribute(null, "name", component);
-                out.endTag(null, TAG_LOCK_TASK_COMPONENTS);
-            }
-
-            if (policy.mLockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
-                out.startTag(null, TAG_LOCK_TASK_FEATURES);
-                out.attribute(null, ATTR_VALUE, Integer.toString(policy.mLockTaskFeatures));
-                out.endTag(null, TAG_LOCK_TASK_FEATURES);
-            }
-
-            if (policy.mSecondaryLockscreenEnabled) {
-                out.startTag(null, TAG_SECONDARY_LOCK_SCREEN);
-                out.attribute(null, ATTR_VALUE, Boolean.toString(true));
-                out.endTag(null, TAG_SECONDARY_LOCK_SCREEN);
-            }
-
-            if (policy.mStatusBarDisabled) {
-                out.startTag(null, TAG_STATUS_BAR);
-                out.attribute(null, ATTR_DISABLED, Boolean.toString(policy.mStatusBarDisabled));
-                out.endTag(null, TAG_STATUS_BAR);
-            }
-
-            if (policy.doNotAskCredentialsOnBoot) {
-                out.startTag(null, DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML);
-                out.endTag(null, DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML);
-            }
-
-            for (String id : policy.mAffiliationIds) {
-                out.startTag(null, TAG_AFFILIATION_ID);
-                out.attribute(null, ATTR_ID, id);
-                out.endTag(null, TAG_AFFILIATION_ID);
-            }
-
-            if (policy.mLastSecurityLogRetrievalTime >= 0) {
-                out.startTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL);
-                out.attribute(null, ATTR_VALUE,
-                        Long.toString(policy.mLastSecurityLogRetrievalTime));
-                out.endTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL);
-            }
-
-            if (policy.mLastBugReportRequestTime >= 0) {
-                out.startTag(null, TAG_LAST_BUG_REPORT_REQUEST);
-                out.attribute(null, ATTR_VALUE,
-                        Long.toString(policy.mLastBugReportRequestTime));
-                out.endTag(null, TAG_LAST_BUG_REPORT_REQUEST);
-            }
-
-            if (policy.mLastNetworkLogsRetrievalTime >= 0) {
-                out.startTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL);
-                out.attribute(null, ATTR_VALUE,
-                        Long.toString(policy.mLastNetworkLogsRetrievalTime));
-                out.endTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL);
-            }
-
-            if (policy.mAdminBroadcastPending) {
-                out.startTag(null, TAG_ADMIN_BROADCAST_PENDING);
-                out.attribute(null, ATTR_VALUE,
-                        Boolean.toString(policy.mAdminBroadcastPending));
-                out.endTag(null, TAG_ADMIN_BROADCAST_PENDING);
-            }
-
-            if (policy.mInitBundle != null) {
-                out.startTag(null, TAG_INITIALIZATION_BUNDLE);
-                policy.mInitBundle.saveToXml(out);
-                out.endTag(null, TAG_INITIALIZATION_BUNDLE);
-            }
-
-            if (policy.mPasswordTokenHandle != 0) {
-                out.startTag(null, TAG_PASSWORD_TOKEN_HANDLE);
-                out.attribute(null, ATTR_VALUE,
-                        Long.toString(policy.mPasswordTokenHandle));
-                out.endTag(null, TAG_PASSWORD_TOKEN_HANDLE);
-            }
-
-            if (policy.mCurrentInputMethodSet) {
-                out.startTag(null, TAG_CURRENT_INPUT_METHOD_SET);
-                out.endTag(null, TAG_CURRENT_INPUT_METHOD_SET);
-            }
-
-            for (final String cert : policy.mOwnerInstalledCaCerts) {
-                out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT);
-                out.attribute(null, ATTR_ALIAS, cert);
-                out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
-            }
-
-            for (int i = 0, size = policy.mUserControlDisabledPackages.size(); i < size; i++) {
-                String packageName = policy.mUserControlDisabledPackages.get(i);
-                out.startTag(null, TAG_PROTECTED_PACKAGES);
-                out.attribute(null, ATTR_NAME, packageName);
-                out.endTag(null, TAG_PROTECTED_PACKAGES);
-            }
-
-            if (policy.mAppsSuspended) {
-                out.startTag(null, TAG_APPS_SUSPENDED);
-                out.attribute(null, ATTR_VALUE, Boolean.toString(policy.mAppsSuspended));
-                out.endTag(null, TAG_APPS_SUSPENDED);
-            }
-
-            out.endTag(null, "policies");
-
-            out.endDocument();
-            stream.flush();
-            FileUtils.sync(stream);
-            stream.close();
-            journal.commit();
+        if (DevicePolicyData.store(
+                getUserData(userHandle),
+                makeJournaledFile(userHandle),
+                !mInjector.storageManagerIsFileBasedEncryptionEnabled())) {
             sendChangedNotification(userHandle);
-        } catch (XmlPullParserException | IOException e) {
-            Slog.w(LOG_TAG, "failed writing file", e);
-            try {
-                if (stream != null) {
-                    stream.close();
-                }
-            } catch (IOException ex) {
-                // Ignore
-            }
-            journal.rollback();
         }
     }
 
@@ -3801,225 +2455,19 @@
     }
 
     private void loadSettingsLocked(DevicePolicyData policy, int userHandle) {
-        JournaledFile journal = makeJournaledFile(userHandle);
-        FileInputStream stream = null;
-        File file = journal.chooseForRead();
-        boolean needsRewrite = false;
-        try {
-            stream = new FileInputStream(file);
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(stream, StandardCharsets.UTF_8.name());
-
-            int type;
-            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
-                    && type != XmlPullParser.START_TAG) {
-            }
-            String tag = parser.getName();
-            if (!"policies".equals(tag)) {
-                throw new XmlPullParserException(
-                        "Settings do not start with policies tag: found " + tag);
-            }
-
-            // Extract the permission provider component name if available
-            String permissionProvider = parser.getAttributeValue(null, ATTR_PERMISSION_PROVIDER);
-            if (permissionProvider != null) {
-                policy.mRestrictionsProvider = ComponentName.unflattenFromString(permissionProvider);
-            }
-            String userSetupComplete = parser.getAttributeValue(null, ATTR_SETUP_COMPLETE);
-            if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
-                policy.mUserSetupComplete = true;
-            }
-            String paired = parser.getAttributeValue(null, ATTR_DEVICE_PAIRED);
-            if (paired != null && Boolean.toString(true).equals(paired)) {
-                policy.mPaired = true;
-            }
-            String deviceProvisioningConfigApplied = parser.getAttributeValue(null,
-                    ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED);
-            if (deviceProvisioningConfigApplied != null
-                    && Boolean.toString(true).equals(deviceProvisioningConfigApplied)) {
-                policy.mDeviceProvisioningConfigApplied = true;
-            }
-            String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
-            if (!TextUtils.isEmpty(provisioningState)) {
-                policy.mUserProvisioningState = Integer.parseInt(provisioningState);
-            }
-            String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
-            if (!TextUtils.isEmpty(permissionPolicy)) {
-                policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
-            }
-            // Check for delegation compatibility with pre-O.
-            // TODO(edmanp) remove in P.
-            {
-                final String certDelegate = parser.getAttributeValue(null,
-                        ATTR_DELEGATED_CERT_INSTALLER);
-                if (certDelegate != null) {
-                    List<String> scopes = policy.mDelegationMap.get(certDelegate);
-                    if (scopes == null) {
-                        scopes = new ArrayList<>();
-                        policy.mDelegationMap.put(certDelegate, scopes);
-                    }
-                    if (!scopes.contains(DELEGATION_CERT_INSTALL)) {
-                        scopes.add(DELEGATION_CERT_INSTALL);
-                        needsRewrite = true;
-                    }
-                }
-                final String appRestrictionsDelegate = parser.getAttributeValue(null,
-                        ATTR_APPLICATION_RESTRICTIONS_MANAGER);
-                if (appRestrictionsDelegate != null) {
-                    List<String> scopes = policy.mDelegationMap.get(appRestrictionsDelegate);
-                    if (scopes == null) {
-                        scopes = new ArrayList<>();
-                        policy.mDelegationMap.put(appRestrictionsDelegate, scopes);
-                    }
-                    if (!scopes.contains(DELEGATION_APP_RESTRICTIONS)) {
-                        scopes.add(DELEGATION_APP_RESTRICTIONS);
-                        needsRewrite = true;
-                    }
-                }
-            }
-
-            type = parser.next();
-            int outerDepth = parser.getDepth();
-            policy.mLockTaskPackages.clear();
-            policy.mAdminList.clear();
-            policy.mAdminMap.clear();
-            policy.mAffiliationIds.clear();
-            policy.mOwnerInstalledCaCerts.clear();
-            policy.mUserControlDisabledPackages.clear();
-            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
-                   && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                    continue;
-                }
-                tag = parser.getName();
-                if ("admin".equals(tag)) {
-                    String name = parser.getAttributeValue(null, "name");
-                    try {
-                        DeviceAdminInfo dai = findAdmin(
-                                ComponentName.unflattenFromString(name), userHandle,
-                                /* throwForMissingPermission= */ false);
-                        if (VERBOSE_LOG
-                                && (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
-                                != userHandle)) {
-                            Slog.w(LOG_TAG, "findAdmin returned an incorrect uid "
-                                    + dai.getActivityInfo().applicationInfo.uid + " for user "
-                                    + userHandle);
-                        }
-                        if (dai != null) {
-                            boolean shouldOverwritePolicies =
-                                    shouldOverwritePoliciesFromXml(dai.getComponent(), userHandle);
-                            ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
-                            ap.readFromXml(parser, shouldOverwritePolicies);
-                            policy.mAdminMap.put(ap.info.getComponent(), ap);
-                        }
-                    } catch (RuntimeException e) {
-                        Slog.w(LOG_TAG, "Failed loading admin " + name, e);
-                    }
-                } else if ("delegation".equals(tag)) {
-                    // Parse delegation info.
-                    final String delegatePackage = parser.getAttributeValue(null,
-                            "delegatePackage");
-                    final String scope = parser.getAttributeValue(null, "scope");
-
-                    // Get a reference to the scopes list for the delegatePackage.
-                    List<String> scopes = policy.mDelegationMap.get(delegatePackage);
-                    // Or make a new list if none was found.
-                    if (scopes == null) {
-                        scopes = new ArrayList<>();
-                        policy.mDelegationMap.put(delegatePackage, scopes);
-                    }
-                    // Add the new scope to the list of delegatePackage if it's not already there.
-                    if (!scopes.contains(scope)) {
-                        scopes.add(scope);
-                    }
-                } else if ("failed-password-attempts".equals(tag)) {
-                    policy.mFailedPasswordAttempts = Integer.parseInt(
-                            parser.getAttributeValue(null, "value"));
-                } else if ("password-owner".equals(tag)) {
-                    policy.mPasswordOwner = Integer.parseInt(
-                            parser.getAttributeValue(null, "value"));
-                } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
-                    policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
-                } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
-                    policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
-                } else if (TAG_LOCK_TASK_FEATURES.equals(tag)) {
-                    policy.mLockTaskFeatures = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_SECONDARY_LOCK_SCREEN.equals(tag)) {
-                    policy.mSecondaryLockscreenEnabled = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_STATUS_BAR.equals(tag)) {
-                    policy.mStatusBarDisabled = Boolean.parseBoolean(
-                            parser.getAttributeValue(null, ATTR_DISABLED));
-                } else if (DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML.equals(tag)) {
-                    policy.doNotAskCredentialsOnBoot = true;
-                } else if (TAG_AFFILIATION_ID.equals(tag)) {
-                    policy.mAffiliationIds.add(parser.getAttributeValue(null, ATTR_ID));
-                } else if (TAG_LAST_SECURITY_LOG_RETRIEVAL.equals(tag)) {
-                    policy.mLastSecurityLogRetrievalTime = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_LAST_BUG_REPORT_REQUEST.equals(tag)) {
-                    policy.mLastBugReportRequestTime = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_LAST_NETWORK_LOG_RETRIEVAL.equals(tag)) {
-                    policy.mLastNetworkLogsRetrievalTime = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_ADMIN_BROADCAST_PENDING.equals(tag)) {
-                    String pending = parser.getAttributeValue(null, ATTR_VALUE);
-                    policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
-                } else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) {
-                    policy.mInitBundle = PersistableBundle.restoreFromXml(parser);
-                } else if ("active-password".equals(tag)) {
-                    // Remove password metrics from saved settings, as we no longer wish to store
-                    // these on disk
-                    needsRewrite = true;
-                } else if (TAG_PASSWORD_VALIDITY.equals(tag)) {
-                    if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
-                        // This flag is only used for FDE devices
-                        policy.mPasswordValidAtLastCheckpoint = Boolean.parseBoolean(
-                                parser.getAttributeValue(null, ATTR_VALUE));
-                    }
-                } else if (TAG_PASSWORD_TOKEN_HANDLE.equals(tag)) {
-                    policy.mPasswordTokenHandle = Long.parseLong(
-                            parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_CURRENT_INPUT_METHOD_SET.equals(tag)) {
-                    policy.mCurrentInputMethodSet = true;
-                } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
-                    policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
-                } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
-                    policy.mUserControlDisabledPackages.add(
-                            parser.getAttributeValue(null, ATTR_NAME));
-                } else if (TAG_APPS_SUSPENDED.equals(tag)) {
-                    policy.mAppsSuspended =
-                            Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
-                } else {
-                    Slog.w(LOG_TAG, "Unknown tag: " + tag);
-                    XmlUtils.skipCurrentTag(parser);
-                }
-            }
-        } catch (FileNotFoundException e) {
-            // Don't be noisy, this is normal if we haven't defined any policies.
-        } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
-                | IndexOutOfBoundsException e) {
-            Slog.w(LOG_TAG, "failed parsing " + file, e);
-        }
-        try {
-            if (stream != null) {
-                stream.close();
-            }
-        } catch (IOException e) {
-            // Ignore
-        }
-
-        // Generate a list of admins from the admin map
-        policy.mAdminList.addAll(policy.mAdminMap.values());
+        boolean needsRewrite = DevicePolicyData.load(policy,
+                !mInjector.storageManagerIsFileBasedEncryptionEnabled(),
+                makeJournaledFile(userHandle),
+                component -> findAdmin(
+                        component, userHandle, /* throwForMissingPermission= */ false),
+                getOwnerComponent(userHandle));
 
         // Might need to upgrade the file by rewriting it
         if (needsRewrite) {
             saveSettingsLocked(userHandle);
         }
 
-        validatePasswordOwnerLocked(policy);
+        policy.validatePasswordOwner();
         updateMaximumTimeToLockLocked(userHandle);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
         updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
@@ -4029,14 +2477,6 @@
         }
     }
 
-    private boolean shouldOverwritePoliciesFromXml(
-            ComponentName deviceAdminComponent, int userHandle) {
-        // http://b/123415062: If DA, overwrite with the stored policies that were agreed by the
-        // user to prevent apps from sneaking additional policies into updates.
-        return !isProfileOwner(deviceAdminComponent, userHandle)
-                && !isDeviceOwner(deviceAdminComponent, userHandle);
-    }
-
     private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
         long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -4098,23 +2538,6 @@
                 + Integer.toHexString(quality));
     }
 
-    void validatePasswordOwnerLocked(DevicePolicyData policy) {
-        if (policy.mPasswordOwner >= 0) {
-            boolean haveOwner = false;
-            for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
-                if (policy.mAdminList.get(i).getUid() == policy.mPasswordOwner) {
-                    haveOwner = true;
-                    break;
-                }
-            }
-            if (!haveOwner) {
-                Slog.w(LOG_TAG, "Previous password owner " + policy.mPasswordOwner
-                        + " no longer active; disabling");
-                policy.mPasswordOwner = -1;
-            }
-        }
-    }
-
     @VisibleForTesting
     @Override
     void systemReady(int phase) {
@@ -5842,8 +4265,8 @@
     private void setDoNotAskCredentialsOnBoot() {
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
-            if (!policyData.doNotAskCredentialsOnBoot) {
-                policyData.doNotAskCredentialsOnBoot = true;
+            if (!policyData.mDoNotAskCredentialsOnBoot) {
+                policyData.mDoNotAskCredentialsOnBoot = true;
                 saveSettingsLocked(UserHandle.USER_SYSTEM);
             }
         }
@@ -5855,7 +4278,7 @@
                 android.Manifest.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT, null);
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
-            return policyData.doNotAskCredentialsOnBoot;
+            return policyData.mDoNotAskCredentialsOnBoot;
         }
     }
 
@@ -14363,7 +12786,7 @@
                     DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
             policy.mAdminList.remove(admin);
             policy.mAdminMap.remove(adminReceiver);
-            validatePasswordOwnerLocked(policy);
+            policy.validatePasswordOwner();
             if (doProxyCleanup) {
                 resetGlobalProxyLocked(policy);
             }
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index a5f0d04..f7082a9 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -23,7 +23,6 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <binder/AppOpsManager.h>
-#include <binder/Nullable.h>
 #include <binder/Status.h>
 #include <sys/stat.h>
 #include <uuid/uuid.h>
@@ -1404,7 +1403,7 @@
     }
 
     FileSystemControlParcel fsControlParcel;
-    fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>();
+    fsControlParcel.incremental = std::make_optional<IncrementalFileSystemControlParcel>();
     fsControlParcel.incremental->cmd.reset(dup(ifs.control.cmd()));
     fsControlParcel.incremental->pendingReads.reset(dup(ifs.control.pendingReads()));
     fsControlParcel.incremental->log.reset(dup(ifs.control.logs()));
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 37bf664..33317a3 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionSessionId;
 import android.app.prediction.AppTarget;
@@ -26,8 +28,11 @@
 import android.app.prediction.IPredictionCallback;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
+import android.os.Binder;
 import android.os.CancellationSignal;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Slog;
 
@@ -35,6 +40,7 @@
 import com.android.server.SystemService;
 import com.android.server.people.data.DataManager;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
@@ -68,6 +74,7 @@
 
     @Override
     public void onStart() {
+        publishBinderService(Context.PEOPLE_SERVICE, new BinderService());
         publishLocalService(PeopleServiceInternal.class, new LocalService());
     }
 
@@ -81,6 +88,38 @@
         mDataManager.onUserStopping(user.getUserIdentifier());
     }
 
+    /**
+     * Enforces that only the system or root UID can make certain calls.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller is not system or root
+     */
+    private static void enforceSystemOrRoot(String message) {
+        int uid = Binder.getCallingUid();
+        if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) {
+            throw new SecurityException("Only system may " + message);
+        }
+    }
+
+    private final class BinderService extends IPeopleManager.Stub {
+
+        @Override
+        public ParceledListSlice<ConversationChannel> getRecentConversations() {
+            enforceSystemOrRoot("get recent conversations");
+            return new ParceledListSlice<>(new ArrayList<>());
+        }
+
+        @Override
+        public void removeRecentConversation(String packageName, int userId, String shortcutId) {
+            enforceSystemOrRoot("remove a recent conversation");
+        }
+
+        @Override
+        public void removeAllRecentConversations() {
+            enforceSystemOrRoot("remove all recent conversations");
+        }
+    }
+
     @VisibleForTesting
     final class LocalService extends PeopleServiceInternal {
 
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index cd9b6ac..cbebe69 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -1092,9 +1092,11 @@
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
+        backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
 
-        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
     }
 
     /** Test that the backup service does not route methods for non-registered users. */
@@ -1104,9 +1106,11 @@
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
-        backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
+        backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
 
-        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 6184c4e..09e3bfe 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -794,7 +794,7 @@
         setUpAgent(PACKAGE_1);
         doThrow(SecurityException.class)
                 .when(mBackupManagerService)
-                .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt());
+                .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt(), anyInt());
         KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
 
         runTask(task);
@@ -812,7 +812,7 @@
         setUpAgent(PACKAGE_1);
         doThrow(SecurityException.class)
                 .when(mBackupManagerService)
-                .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt());
+                .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt(), anyInt());
         KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
 
         runTask(task);
@@ -2593,11 +2593,13 @@
             if (packageData.available) {
                 doReturn(backupAgentBinder)
                         .when(mBackupManagerService)
-                        .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt());
+                        .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt(),
+                                anyInt());
             } else {
                 doReturn(null)
                         .when(mBackupManagerService)
-                        .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt());
+                        .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt(),
+                                anyInt());
             }
             return new AgentMock(backupAgentBinder, backupAgent);
         } catch (RemoteException e) {
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 3fc421d..5883c1c 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -57,6 +57,7 @@
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.utils.BackupEligibilityRules;
 import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowEventLog;
 import com.android.server.testing.shadows.ShadowPerformUnifiedRestoreTask;
@@ -96,6 +97,7 @@
     @Mock private TransportManager mTransportManager;
     @Mock private IRestoreObserver mObserver;
     @Mock private IBackupManagerMonitor mMonitor;
+    @Mock private BackupEligibilityRules mBackupEligibilityRules;
     private ShadowLooper mShadowBackupLooper;
     private ShadowApplication mShadowApplication;
     private UserBackupManagerService.BackupWakeLock mWakeLock;
@@ -576,7 +578,8 @@
     private IRestoreSession createActiveRestoreSession(
             String packageName, TransportData transport) {
         return new ActiveRestoreSession(
-                mBackupManagerService, packageName, transport.transportName);
+                mBackupManagerService, packageName, transport.transportName,
+                mBackupEligibilityRules);
     }
 
     private IRestoreSession createActiveRestoreSessionWithRestoreSets(
@@ -584,7 +587,8 @@
             throws RemoteException {
         ActiveRestoreSession restoreSession =
                 new ActiveRestoreSession(
-                        mBackupManagerService, packageName, transport.transportName);
+                        mBackupManagerService, packageName, transport.transportName,
+                        mBackupEligibilityRules);
         restoreSession.setRestoreSets(restoreSets);
         return restoreSession;
     }
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/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index ecdb30f..09552082 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -33,7 +33,6 @@
 import com.android.server.pm.parsing.pkg.ParsedPackage
 import com.android.server.pm.permission.PermissionManagerServiceInternal
 import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
-import com.android.server.pm.test.override.R
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
 import com.android.server.testutils.mockThrowOnUnmocked
@@ -266,7 +265,7 @@
                     .hideAsFinal()
 
     private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"),
-            File("/test"), null, null, null, null, 0, 0, 0, 0, null, null, null)) {
+            null, null, null, null, 0, 0, 0, 0, null, null, null)) {
         this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
     }
 
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 41dfade..e4e7e22 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -25,17 +25,29 @@
     ],
     test_suites: ["general-tests"],
     java_resources: [
-        ":PackageManagerDummyAppVersion1",
-        ":PackageManagerDummyAppVersion2",
-        ":PackageManagerDummyAppVersion3",
-        ":PackageManagerDummyAppVersion4",
-        ":PackageManagerDummyAppOriginalOverride",
-        ":PackageManagerServiceHostTestsResources",
-    ]
+        ":PackageManagerTestAppStub",
+        ":PackageManagerTestAppVersion1",
+        ":PackageManagerTestAppVersion2",
+        ":PackageManagerTestAppVersion3",
+        ":PackageManagerTestAppVersion3Invalid",
+        ":PackageManagerTestAppVersion4",
+        ":PackageManagerTestAppOriginalOverride",
+        ":PackageManagerServiceDeviceSideTests",
+    ],
 }
 
-filegroup {
-    name: "PackageManagerServiceHostTestsResources",
-    srcs: [ "resources/*" ],
-    path: "resources/"
+genrule {
+    name: "PackageManagerTestAppVersion3Invalid",
+    tools: [
+        "soong_zip",
+        "zipalign",
+    ],
+    srcs: [
+        ":PackageManagerTestAppVersion3",
+    ],
+    out: ["PackageManagerTestAppVersion3Invalid.apk"],
+    cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" +
+        " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" +
+        " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" +
+        " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)",
 }
diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
deleted file mode 100644
index 127886c..0000000
--- a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
+++ /dev/null
Binary files differ
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
new file mode 100644
index 0000000..3847658
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
@@ -0,0 +1,71 @@
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class FactoryPackageTest : BaseHostJUnit4Test() {
+
+    companion object {
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+        private const val DEVICE_SIDE = "PackageManagerServiceDeviceSideTests.apk"
+
+        @get:ClassRule
+        val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+    }
+
+    private val tempFolder = TemporaryFolder()
+    private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+            SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
+
+    @get:Rule
+    val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+    private val filePath =
+            HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.SYSTEM)
+
+    @Before
+    @After
+    fun removeApk() {
+        device.uninstallPackage(TEST_PKG_NAME)
+        device.deleteFile(filePath.parent.toString())
+        device.reboot()
+    }
+
+    @Test
+    fun testGetInstalledPackagesFactoryOnlyFlag() {
+        // First, push a system app to the device and then update it so there's a data variant
+        preparer.pushResourceFile(VERSION_ONE, filePath.toString())
+                .reboot()
+
+        val versionTwoFile = HostUtils.copyResourceToHostFile(VERSION_TWO, tempFolder.newFile())
+
+        assertThat(device.installPackage(versionTwoFile, true)).isNull()
+
+        runDeviceTest("testGetInstalledPackagesWithFactoryOnly")
+    }
+
+    /**
+     * Run a device side test from com.android.server.pm.test.deviceside.DeviceSide
+     *
+     * @param method the method to run
+     */
+    fun runDeviceTest(method: String) {
+        val deviceSideFile = HostUtils.copyResourceToHostFile(DEVICE_SIDE, tempFolder.newFile())
+        assertThat(device.installPackage(deviceSideFile, true)).isNull()
+        runDeviceTests(device, "com.android.server.pm.test.deviceside",
+                "com.android.server.pm.test.deviceside.DeviceSide", method)
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 234fcf1..8dfefaf 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -21,8 +21,9 @@
 import java.io.File
 import java.io.FileOutputStream
 
-internal fun SystemPreparer.pushApk(file: String, partition: Partition) =
-        pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString())
+internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) =
+        pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition)
+                .toString())
 
 internal fun SystemPreparer.deleteApkFolders(
     partition: Partition,
@@ -58,4 +59,55 @@
         }
         return file
     }
+
+    /**
+     * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot,
+     * so the following methods parse the package dump directly to see if the path matches.
+     */
+    fun getCodePaths(device: ITestDevice, pkgName: String) =
+            device.executeShellCommand("pm dump $pkgName")
+                    .lineSequence()
+                    .map(String::trim)
+                    .filter { it.startsWith("codePath=") }
+                    .map { it.removePrefix("codePath=") }
+                    .toList()
+
+    private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
+            device.executeShellCommand("pm dump $pkgName")
+                    .lineSequence()
+                    .dropWhile { !it.startsWith("Packages:") }
+                    .takeWhile {
+                        !it.startsWith("Hidden system packages:") &&
+                                !it.startsWith("Queries:")
+                    }
+                    .map(String::trim)
+                    .filter { it.startsWith("User ") }
+
+    fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) =
+            userIdLineSequence(device, pkgName).associate {
+                val userId = it.removePrefix("User ")
+                        .takeWhile(Char::isDigit)
+                        .toInt()
+                val enabled = it.substringAfter("enabled=")
+                        .takeWhile(Char::isDigit)
+                        .toInt()
+                        .let {
+                            when (it) {
+                                0, 1 -> true
+                                else -> false
+                            }
+                        }
+                userId to enabled
+            }
+
+    fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) =
+            userIdLineSequence(device, pkgName).associate {
+                val userId = it.removePrefix("User ")
+                        .takeWhile(Char::isDigit)
+                        .toInt()
+                val installed = it.substringAfter("installed=")
+                        .takeWhile { !it.isWhitespace() }
+                        .toBoolean()
+                userId to installed
+            }
 }
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index 39b40d8..b7d1359 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -33,11 +33,11 @@
 class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
 
     companion object {
-        private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
-        private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
-        private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
-        private const val VERSION_THREE_INVALID = "PackageManagerDummyAppVersion3Invalid.apk"
-        private const val VERSION_FOUR = "PackageManagerDummyAppVersion4.apk"
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+        private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk"
+        private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk"
 
         @get:ClassRule
         val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -49,14 +49,14 @@
 
     @get:Rule
     val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
-    private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT)
+    private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT)
 
     @Before
     @After
     fun removeApk() {
         device.uninstallPackage(TEST_PKG_NAME)
-        device.deleteFile(filePath.parent.toString())
-        device.reboot()
+        preparer.deleteFile(filePath.parent.toString())
+                .reboot()
     }
 
     @Test
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index fb0348c..4ae3ca5 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -33,11 +33,11 @@
 class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
 
     companion object {
-        private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
-        private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
-        private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
-        private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk"
-        private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk"
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+        private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk"
+        private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk"
 
         @get:ClassRule
         val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -55,6 +55,7 @@
     fun deleteApkFolders() {
         preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE,
                 NEW_PKG)
+                .reboot()
     }
 
     @Test
@@ -99,9 +100,7 @@
     }
 
     private fun assertCodePath(apk: String) {
-        // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason,
-        // so parse the package dump directly to see if the path matches.
-        assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME"))
-                .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
+        assertThat(HostUtils.getCodePaths(device, TEST_PKG_NAME))
+                .containsExactly(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
new file mode 100644
index 0000000..207f10a
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.device.ITestDevice
+import com.android.tradefed.device.UserInfo
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.AfterClass
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+import java.util.zip.GZIPOutputStream
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() {
+
+    companion object {
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+        private const val VERSION_STUB = "PackageManagerTestAppStub.apk"
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+
+        /**
+         * How many total users on device to test, including primary. This will clean up any
+         * users created specifically for this test.
+         */
+        private const val USER_COUNT = 3
+
+        /**
+         * Whether to manually reset state at each test method without rebooting
+         * for faster iterative development.
+         */
+        private const val DEBUG_NO_REBOOT = false
+
+        @get:ClassRule
+        val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+
+        private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName
+
+        private val deviceCompressedFile =
+                HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent
+                        .resolve("$parentClassName.apk.gz")
+
+        private val stubFile =
+                HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT)
+
+        private val secondaryUsers = mutableListOf<Int>()
+        private val usersToRemove = mutableListOf<Int>()
+        private var savedDevice: ITestDevice? = null
+        private var savedPreparer: SystemPreparer? = null
+
+        private fun setUpUsers(device: ITestDevice) {
+            if (this.savedDevice != null) return
+            this.savedDevice = device
+            secondaryUsers.clear()
+            secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 }
+            while (secondaryUsers.size < USER_COUNT) {
+                secondaryUsers += device.createUser(parentClassName + secondaryUsers.size)
+                        .also { usersToRemove += it }
+            }
+        }
+
+        @JvmStatic
+        @AfterClass
+        fun cleanUp() {
+            savedDevice ?: return
+
+            usersToRemove.forEach {
+                savedDevice?.removeUser(it)
+            }
+
+            savedDevice?.uninstallPackage(TEST_PKG_NAME)
+            savedDevice?.deleteFile(stubFile.parent.toString())
+            savedDevice?.deleteFile(deviceCompressedFile.parent.toString())
+            savedDevice?.reboot()
+            savedDevice = null
+
+            if (DEBUG_NO_REBOOT) {
+                savedPreparer?.after()
+                savedPreparer = null
+            }
+        }
+    }
+
+    private val tempFolder = TemporaryFolder()
+
+    // TODO(b/160159215): Use START_STOP rather than FULL once it's fixed. This will drastically
+    //  improve pre/post-submit times.
+    private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+            SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
+
+    @get:Rule
+    val rules = RuleChain.outerRule(tempFolder).let {
+        if (DEBUG_NO_REBOOT) {
+            it!!
+        } else {
+            it.around(preparer)!!
+        }
+    }
+
+    private var hostCompressedFile: File? = null
+
+    private val previousCodePaths = mutableListOf<String>()
+
+    @Before
+    fun ensureUserAndCompressStubAndInstall() {
+        setUpUsers(device)
+
+        val initialized = hostCompressedFile != null
+        if (!initialized) {
+            hostCompressedFile = tempFolder.newFile()
+            hostCompressedFile!!.outputStream().use {
+                javaClass.classLoader
+                        .getResource(VERSION_ONE)!!
+                        .openStream()
+                        .use { input ->
+                            GZIPOutputStream(it).use { output ->
+                                input.copyTo(output)
+                            }
+                        }
+            }
+        }
+
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        if (!initialized || !DEBUG_NO_REBOOT) {
+            savedPreparer = preparer
+            preparer.pushResourceFile(VERSION_STUB, stubFile.toString())
+                    .pushFile(hostCompressedFile, deviceCompressedFile.toString())
+                    .reboot()
+        }
+
+        // This test forces the state to installed/enabled for all users,
+        // since it only tests the uninstall/disable side.
+        installExisting(User.PRIMARY)
+        installExisting(User.SECONDARY)
+
+        ensureEnabled()
+
+        // Ensure data app isn't re-installed multiple times by comparing against the original path
+        val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first()
+        assertThat(codePath).contains("/data/app")
+        assertThat(codePath).contains(TEST_PKG_NAME)
+
+        previousCodePaths.clear()
+        previousCodePaths += codePath
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disablePrimaryFirstAndUninstall() {
+        toggleEnabled(false, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(false, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disableSecondaryFirstAndUninstall() {
+        toggleEnabled(false, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(false, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstalledEnablePrimaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstalledEnableSecondaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() {
+        uninstall(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        uninstall(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        installExisting(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        installExisting(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() {
+        uninstall(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        uninstall(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        installExisting(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        installExisting(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallUpdatesAndEnablePrimaryFirst() {
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        // TODO: Is this intentional? There is no user argument for this command.
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                // If any user is enabled when uninstalling updates, /data is re-uncompressed
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        // Test enabling secondary to ensure path does not change, even though it's already enabled
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallUpdatesAndEnableSecondaryFirst() {
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                // If any user is enabled when uninstalling updates, /data is re-uncompressed
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstallUpdatesAndEnablePrimaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstallUpdatesAndEnableSecondaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() {
+        uninstall(User.PRIMARY)
+        uninstall(User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        assertState(
+                primaryInstalled = false, primaryEnabled = false,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() {
+        uninstall(User.PRIMARY)
+        uninstall(User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        assertState(
+                primaryInstalled = false, primaryEnabled = false,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = false,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    private fun ensureEnabled() {
+        toggleEnabled(true, User.PRIMARY)
+        toggleEnabled(true, User.SECONDARY)
+
+        assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value })
+                .isTrue()
+    }
+
+    private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) {
+        val command = if (enabled) "enable" else "disable"
+        @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+            User.PRIMARY -> {
+                device.executeShellCommand("pm $command --user 0 $pkgName")
+            }
+            User.SECONDARY -> {
+                secondaryUsers.forEach {
+                    device.executeShellCommand("pm $command --user $it $pkgName")
+                }
+            }
+        }
+    }
+
+    private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) {
+        @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+            User.PRIMARY -> {
+                device.executeShellCommand("pm uninstall --user 0 $pkgName")
+            }
+            User.SECONDARY -> {
+                secondaryUsers.forEach {
+                    device.executeShellCommand("pm uninstall --user $it $pkgName")
+                }
+            }
+        }
+    }
+
+    private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) {
+        @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+            User.PRIMARY -> {
+                device.executeShellCommand("pm install-existing --user 0 $pkgName")
+            }
+            User.SECONDARY -> {
+                secondaryUsers.forEach {
+                    device.executeShellCommand("pm install-existing --user $it $pkgName")
+                }
+            }
+        }
+    }
+
+    private fun assertState(
+        primaryInstalled: Boolean,
+        primaryEnabled: Boolean,
+        secondaryInstalled: Boolean,
+        secondaryEnabled: Boolean,
+        codePaths: List<CodePath>
+    ) {
+        HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME)
+                .forEach { (userId, installed) ->
+                    if (userId == 0) {
+                        assertThat(installed).isEqualTo(primaryInstalled)
+                    } else {
+                        assertThat(installed).isEqualTo(secondaryInstalled)
+                    }
+                }
+
+        HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME)
+                .forEach { (userId, enabled) ->
+                    if (userId == 0) {
+                        assertThat(enabled).isEqualTo(primaryEnabled)
+                    } else {
+                        assertThat(enabled).isEqualTo(secondaryEnabled)
+                    }
+                }
+
+        assertCodePaths(codePaths.first(), codePaths.getOrNull(1))
+    }
+
+    private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) {
+        val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME)
+        assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size)
+
+        when (firstCodePath) {
+            CodePath.SAME -> {
+                assertThat(codePaths[0]).contains("/data/app")
+                assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+                assertThat(codePaths[0]).isEqualTo(previousCodePaths.last())
+            }
+            CodePath.DIFFERENT -> {
+                assertThat(codePaths[0]).contains("/data/app")
+                assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+                assertThat(previousCodePaths).doesNotContain(codePaths[0])
+                previousCodePaths.add(codePaths[0])
+            }
+            CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString())
+        }
+
+        when (secondCodePath) {
+            CodePath.SAME, CodePath.DIFFERENT ->
+                throw AssertionError("secondDataPath cannot be a data path")
+            CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+        }
+    }
+
+    enum class User {
+        /** The primary system user 0 */
+        PRIMARY,
+
+        /**
+         * All other users on the device that are not 0. This is split into an enum so that all
+         * methods that handle secondary act on all non-system users. Some behaviors only occur
+         * if a package state is marked for all non-primary users on the device, which can be
+         * more than just 1.
+         */
+        SECONDARY
+    }
+
+    enum class CodePath {
+        /** The data code path hasn't changed */
+        SAME,
+
+        /** New data code path */
+        DIFFERENT,
+
+        /** The static system code path */
+        SYSTEM
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
index c9b2927..4a3076e 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
@@ -13,26 +13,32 @@
 // limitations under the License.
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion1",
+    name: "PackageManagerTestAppStub",
+    manifest: "AndroidManifestVersion1.xml",
+    srcs: []
+}
+
+android_test_helper_app {
+    name: "PackageManagerTestAppVersion1",
     manifest: "AndroidManifestVersion1.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion2",
+    name: "PackageManagerTestAppVersion2",
     manifest: "AndroidManifestVersion2.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion3",
+    name: "PackageManagerTestAppVersion3",
     manifest: "AndroidManifestVersion3.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion4",
+    name: "PackageManagerTestAppVersion4",
     manifest: "AndroidManifestVersion4.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppOriginalOverride",
+    name: "PackageManagerTestAppOriginalOverride",
     manifest: "AndroidManifestOriginalOverride.xml"
 }
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
index f16e1bc..cba580e 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
@@ -16,10 +16,10 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app.override"
+    package="com.android.server.pm.test.test_app.override"
     android:versionCode="2"
     >
 
-    <original-package android:name="com.android.server.pm.test.dummy_app"/>
+    <original-package android:name="com.android.server.pm.test.test_app"/>
 
 </manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
index b492a31..efc7372 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="1"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
index 25e9f8e..620054c 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="2"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
index 935f5e6..1997771 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="3"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
index d0643cb..d6ade03 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="4"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
new file mode 100644
index 0000000..af0ac77
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+    name: "PackageManagerServiceDeviceSideTests",
+    sdk_version: "test_current",
+    srcs: ["src/**/*.kt"],
+    libs: [
+        "android.test.base",
+    ],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "junit",
+        "junit-params",
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "truth-prebuilt",
+    ],
+    platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml
new file mode 100644
index 0000000..286ad56
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.pm.test.deviceside">
+
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.server.pm.test.deviceside" />
+</manifest>
+
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/src/com/android/server/pm/cts/test/deviceside/DeviceSide.kt b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/src/com/android/server/pm/cts/test/deviceside/DeviceSide.kt
new file mode 100644
index 0000000..d140662
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/src/com/android/server/pm/cts/test/deviceside/DeviceSide.kt
@@ -0,0 +1,42 @@
+package com.android.server.pm.test.deviceside
+
+import android.content.pm.PackageManager.MATCH_FACTORY_ONLY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DeviceSide {
+    companion object {
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+    }
+
+    @Test
+    fun testGetInstalledPackagesWithFactoryOnly() {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        val uiAutomation = instrumentation.uiAutomation
+        val ctx = instrumentation.context
+
+        uiAutomation.adoptShellPermissionIdentity()
+        try {
+            val packages1 = ctx.packageManager.getInstalledPackages(0)
+                    .filter { it.packageName == TEST_PKG_NAME }
+            val packages2 = ctx.packageManager.getInstalledPackages(MATCH_FACTORY_ONLY)
+                    .filter { it.packageName == TEST_PKG_NAME }
+
+            Truth.assertWithMessage("Incorrect number of packages found")
+                    .that(packages1.size).isEqualTo(1)
+            Truth.assertWithMessage("Incorrect number of packages found")
+                    .that(packages2.size).isEqualTo(1)
+
+            Truth.assertWithMessage("Incorrect version code for updated package")
+                    .that(packages1[0].longVersionCode).isEqualTo(2)
+            Truth.assertWithMessage("Incorrect version code for factory package")
+                    .that(packages2[0].longVersionCode).isEqualTo(1)
+        } finally {
+            uiAutomation.dropShellPermissionIdentity()
+        }
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 2a267c4..82726c7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -99,6 +99,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
@@ -165,7 +166,7 @@
         setFieldValue(ActivityManagerService.class, sService, "mHandler",
                 mock(ActivityManagerService.MainHandler.class));
         setFieldValue(ActivityManagerService.class, sService, "mProcessStats",
-                mock(ProcessStatsService.class));
+                new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats")));
         setFieldValue(ActivityManagerService.class, sService, "mBackupTargets",
                 mock(SparseArray.class));
         setFieldValue(ActivityManagerService.class, sService, "mOomAdjProfiler",
@@ -500,7 +501,7 @@
     public void testUpdateOomAdj_DoOne_Backup() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        BackupRecord backupTarget = new BackupRecord(null, 0, 0);
+        BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
         backupTarget.app = app;
         doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
         sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -802,7 +803,7 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
-        BackupRecord backupTarget = new BackupRecord(null, 0, 0);
+        BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
         backupTarget.app = client;
         doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
         sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
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/assets/NetworkPolicyManagerServiceTest/netpolicy/restrict-background-lists-whitelist-format.xml b/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/restrict-background-lists-allowlist-format.xml
similarity index 100%
rename from services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/restrict-background-lists-whitelist-format.xml
rename to services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/restrict-background-lists-allowlist-format.xml
diff --git a/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-whitelisted-restrict-background-off.xml b/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-allowlisted-restrict-background-off.xml
similarity index 100%
rename from services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-whitelisted-restrict-background-off.xml
rename to services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-allowlisted-restrict-background-off.xml
diff --git a/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-whitelisted-restrict-background-on.xml b/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-allowlisted-restrict-background-on.xml
similarity index 100%
rename from services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-whitelisted-restrict-background-on.xml
rename to services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-allowlisted-restrict-background-on.xml
diff --git a/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-blacklisted-restrict-background-off.xml b/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-denylisted-restrict-background-off.xml
similarity index 100%
rename from services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-blacklisted-restrict-background-off.xml
rename to services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-denylisted-restrict-background-off.xml
diff --git a/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-blacklisted-restrict-background-on.xml b/services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-denylisted-restrict-background-on.xml
similarity index 100%
rename from services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-blacklisted-restrict-background-on.xml
rename to services/tests/servicestests/assets/NetworkPolicyManagerServiceTest/netpolicy/uidA-denylisted-restrict-background-on.xml
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index c692253..7d6d90c 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -26,7 +26,9 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -65,6 +67,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
@@ -183,13 +186,12 @@
 
     @Test
     public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         assertTrue(createService().hasAmplitudeControl());
     }
 
     @Test
     public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false);
         assertFalse(createService().hasAmplitudeControl());
     }
 
@@ -270,7 +272,7 @@
 
     @Test
     public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibratorService service = createService();
         Mockito.clearInvocations(mNativeWrapperMock);
 
@@ -278,7 +280,7 @@
         assertTrue(service.isVibrating());
 
         verify(mNativeWrapperMock).vibratorOff();
-        verify(mNativeWrapperMock).vibratorOn(eq(100L));
+        verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
         verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128));
     }
 
@@ -291,7 +293,7 @@
         assertTrue(service.isVibrating());
 
         verify(mNativeWrapperMock).vibratorOff();
-        verify(mNativeWrapperMock).vibratorOn(eq(100L));
+        verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
         verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt());
     }
 
@@ -340,81 +342,162 @@
     @Test
     public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime()
             throws Exception {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibratorService service = createService();
         Mockito.clearInvocations(mNativeWrapperMock);
 
         VibrationEffect effect = VibrationEffect.createWaveform(
-                new long[] { 10, 10, 10 }, new int[] { 100, 200, 50 }, -1);
+                new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
         vibrate(service, effect);
 
         verify(mNativeWrapperMock).vibratorOff();
 
+        // Wait for VibrateThread to turn vibrator ON with total timing and no callback.
         Thread.sleep(5);
-        verify(mNativeWrapperMock).vibratorOn(eq(30L));
+        verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull());
+
+        // First amplitude set right away.
         verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
 
+        // Second amplitude set after first timing is finished.
         Thread.sleep(10);
         verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200));
 
+        // Third amplitude set after second timing is finished.
         Thread.sleep(10);
         verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50));
     }
 
     @Test
-    public void vibrate_withCallback_finishesVibrationWhenCallbackTriggered() {
-        mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+    public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() {
+        doAnswer(invocation -> {
+            ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
+            return null;
+        }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
         VibratorService service = createService();
         Mockito.clearInvocations(mNativeWrapperMock);
 
+        vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
+
+        InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L),
+                any(VibratorService.Vibration.class));
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+    }
+
+    @Test
+    public void vibrate_withOneShotAndNativeCallbackNotTriggered_finishesVibrationViaFallback() {
+        VibratorService service = createService();
+        Mockito.clearInvocations(mNativeWrapperMock);
+
+        vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
+
+        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();
+    }
+
+    @Test
+    public void vibrate_withWaveformAndNativeCallback_callbackCannotBeTriggeredByNative()
+            throws Exception {
+        VibratorService service = createService();
+        Mockito.clearInvocations(mNativeWrapperMock);
+
+        VibrationEffect effect = VibrationEffect.createWaveform(new long[]{1, 3, 1, 2}, -1);
+        vibrate(service, effect);
+
+        // Wait for VibrateThread to finish: 1ms OFF, 3ms ON, 1ms OFF, 2ms ON.
+        Thread.sleep(15);
+        InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), isNull());
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), isNull());
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+    }
+
+    @Test
+    public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() {
+        mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
         doAnswer(invocation -> {
             ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
             return null;
         }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
                 any(), any(VibratorService.Vibration.class));
+        VibratorService service = createService();
+        Mockito.clearInvocations(mNativeWrapperMock);
 
-        // Use vibration with delay so there is time for the callback to be triggered.
         VibrationEffect effect = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
                 .compose();
         vibrate(service, effect);
 
-        // Vibration canceled once before perform and once by native callback.
-        verify(mNativeWrapperMock, times(2)).vibratorOff();
+        InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+                any(VibrationEffect.Composition.PrimitiveEffect[].class),
+                any(VibratorService.Vibration.class));
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+    }
+
+    @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);
-        VibratorService service = createService();
-        Mockito.clearInvocations(mNativeWrapperMock);
-
         doAnswer(invocation -> {
             ((VibratorService.Vibration) invocation.getArgument(1)).binderDied();
             return null;
         }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
                 any(), any(VibratorService.Vibration.class));
+        VibratorService service = createService();
+        Mockito.clearInvocations(mNativeWrapperMock);
 
-        // Use vibration with delay so there is time for the callback to be triggered.
         VibrationEffect effect = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
                 .compose();
         vibrate(service, effect);
 
-        // Vibration canceled once before perform and once by native binder death.
-        verify(mNativeWrapperMock, times(2)).vibratorOff();
-        verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+        InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
                 any(VibrationEffect.Composition.PrimitiveEffect[].class),
                 any(VibratorService.Vibration.class));
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
     }
 
     @Test
     public void cancelVibrate_withDeviceVibrating_callsVibratorOff() {
         VibratorService service = createService();
-        vibrate(service, VibrationEffect.createOneShot(100, 128));
+        vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
         assertTrue(service.isVibrating());
         Mockito.clearInvocations(mNativeWrapperMock);
 
@@ -435,18 +518,20 @@
 
     @Test
     public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
+        doAnswer(invocation -> {
+            ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
+            return null;
+        }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
         VibratorService service = createService();
 
         service.registerVibratorStateListener(mVibratorStateListenerMock);
         verify(mVibratorStateListenerMock).onVibrating(false);
+        Mockito.clearInvocations(mVibratorStateListenerMock);
 
         vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE));
-        verify(mVibratorStateListenerMock).onVibrating(true);
-
-        // Run the scheduled callback to finish one-shot vibration.
-        mTestLooper.moveTimeForward(10);
-        mTestLooper.dispatchAll();
-        verify(mVibratorStateListenerMock, times(2)).onVibrating(false);
+        InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
+        inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true));
+        inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
     }
 
     @Test
@@ -511,7 +596,7 @@
         setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_OFF);
 
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibratorService service = createService();
         service.systemReady();
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 6450a0f..763654d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -42,6 +42,7 @@
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.InputDevice;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.test.InstrumentationRegistry;
@@ -87,6 +88,8 @@
     private MotionEvent mLastEvent;
     private TestHandler mHandler;
     private TouchExplorer mTouchExplorer;
+    private Context mContext;
+    private int mTouchSlop;
     private long mLastDownTime = Integer.MIN_VALUE;
 
     // mock package-private GestureManifold class
@@ -121,12 +124,13 @@
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
-        Context context = InstrumentationRegistry.getContext();
-        AccessibilityManagerService ams = new AccessibilityManagerService(context);
+        mContext = InstrumentationRegistry.getContext();
+        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        AccessibilityManagerService ams = new AccessibilityManagerService(mContext);
         GestureManifold detector = mock(GestureManifold.class);
         mCaptor = new EventCaptor();
         mHandler = new TestHandler();
-        mTouchExplorer = new TouchExplorer(context, ams, detector, mHandler);
+        mTouchExplorer = new TouchExplorer(mContext, ams, detector, mHandler);
         mTouchExplorer.setNext(mCaptor);
     }
 
@@ -354,12 +358,12 @@
                     break;
                 case STATE_DRAGGING_2FINGERS:
                     goFromStateClearTo(STATE_TOUCH_EXPLORING_2FINGER);
-                    moveEachPointers(mLastEvent, p(10, 0), p(10, 0));
+                    moveEachPointers(mLastEvent, p(mTouchSlop, 0), p(mTouchSlop, 0));
                     send(mLastEvent);
                     break;
                 case STATE_PINCH_2FINGERS:
                     goFromStateClearTo(STATE_DRAGGING_2FINGERS);
-                    moveEachPointers(mLastEvent, p(10, 0), p(-10, 1));
+                    moveEachPointers(mLastEvent, p(mTouchSlop, 0), p(-mTouchSlop, 1));
                     send(mLastEvent);
                     break;
                 case STATE_MOVING_3FINGERS:
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 9a465a91..8fc2287 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1604,7 +1604,7 @@
         dpm.setApplicationRestrictionsManagingPackage(admin1, RESTRICTIONS_DELEGATE);
 
         // DPMS correctly stores and retrieves the delegates
-        DevicePolicyManagerService.DevicePolicyData policy = dpms.mUserData.get(userHandle);
+        DevicePolicyData policy = dpms.mUserData.get(userHandle);
         assertEquals(2, policy.mDelegationMap.size());
         MoreAsserts.assertContentsInAnyOrder(policy.mDelegationMap.get(CERT_DELEGATE),
             DELEGATION_CERT_INSTALL);
@@ -1846,11 +1846,11 @@
         reset(getServices().userManagerInternal);
     }
 
-    private DevicePolicyManagerService.ActiveAdmin getDeviceOwner() {
+    private ActiveAdmin getDeviceOwner() {
         ComponentName component = dpms.mOwners.getDeviceOwnerComponent();
-        DevicePolicyManagerService.DevicePolicyData policy =
+        DevicePolicyData policy =
                 dpms.getUserData(dpms.mOwners.getDeviceOwnerUserId());
-        for (DevicePolicyManagerService.ActiveAdmin admin : policy.mAdminList) {
+        for (ActiveAdmin admin : policy.mAdminList) {
             if (component.equals(admin.info.getComponent())) {
                 return admin;
             }
@@ -3745,8 +3745,7 @@
         setUserSetupCompleteForUser(false, userId);
 
         // GIVEN userComplete is true in DPM
-        DevicePolicyManagerService.DevicePolicyData userData =
-                new DevicePolicyManagerService.DevicePolicyData(userId);
+        DevicePolicyData userData = new DevicePolicyData(userId);
         userData.mUserSetupComplete = true;
         dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
 
@@ -3770,8 +3769,7 @@
         setUserSetupCompleteForUser(false, userId);
 
         // GIVEN userComplete is true in DPM
-        DevicePolicyManagerService.DevicePolicyData userData =
-                new DevicePolicyManagerService.DevicePolicyData(userId);
+        DevicePolicyData userData = new DevicePolicyData(userId);
         userData.mUserSetupComplete = true;
         dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 960a7ab..ef2365e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -16,7 +16,6 @@
 package com.android.server.hdmi;
 
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
-import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 
@@ -62,6 +61,7 @@
     private TestLooper mTestLooper = new TestLooper();
     private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
     private int mPlaybackPhysicalAddress;
+    private int mPlaybackLogicalAddress;
     private boolean mWokenUp;
     private boolean mStandby;
 
@@ -129,6 +129,7 @@
         mPlaybackPhysicalAddress = 0x2000;
         mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
         mTestLooper.dispatchAll();
+        mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
         mNativeWrapper.clearResultMessages();
     }
 
@@ -144,7 +145,7 @@
                         mPlaybackPhysicalAddress);
 
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
 
         assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue();
@@ -165,7 +166,7 @@
                         mPlaybackPhysicalAddress);
 
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
 
         assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue();
@@ -186,7 +187,7 @@
                         mPlaybackPhysicalAddress);
 
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
 
         assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue();
@@ -207,7 +208,7 @@
                         mPlaybackPhysicalAddress);
 
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
 
         assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue();
@@ -230,7 +231,7 @@
                         mPlaybackPhysicalAddress);
 
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
 
         assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue();
@@ -253,7 +254,7 @@
                         mPlaybackPhysicalAddress);
 
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
 
         assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue();
@@ -262,6 +263,112 @@
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
 
+    @Test
+    public void handleRoutingChange_otherDevice_None() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.NONE;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isFalse();
+    }
+
+    @Test
+    public void handleRoutingChange_otherDevice_StandbyNow() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isTrue();
+    }
+
+    @Test
+    public void handleRoutingChange_otherDevice_StandbyNow_InactiveSource() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(false);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isFalse();
+    }
+
+    @Test
+    public void handleRoutingChange_sameDevice_StandbyNow_ActiveSource() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue();
+        assertThat(mStandby).isFalse();
+    }
+
+    @Test
+    public void handleRoutingInformation_otherDevice_None() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.NONE;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isFalse();
+    }
+
+    @Test
+    public void handleRoutingInformation_otherDevice_StandbyNow() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isTrue();
+    }
+
+    @Test
+    public void handleRoutingInformation_otherDevice_StandbyNow_InactiveSource() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(false);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isFalse();
+    }
+
+    @Test
+    public void handleRoutingInformation_sameDevice_StandbyNow_ActiveSource() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue();
+        assertThat(mStandby).isFalse();
+    }
+
     // Playback device does not handle routing control related feature right now
     @Ignore("b/120845532")
     @Test
@@ -442,7 +549,7 @@
         mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
             HdmiProperties.power_state_change_on_active_source_lost_values.NONE;
         mStandby = false;
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                                          mPlaybackPhysicalAddress);
         assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message)).isTrue();
         mTestLooper.dispatchAll();
@@ -465,7 +572,7 @@
         mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
             HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
         mStandby = false;
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                                          mPlaybackPhysicalAddress);
         assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message)).isTrue();
         mTestLooper.dispatchAll();
@@ -629,4 +736,43 @@
                 mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress());
         assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue();
     }
+
+    @Test
+    public void handleSetStreamPath_otherDevice_None() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.NONE;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isFalse();
+    }
+
+    @Test
+    public void handleSetStreamPath_otherDevice_StandbyNow() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(true);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isTrue();
+    }
+
+    @Test
+    public void handleSetStreamPath_otherDevice_StandbyNow_InactiveSource() {
+        mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost =
+            HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW;
+        mHdmiCecLocalDevicePlayback.setIsActiveSource(false);
+        mStandby = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mStandby).isFalse();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 12b144f..1f66c7c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -129,7 +129,7 @@
         mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID);
         // verify credential
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                firstUnifiedPassword, 0, PRIMARY_USER_ID)
+                firstUnifiedPassword, PRIMARY_USER_ID, 0 /* flags */)
                 .getResponseCode());
 
         // Verify that we have a new auth token for the profile
@@ -186,13 +186,13 @@
         mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID);
         // verify primary credential
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                primaryPassword, 0, PRIMARY_USER_ID)
+                primaryPassword, PRIMARY_USER_ID, 0 /* flags */)
                 .getResponseCode());
         assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
 
         // verify profile credential
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                profilePassword, 0, MANAGED_PROFILE_USER_ID)
+                profilePassword, MANAGED_PROFILE_USER_ID, 0 /* flags */)
                 .getResponseCode());
         assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
         assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
@@ -203,7 +203,7 @@
                 newPassword("pwd"), primaryPassword, PRIMARY_USER_ID));
         mStorageManager.setIgnoreBadUnlock(false);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                profilePassword, 0, MANAGED_PROFILE_USER_ID)
+                profilePassword, MANAGED_PROFILE_USER_ID, 0 /* flags */)
                 .getResponseCode());
         assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
     }
@@ -389,7 +389,7 @@
         initializeStorageWithCredential(PRIMARY_USER_ID, password, 1234);
         reset(mRecoverableKeyStoreManager);
 
-        mService.verifyCredential(password, 1, PRIMARY_USER_ID);
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */);
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretAvailable(
@@ -406,7 +406,7 @@
                 MANAGED_PROFILE_USER_ID));
         reset(mRecoverableKeyStoreManager);
 
-        mService.verifyCredential(pattern, 1, MANAGED_PROFILE_USER_ID);
+        mService.verifyCredential(pattern, MANAGED_PROFILE_USER_ID, 0 /* flags */);
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretAvailable(
@@ -421,7 +421,7 @@
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
         reset(mRecoverableKeyStoreManager);
 
-        mService.verifyCredential(pattern, 1, PRIMARY_USER_ID);
+        mService.verifyCredential(pattern, PRIMARY_USER_ID, 0 /* flags */);
 
         // Parent sends its credentials for both the parent and profile.
         verify(mRecoverableKeyStoreManager)
@@ -484,9 +484,8 @@
 
     private void assertVerifyCredentials(int userId, LockscreenCredential credential, long sid)
             throws RemoteException{
-        final long challenge = 54321;
-        VerifyCredentialResponse response = mService.verifyCredential(credential,
-                challenge, userId);
+        VerifyCredentialResponse response = mService.verifyCredential(credential, userId,
+                0 /* flags */);
 
         assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode());
         if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId));
@@ -508,7 +507,7 @@
             badCredential = LockscreenCredential.createPin("0");
         }
         assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(
-                badCredential, challenge, userId).getResponseCode());
+                badCredential, userId, 0 /* flags */).getResponseCode());
     }
 
     private void initializeStorageWithCredential(int userId, LockscreenCredential credential,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
index 4b3f7b5..9c0239b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
@@ -52,7 +52,8 @@
 
         assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP));
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
-                mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+                mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */)
+                        .getResponseCode());
     }
 
     @Test
@@ -61,7 +62,8 @@
 
         assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP));
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
-                mService.verifyCredential(newPattern("4321"), 0, USER_FRP).getResponseCode());
+                mService.verifyCredential(newPattern("4321"), USER_FRP, 0 /* flags */)
+                        .getResponseCode());
     }
 
     @Test
@@ -70,7 +72,8 @@
 
         assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP));
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
-                mService.verifyCredential(newPassword("4321"), 0, USER_FRP).getResponseCode());
+                mService.verifyCredential(newPassword("4321"), USER_FRP, 0 /* flags */)
+                        .getResponseCode());
     }
 
     @Test
@@ -80,7 +83,8 @@
 
         assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP));
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
-                mService.verifyCredential(newPattern("5678"), 0, USER_FRP).getResponseCode());
+                mService.verifyCredential(newPattern("5678"), USER_FRP, 0 /* flags */)
+                        .getResponseCode());
     }
 
     @Test
@@ -98,7 +102,8 @@
 
         mSettings.setDeviceProvisioned(true);
         assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
-                mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+                mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */)
+                        .getResponseCode());
     }
 
     @Test
@@ -113,7 +118,8 @@
 
         assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP));
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
-                mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+                mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */)
+                        .getResponseCode());
 
     }
 
@@ -129,6 +135,7 @@
 
         assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP));
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
-                mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+                mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */)
+                        .getResponseCode());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index ba85199..2bd0e55 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -119,8 +119,7 @@
         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         mService.setLockCredential(newPassword, password, PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                newPassword, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                newPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
     }
 
@@ -131,12 +130,10 @@
 
         initializeCredentialUnderSP(password, PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                password, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
 
         assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
-                badPassword, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                badPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
     }
 
     @Test
@@ -153,8 +150,7 @@
         // set a new password
         mService.setLockCredential(badPassword, nonePassword(), PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                badPassword, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                badPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
     }
 
@@ -166,8 +162,7 @@
         initializeCredentialUnderSP(password, PRIMARY_USER_ID);
         mService.setLockCredential(badPassword, password, PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                badPassword, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                badPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
 
         // Check the same secret was passed each time
         ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class);
@@ -183,8 +178,7 @@
         initializeCredentialUnderSP(password, PRIMARY_USER_ID);
         reset(mAuthSecretService);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                password, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
     }
 
@@ -194,8 +188,7 @@
 
         initializeCredentialUnderSP(password, SECONDARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                password, 0, SECONDARY_USER_ID)
-                        .getResponseCode());
+                password, SECONDARY_USER_ID, 0 /* flags */).getResponseCode());
         verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
     }
 
@@ -246,7 +239,8 @@
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
 
-        mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
+                .getResponseCode();
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
 
@@ -259,8 +253,7 @@
         verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                pattern, 0, PRIMARY_USER_ID)
-                    .getResponseCode());
+                pattern, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
     }
 
@@ -275,7 +268,8 @@
         long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
+                .getResponseCode();
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mLocalService.setLockCredentialWithToken(nonePassword(), handle, token, PRIMARY_USER_ID);
@@ -284,8 +278,7 @@
                 PRIMARY_USER_ID);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                pattern, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                pattern, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
     }
 
@@ -301,7 +294,8 @@
         long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
+                .getResponseCode();
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mService.setLockCredential(pattern, password, PRIMARY_USER_ID);
@@ -309,8 +303,7 @@
         mLocalService.setLockCredentialWithToken(newPassword, handle, token, PRIMARY_USER_ID);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                newPassword, 0, PRIMARY_USER_ID)
-                    .getResponseCode());
+                newPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
     }
 
@@ -357,8 +350,7 @@
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         // Activate token (password gets migrated to SP at the same time)
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                password, 0, PRIMARY_USER_ID)
-                    .getResponseCode());
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         // Verify token is activated
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
     }
@@ -488,8 +480,7 @@
 
         initializeCredentialUnderSP(password, PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
-                password, 0, PRIMARY_USER_ID)
-                        .getResponseCode());
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
     }
 
@@ -503,7 +494,8 @@
         reset(mDevicePolicyManager);
 
         long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
-        mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
+                .getResponseCode();
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mService.onCleanupUser(PRIMARY_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 128177b..58b71d4 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -462,7 +462,7 @@
 
     @Test
     public void testTurnRestrictBackgroundOn() throws Exception {
-        assertRestrictBackgroundOff(); // Sanity check.
+        assertRestrictBackgroundOff();
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(futureIntent, null);
@@ -471,7 +471,7 @@
     @Test
     @NetPolicyXml("restrict-background-on.xml")
     public void testTurnRestrictBackgroundOff() throws Exception {
-        assertRestrictBackgroundOn(); // Sanity check.
+        assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
         setRestrictBackground(false);
@@ -479,28 +479,27 @@
     }
 
     /**
-     * Adds whitelist when restrict background is on - app should receive an intent.
+     * Adds allowlist when restrict background is on - app should receive an intent.
      */
     @Test
     @NetPolicyXml("restrict-background-on.xml")
-    public void testAddRestrictBackgroundWhitelist_restrictBackgroundOn() throws Exception {
-        assertRestrictBackgroundOn(); // Sanity check.
+    public void testAddRestrictBackgroundAllowlist_restrictBackgroundOn() throws Exception {
+        assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
-        addRestrictBackgroundWhitelist(true);
+        addRestrictBackgroundAllowlist(true);
     }
 
     /**
-     * Adds whitelist when restrict background is off - app should not receive an intent.
+     * Adds allowlist when restrict background is off - app should not receive an intent.
      */
     @Test
-    public void testAddRestrictBackgroundWhitelist_restrictBackgroundOff() throws Exception {
-        assertRestrictBackgroundOff(); // Sanity check.
-        addRestrictBackgroundWhitelist(false);
+    public void testAddRestrictBackgroundAllowlist_restrictBackgroundOff() throws Exception {
+        assertRestrictBackgroundOff();
+        addRestrictBackgroundAllowlist(false);
     }
 
-    private void addRestrictBackgroundWhitelist(boolean expectIntent) throws Exception {
-        // Sanity checks.
-        assertWhitelistUids();
+    private void addRestrictBackgroundAllowlist(boolean expectIntent) throws Exception {
+        assertAllowlistUids();
         assertUidPolicy(UID_A, POLICY_NONE);
 
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
@@ -508,7 +507,7 @@
 
         mService.setUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
 
-        assertWhitelistUids(UID_A);
+        assertAllowlistUids(UID_A);
         assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
         mPolicyListener.waitAndVerify()
                 .onUidPoliciesChanged(APP_ID_A, POLICY_ALLOW_METERED_BACKGROUND);
@@ -520,24 +519,24 @@
     }
 
     /**
-     * Removes whitelist when restrict background is on - app should receive an intent.
+     * Removes allowlist when restrict background is on - app should receive an intent.
      */
     @Test
-    @NetPolicyXml("uidA-whitelisted-restrict-background-on.xml")
-    public void testRemoveRestrictBackgroundWhitelist_restrictBackgroundOn() throws Exception {
-        assertRestrictBackgroundOn(); // Sanity check.
+    @NetPolicyXml("uidA-allowlisted-restrict-background-on.xml")
+    public void testRemoveRestrictBackgroundAllowlist_restrictBackgroundOn() throws Exception {
+        assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
-        removeRestrictBackgroundWhitelist(true);
+        removeRestrictBackgroundAllowlist(true);
     }
 
     /**
-     * Removes whitelist when restrict background is off - app should not receive an intent.
+     * Removes allowlist when restrict background is off - app should not receive an intent.
      */
     @Test
-    @NetPolicyXml("uidA-whitelisted-restrict-background-off.xml")
-    public void testRemoveRestrictBackgroundWhitelist_restrictBackgroundOff() throws Exception {
-        assertRestrictBackgroundOff(); // Sanity check.
-        removeRestrictBackgroundWhitelist(false);
+    @NetPolicyXml("uidA-allowlisted-restrict-background-off.xml")
+    public void testRemoveRestrictBackgroundAllowlist_restrictBackgroundOff() throws Exception {
+        assertRestrictBackgroundOff();
+        removeRestrictBackgroundAllowlist(false);
     }
 
     @Test
@@ -688,9 +687,8 @@
         assertFalse(mService.getRestrictBackground());
     }
 
-    private void removeRestrictBackgroundWhitelist(boolean expectIntent) throws Exception {
-        // Sanity checks.
-        assertWhitelistUids(UID_A);
+    private void removeRestrictBackgroundAllowlist(boolean expectIntent) throws Exception {
+        assertAllowlistUids(UID_A);
         assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
 
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
@@ -698,7 +696,7 @@
 
         mService.setUidPolicy(UID_A, POLICY_NONE);
 
-        assertWhitelistUids();
+        assertAllowlistUids();
         assertUidPolicy(UID_A, POLICY_NONE);
         mPolicyListener.waitAndVerify().onUidPoliciesChanged(APP_ID_A, POLICY_NONE);
         if (expectIntent) {
@@ -709,27 +707,27 @@
     }
 
     /**
-     * Adds blacklist when restrict background is on - app should not receive an intent.
+     * Adds denylist when restrict background is on - app should not receive an intent.
      */
     @Test
     @NetPolicyXml("restrict-background-on.xml")
-    public void testAddRestrictBackgroundBlacklist_restrictBackgroundOn() throws Exception {
-        assertRestrictBackgroundOn(); // Sanity check.
+    public void testAddRestrictBackgroundDenylist_restrictBackgroundOn() throws Exception {
+        assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
-        addRestrictBackgroundBlacklist(false);
+        addRestrictBackgroundDenylist(false);
     }
 
     /**
-     * Adds blacklist when restrict background is off - app should receive an intent.
+     * Adds denylist when restrict background is off - app should receive an intent.
      */
     @Test
-    public void testAddRestrictBackgroundBlacklist_restrictBackgroundOff() throws Exception {
-        assertRestrictBackgroundOff(); // Sanity check.
-        addRestrictBackgroundBlacklist(true);
+    public void testAddRestrictBackgroundDenylist_restrictBackgroundOff() throws Exception {
+        assertRestrictBackgroundOff();
+        addRestrictBackgroundDenylist(true);
     }
 
-    private void addRestrictBackgroundBlacklist(boolean expectIntent) throws Exception {
-        assertUidPolicy(UID_A, POLICY_NONE); // Sanity check.
+    private void addRestrictBackgroundDenylist(boolean expectIntent) throws Exception {
+        assertUidPolicy(UID_A, POLICY_NONE);
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
         mPolicyListener.expect().onUidPoliciesChanged(anyInt(), anyInt());
 
@@ -746,28 +744,28 @@
     }
 
     /**
-     * Removes blacklist when restrict background is on - app should not receive an intent.
+     * Removes denylist when restrict background is on - app should not receive an intent.
      */
     @Test
-    @NetPolicyXml("uidA-blacklisted-restrict-background-on.xml")
-    public void testRemoveRestrictBackgroundBlacklist_restrictBackgroundOn() throws Exception {
-        assertRestrictBackgroundOn(); // Sanity check.
+    @NetPolicyXml("uidA-denylisted-restrict-background-on.xml")
+    public void testRemoveRestrictBackgroundDenylist_restrictBackgroundOn() throws Exception {
+        assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
-        removeRestrictBackgroundBlacklist(false);
+        removeRestrictBackgroundDenylist(false);
     }
 
     /**
-     * Removes blacklist when restrict background is off - app should receive an intent.
+     * Removes denylist when restrict background is off - app should receive an intent.
      */
     @Test
-    @NetPolicyXml("uidA-blacklisted-restrict-background-off.xml")
-    public void testRemoveRestrictBackgroundBlacklist_restrictBackgroundOff() throws Exception {
-        assertRestrictBackgroundOff(); // Sanity check.
-        removeRestrictBackgroundBlacklist(true);
+    @NetPolicyXml("uidA-denylisted-restrict-background-off.xml")
+    public void testRemoveRestrictBackgroundDenylist_restrictBackgroundOff() throws Exception {
+        assertRestrictBackgroundOff();
+        removeRestrictBackgroundDenylist(true);
     }
 
-    private void removeRestrictBackgroundBlacklist(boolean expectIntent) throws Exception {
-        assertUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); // Sanity check.
+    private void removeRestrictBackgroundDenylist(boolean expectIntent) throws Exception {
+        assertUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
         mPolicyListener.expect().onUidPoliciesChanged(anyInt(), anyInt());
 
@@ -784,9 +782,8 @@
     }
 
     @Test
-    @NetPolicyXml("uidA-blacklisted-restrict-background-on.xml")
-    public void testBlacklistedAppIsNotNotifiedWhenRestrictBackgroundIsOn() throws Exception {
-        // Sanity checks.
+    @NetPolicyXml("uidA-denylisted-restrict-background-on.xml")
+    public void testDenylistedAppIsNotNotifiedWhenRestrictBackgroundIsOn() throws Exception {
         assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
         assertUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
@@ -797,12 +794,11 @@
     }
 
     @Test
-    @NetPolicyXml("uidA-whitelisted-restrict-background-on.xml")
-    public void testWhitelistedAppIsNotNotifiedWhenRestrictBackgroundIsOn() throws Exception {
-        // Sanity checks.
+    @NetPolicyXml("uidA-allowlisted-restrict-background-on.xml")
+    public void testAllowlistedAppIsNotNotifiedWhenRestrictBackgroundIsOn() throws Exception {
         assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
-        assertWhitelistUids(UID_A);
+        assertAllowlistUids(UID_A);
 
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
         setRestrictBackground(true);
@@ -810,12 +806,11 @@
     }
 
     @Test
-    @NetPolicyXml("uidA-whitelisted-restrict-background-on.xml")
-    public void testWhitelistedAppIsNotifiedWhenBlacklisted() throws Exception {
-        // Sanity checks.
+    @NetPolicyXml("uidA-allowlisted-restrict-background-on.xml")
+    public void testAllowlistedAppIsNotifiedWhenDenylisted() throws Exception {
         assertRestrictBackgroundOn();
         assertRestrictBackgroundChangedReceived(mFutureIntent, null);
-        assertWhitelistUids(UID_A);
+        assertAllowlistUids(UID_A);
 
         final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
         mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
@@ -823,8 +818,8 @@
     }
 
     @Test
-    @NetPolicyXml("restrict-background-lists-whitelist-format.xml")
-    public void testRestrictBackgroundLists_whitelistFormat() throws Exception {
+    @NetPolicyXml("restrict-background-lists-allowlist-format.xml")
+    public void testRestrictBackgroundLists_allowlistFormat() throws Exception {
         restrictBackgroundListsTest();
     }
 
@@ -835,33 +830,33 @@
     }
 
     private void restrictBackgroundListsTest() throws Exception {
-        // UIds that are whitelisted.
-        assertWhitelistUids(UID_A, UID_B, UID_C);
+        // UIds that are allowlisted.
+        assertAllowlistUids(UID_A, UID_B, UID_C);
         assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
         assertUidPolicy(UID_B, POLICY_ALLOW_METERED_BACKGROUND);
         assertUidPolicy(UID_C, POLICY_ALLOW_METERED_BACKGROUND);
 
-        // UIDs that are blacklisted.
+        // UIDs that are denylisted.
         assertUidPolicy(UID_D, POLICY_NONE);
         assertUidPolicy(UID_E, POLICY_REJECT_METERED_BACKGROUND);
 
         // UIDS that have legacy policies.
         assertUidPolicy(UID_F, 2); // POLICY_ALLOW_BACKGROUND_BATTERY_SAVE
 
-        // Remove whitelist.
+        // Remove allowlist.
         mService.setUidPolicy(UID_A, POLICY_NONE);
         assertUidPolicy(UID_A, POLICY_NONE);
-        assertWhitelistUids(UID_B, UID_C);
+        assertAllowlistUids(UID_B, UID_C);
 
-        // Add whitelist when blacklisted.
+        // Add allowlist when denylisted.
         mService.setUidPolicy(UID_E, POLICY_ALLOW_METERED_BACKGROUND);
         assertUidPolicy(UID_E, POLICY_ALLOW_METERED_BACKGROUND);
-        assertWhitelistUids(UID_B, UID_C, UID_E);
+        assertAllowlistUids(UID_B, UID_C, UID_E);
 
-        // Add blacklist when whitelisted.
+        // Add denylist when allowlisted.
         mService.setUidPolicy(UID_B, POLICY_REJECT_METERED_BACKGROUND);
         assertUidPolicy(UID_B, POLICY_REJECT_METERED_BACKGROUND);
-        assertWhitelistUids(UID_C, UID_E);
+        assertAllowlistUids(UID_C, UID_E);
     }
 
     /**
@@ -870,9 +865,9 @@
     @Test
     @NetPolicyXml("restrict-background-lists-mixed-format.xml")
     public void testRestrictBackgroundLists_mixedFormat() throws Exception {
-        assertWhitelistUids(UID_A, UID_C, UID_D);
+        assertAllowlistUids(UID_A, UID_C, UID_D);
         assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
-        assertUidPolicy(UID_B, POLICY_REJECT_METERED_BACKGROUND); // Blacklist prevails.
+        assertUidPolicy(UID_B, POLICY_REJECT_METERED_BACKGROUND); // Denylist prevails.
         assertUidPolicy(UID_C, (POLICY_ALLOW_METERED_BACKGROUND | 2));
         assertUidPolicy(UID_D, POLICY_ALLOW_METERED_BACKGROUND);
     }
@@ -2045,7 +2040,7 @@
         }
     }
 
-    private void assertWhitelistUids(int... uids) {
+    private void assertAllowlistUids(int... uids) {
         assertContainsInAnyOrder(mService.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND), uids);
     }
 
@@ -2133,7 +2128,6 @@
 
     private void setRestrictBackground(boolean flag) throws Exception {
         mService.setRestrictBackground(flag);
-        // Sanity check.
         assertEquals("restrictBackground not set", flag, mService.getRestrictBackground());
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index f991dff..b2512d3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -69,6 +69,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 @Presubmit
 @RunWith(JUnit4.class)
@@ -88,6 +89,8 @@
     AppsFilter.FeatureConfig mFeatureConfigMock;
     @Mock
     AppsFilter.StateProvider mStateProvider;
+    @Mock
+    Executor mMockExecutor;
 
     private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
 
@@ -184,10 +187,15 @@
         doAnswer(invocation -> {
             ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0))
                     .currentState(mExisting, USER_INFO_LIST);
-            return null;
+            return new Object();
         }).when(mStateProvider)
                 .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class));
 
+        doAnswer(invocation -> {
+            ((Runnable) invocation.getArgument(0)).run();
+            return new Object();
+        }).when(mMockExecutor).execute(any(Runnable.class));
+
         when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
         when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer(
                 (Answer<Boolean>) invocation ->
@@ -198,7 +206,8 @@
     @Test
     public void testSystemReadyPropogates() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         appsFilter.onSystemReady();
         verify(mFeatureConfigMock).onSystemReady();
     }
@@ -206,7 +215,8 @@
     @Test
     public void testQueriesAction_FilterMatches() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -222,7 +232,8 @@
     @Test
     public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         final Signature frameworkSignature = Mockito.mock(Signature.class);
         final PackageParser.SigningDetails frameworkSigningDetails =
                 new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
@@ -260,7 +271,8 @@
     @Test
     public void testQueriesProvider_FilterMatches() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -277,7 +289,8 @@
     @Test
     public void testQueriesDifferentProvider_Filters() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -294,7 +307,8 @@
     @Test
     public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -312,7 +326,8 @@
     @Test
     public void testQueriesAction_NoMatchingAction_Filters() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -328,7 +343,8 @@
     @Test
     public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -348,7 +364,8 @@
     @Test
     public void testNoQueries_Filters() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -364,7 +381,8 @@
     @Test
     public void testForceQueryable_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -381,7 +399,7 @@
     public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
-                        false, null);
+                        false, null, mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -399,7 +417,8 @@
     @Test
     public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         appsFilter.onSystemReady();
 
         final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -428,7 +447,7 @@
     public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
-                        false, null);
+                        false, null, mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -446,7 +465,7 @@
     public void testSystemQueryable_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{},
-                        true /* system force queryable */, null);
+                        true /* system force queryable */, null, mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -463,7 +482,8 @@
     @Test
     public void testQueriesPackage_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -481,7 +501,8 @@
         when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
                 .thenReturn(false);
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -497,7 +518,8 @@
     @Test
     public void testSystemUid_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -512,7 +534,8 @@
     @Test
     public void testSystemUidSecondaryUser_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -528,7 +551,8 @@
     @Test
     public void testNonSystemUid_NoCallingSetting_Filters() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -542,7 +566,8 @@
     @Test
     public void testNoTargetPackage_filters() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -550,7 +575,6 @@
                 .setAppId(DUMMY_TARGET_APPID)
                 .setName("com.some.package")
                 .setCodePath("/")
-                .setResourcePath("/")
                 .setPVersionCode(1L)
                 .build();
         PackageSetting calling = simulateAddPackage(appsFilter,
@@ -600,7 +624,8 @@
                         }
                         return Collections.emptyMap();
                     }
-                });
+                },
+                mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -672,7 +697,8 @@
                         }
                         return Collections.emptyMap();
                     }
-                });
+                },
+                mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -697,7 +723,8 @@
     @Test
     public void testInitiatingApp_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -713,7 +740,8 @@
     @Test
     public void testUninstalledInitiatingApp_Filters() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -729,7 +757,8 @@
     @Test
     public void testOriginatingApp_Filters() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -745,7 +774,8 @@
     @Test
     public void testInstallingApp_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -761,7 +791,8 @@
     @Test
     public void testInstrumentation_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -783,7 +814,8 @@
     @Test
     public void testWhoCanSee() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -874,7 +906,6 @@
                 .setAppId(appId)
                 .setName(newPkg.getPackageName())
                 .setCodePath("/")
-                .setResourcePath("/")
                 .setPVersionCode(1L);
         final PackageSetting setting =
                 (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 164bd72..90edaef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -38,8 +38,7 @@
 
     public PackageSetting generateFakePackageSetting(String name) {
         return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
-                new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
-                "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
+                "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/);
     }
 
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 c4e25b5..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,
@@ -86,8 +86,7 @@
         // Create a real (non-null) PackageSetting and confirm that the removed
         // users are copied properly
         setting = new PackageSetting("name", "realName", new File("codePath"),
-                new File("resourcePath"), "legacyNativeLibraryPathString",
-                "primaryCpuAbiString", "secondaryCpuAbiString",
+                "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString",
                 "cpuAbiOverrideString", 0, 0, 0, 0,
                 null, null, null);
         pri.populateUsers(new int[] {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index aa92ba4..0bf06bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -439,7 +439,6 @@
                 PACKAGE_NAME,
                 REAL_PACKAGE_NAME,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -461,7 +460,6 @@
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -477,7 +475,6 @@
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 null /*primaryCpuAbiString*/,
                 null /*secondaryCpuAbiString*/,
@@ -507,7 +504,6 @@
                 null /*disabledPkg*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -541,7 +537,6 @@
                 null /*disabledPkg*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -581,7 +576,6 @@
                     null /*disabledPkg*/,
                     testUserSetting01 /*sharedUser*/,
                     UPDATED_CODE_PATH /*codePath*/,
-                    null /*resourcePath*/,
                     null /*legacyNativeLibraryPath*/,
                     "arm64-v8a" /*primaryCpuAbi*/,
                     "armeabi" /*secondaryCpuAbi*/,
@@ -609,7 +603,6 @@
                 null /*realPkgName*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -624,12 +617,11 @@
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
-        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         // signatures object must be different
         assertNotSame(testPkgSetting01.signatures, originalSignatures);
@@ -649,7 +641,6 @@
                 null /*realPkgName*/,
                 null /*sharedUser*/,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -665,12 +656,11 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(0));
-        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
-        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
         assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
         // by default, the package is considered stopped
@@ -695,7 +685,6 @@
                 null /*realPkgName*/,
                 testUserSetting01 /*sharedUser*/,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -711,12 +700,11 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(10064));
-        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
-        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
         assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
         final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -738,7 +726,6 @@
                 null /*realPkgName*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -754,12 +741,11 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(10064));
-        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         assertNotSame(testPkgSetting01.signatures, disabledSignatures);
         assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
@@ -806,10 +792,10 @@
     private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
         assertThat(origPkgSetting, is(not(testPkgSetting)));
         assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
-        assertSame(origPkgSetting.codePath, testPkgSetting.codePath);
-        assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath));
-        assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString);
-        assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString));
+        assertSame(origPkgSetting.getCodePath(), testPkgSetting.getCodePath());
+        assertThat(origPkgSetting.getCodePath(), is(testPkgSetting.getCodePath()));
+        assertSame(origPkgSetting.getCodePathString(), testPkgSetting.getCodePathString());
+        assertThat(origPkgSetting.getCodePathString(), is(testPkgSetting.getCodePathString()));
         assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
         assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
         assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
@@ -823,7 +809,9 @@
                 testPkgSetting.legacyNativeLibraryPathString);
         assertThat(origPkgSetting.legacyNativeLibraryPathString,
                 is(testPkgSetting.legacyNativeLibraryPathString));
-        assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+        if (origPkgSetting.mimeGroups != null) {
+            assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+        }
         assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups));
         assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
         assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
@@ -839,10 +827,6 @@
         assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString);
         assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString));
         assertThat(origPkgSetting.realName, is(testPkgSetting.realName));
-        assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath);
-        assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath));
-        assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString);
-        assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString));
         assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString);
         assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString));
         assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser);
@@ -874,7 +858,6 @@
                 PACKAGE_NAME,
                 REAL_PACKAGE_NAME,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -893,7 +876,6 @@
                 packageName,
                 packageName,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index b0b5386..2651cfa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -312,8 +312,7 @@
 
     private static PackageSetting mockPkgSetting(AndroidPackage pkg) {
         return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(),
-                new File(pkg.getCodePath()), new File(pkg.getCodePath()), null,
-                pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
+                new File(pkg.getCodePath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
                 null, pkg.getVersionCode(),
                 PackageInfoUtils.appInfoFlags(pkg, null),
                 PackageInfoUtils.appInfoPrivateFlags(pkg, null),
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index d12ea894..f8e92ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -30,7 +30,6 @@
     private String mName;
     private String mRealName;
     private String mCodePath;
-    private String mResourcePath;
     private String mLegacyNativeLibraryPathString;
     private String mPrimaryCpuAbiString;
     private String mSecondaryCpuAbiString;
@@ -74,11 +73,6 @@
         return this;
     }
 
-    public PackageSettingBuilder setResourcePath(String resourcePath) {
-        this.mResourcePath = resourcePath;
-        return this;
-    }
-
     public PackageSettingBuilder setLegacyNativeLibraryPathString(
             String legacyNativeLibraryPathString) {
         this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString;
@@ -162,10 +156,10 @@
 
     public PackageSetting build() {
         final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
-                new File(mCodePath), new File(mResourcePath),
-                mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString,
-                mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId,
-                mUsesStaticLibraries, mUsesStaticLibrariesVersions, mMimeGroups);
+                new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
+                mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
+                mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
+                mMimeGroups);
         packageSetting.signatures = mSigningDetails != null
                 ? new PackageSignatures(mSigningDetails)
                 : new PackageSignatures();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 55bc681..7108490 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -465,9 +465,9 @@
         // Generic PackageSetting object with values from a test app installed on a device to be
         // used to test the methods under the PackageSignatures signatures data member.
         File appPath = new File("/data/app/app");
-        PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
-                "/data/app/app", null, null, null,
-                1, 940097092, 0, 0 /*userId*/, null, null, null /*mimeGroups*/);
+        PackageSetting result = new PackageSetting("test.app", null, appPath,
+                "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null,
+                null /*mimeGroups*/);
         return result;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index e7eff00..56dddb0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -484,8 +484,7 @@
     private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) {
         return new PackageSettingBuilder()
                 .setName(packageName)
-                .setCodePath(createCodePath(packageName))
-                .setResourcePath(createCodePath(packageName));
+                .setCodePath(createCodePath(packageName));
     }
 
     private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) {
@@ -534,8 +533,7 @@
                 arrayContaining("some.static.library", "some.other.static.library"));
         assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
         assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage));
-        assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName))));
-        assertThat(pkgSetting.resourcePath, is(new File(createCodePath(packageName))));
+        assertThat(pkgSetting.getCodePath(), is(new File(createCodePath(packageName))));
         assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
     }
 
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 1536345..8034cac 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -170,7 +170,7 @@
         ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
         try {
             mTimeZoneDetectorService.removeConfigurationListener(mockListener);
-            fail();
+            fail("Expected a SecurityException");
         } finally {
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 1d75967..88b1d19 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.Manifest;
 import android.app.AlarmManager;
 import android.app.IUiModeManager;
 import android.content.BroadcastReceiver;
@@ -24,6 +25,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -67,6 +69,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -230,6 +233,17 @@
     }
 
     @Test
+    public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException {
+        SystemService.TargetUser user = mock(SystemService.TargetUser.class);
+        doReturn(9).when(user).getUserIdentifier();
+        mUiManagerService.onUserSwitching(user, user);
+        when(mContext.checkCallingOrSelfPermission(
+                eq(Manifest.permission.INTERACT_ACROSS_USERS)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        assertFalse(mService.setNightModeActivated(true));
+    }
+
+    @Test
     public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
         mService.setNightMode(MODE_NIGHT_NO);
         when(mTwilightState.isNight()).thenReturn(false);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 980772b..5b2d738 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4856,6 +4856,70 @@
     }
 
     @Test
+    public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws
+            Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = true;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        // notifications from this package are blocked by the user
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+        setAppInForegroundForToasts(mUid, false);
+
+        // enqueue toast -> toast should still enqueue
+        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
+                new TestableToastCallback(), 2000, 0);
+        assertEquals(1, mService.mToastQueue.size());
+        verify(mAm).setProcessImportant(any(), anyInt(), eq(true), any());
+    }
+
+    @Test
+    public void foregroundTextToast_callsSetProcessImportantAsNotForegroundForToast() throws
+            Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        setAppInForegroundForToasts(mUid, true);
+
+        // enqueue toast -> toast should still enqueue
+        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+                "Text", 2000, 0, null);
+        assertEquals(1, mService.mToastQueue.size());
+        verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
+    }
+
+    @Test
+    public void backgroundTextToast_callsSetProcessImportantAsNotForegroundForToast() throws
+            Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        setAppInForegroundForToasts(mUid, false);
+
+        // enqueue toast -> toast should still enqueue
+        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+                "Text", 2000, 0, null);
+        assertEquals(1, mService.mToastQueue.size());
+        verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
+    }
+
+    @Test
     public void testTextToastsCallStatusBar() throws Exception {
         final String testPackage = "testPackageName";
         assertEquals(0, mService.mToastQueue.size());
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/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 4040fa6..e3c795d 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -38,6 +38,7 @@
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
 
     <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
     <application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e3830f6..f860e17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -22,6 +22,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -69,10 +73,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 
 import android.app.ActivityManager.TaskSnapshot;
@@ -84,6 +86,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
@@ -1503,7 +1506,7 @@
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                 WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
         params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT;
-        final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
+        final TestWindowState w = new TestWindowState(
                 mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
         mActivity.addWindow(w);
 
@@ -1685,6 +1688,32 @@
         assertFalse(mActivity.canTurnScreenOn());
     }
 
+    @Test
+    public void testGetLockTaskLaunchMode() {
+        final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true);
+        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+        assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED,
+                ActivityRecord.getLockTaskLaunchMode(mActivity.info, options));
+
+        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
+        assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT,
+                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+
+        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
+        assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT,
+                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+
+        mActivity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
+        assertEquals(LOCK_TASK_LAUNCH_MODE_ALWAYS,
+                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+
+        mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
+        assertEquals(LOCK_TASK_LAUNCH_MODE_NEVER,
+                ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+
+    }
+
     /**
      * Creates an activity on display. For non-default display request it will also create a new
      * display with custom DisplayInfo.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index addf1ff..96b9700 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -38,7 +38,13 @@
 
 import android.app.WaitResult;
 import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
 import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
 
 import androidx.test.filters.MediumTest;
 
@@ -173,4 +179,50 @@
         verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */,
                 eq(true) /* focused */);
     }
+
+    @Test
+    /** Ensures that a trusted virtual display can launch arbitrary activities. */
+    public void testTrustedVirtualDisplayCanLaunchActivities() {
+        final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+        final Task stack = new StackBuilder(mRootWindowContainer)
+                .setDisplay(newDisplay).build();
+        final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
+        VirtualDisplay virtualDisplay = createVirtualDisplay(true);
+        final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
+                virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
+
+        assertThat(allowed).isTrue();
+    }
+
+    @Test
+    /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */
+    public void testUntrustedVirtualDisplayCannotLaunchActivities() {
+        final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+        final Task stack = new StackBuilder(mRootWindowContainer)
+                .setDisplay(newDisplay).build();
+        final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
+        VirtualDisplay virtualDisplay = createVirtualDisplay(false);
+        final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
+                virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
+
+        assertThat(allowed).isFalse();
+    }
+
+    private VirtualDisplay createVirtualDisplay(boolean trusted) {
+        final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+        final DisplayInfo displayInfo = new DisplayInfo();
+        final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
+        defaultDisplay.getDisplayInfo(displayInfo);
+        int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+        if (trusted) {
+            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+        }
+
+        final ImageReader imageReader = ImageReader.newInstance(
+                displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+        return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth,
+                displayInfo.logicalHeight,
+                displayInfo.logicalDensityDpi, imageReader.getSurface(), flags);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 2e988af..5676100 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -225,5 +225,27 @@
 
         mockSession.finishMocking();
     }
+
+    @Test
+    public void testResumeNextActivityOnCrashedAppDied() {
+        mSupervisor.beginDeferResume();
+        final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
+                .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask())
+                .build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        mSupervisor.endDeferResume();
+        // Assume the activity is finishing and hidden because it was crashed.
+        activity.finishing = true;
+        activity.mVisibleRequested = false;
+        activity.setVisible(false);
+        activity.getRootTask().mPausingActivity = activity;
+        homeActivity.setState(Task.ActivityState.PAUSED, "test");
+
+        // Even the visibility states are invisible, the next activity should be resumed because
+        // the crashed activity was pausing.
+        mAtm.mInternal.handleAppDied(activity.app, false /* restarting */,
+                null /* finishInstrumentationCallback */);
+        assertEquals(Task.ActivityState.RESUMED, homeActivity.getState());
+    }
 }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 7adcead..1b21920 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -133,10 +133,10 @@
         // [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (opening, visible)
         //                   +- [TaskStack2] - [Task2] - [ActivityRecord2] (closing, invisible)
         final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
+        final ActivityRecord activity1 = createTestActivityRecord(stack1);
 
         final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
+        final ActivityRecord activity2 = createTestActivityRecord(stack2);
         activity2.setVisible(false);
         activity2.mVisibleRequested = false;
 
@@ -162,13 +162,13 @@
         // [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (closing, invisible)
         //                   +- [TaskStack2] - [Task2] - [ActivityRecord2] (opening, visible)
         final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
+        final ActivityRecord activity1 = createTestActivityRecord(stack1);
         activity1.setVisible(true);
         activity1.mVisibleRequested = true;
         activity1.mRequestForceTransition = true;
 
         final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
+        final ActivityRecord activity2 = createTestActivityRecord(stack2);
         activity2.setVisible(false);
         activity2.mVisibleRequested = false;
         activity2.mRequestForceTransition = true;
@@ -193,10 +193,10 @@
     @Test
     public void testGetAnimationTargets_exitingBeforeTransition() {
         // Create another non-empty task so the animation target won't promote to task display area.
-        WindowTestUtils.createTestActivityRecord(
+        createTestActivityRecord(
                 mDisplayContent.getDefaultTaskDisplayArea().getOrCreateRootHomeTask());
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(stack);
+        final ActivityRecord activity = createTestActivityRecord(stack);
         activity.setVisible(false);
         activity.mIsExiting = true;
 
@@ -218,20 +218,20 @@
         //                   +- [TaskStack2] - [Task2] - [ActivityRecord2] (closing, invisible)
         //                                                      +- [AppWindow2] (being-replaced)
         final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
+        final ActivityRecord activity1 = createTestActivityRecord(stack1);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow1");
-        final WindowTestUtils.TestWindowState appWindow1 = createWindowState(attrs, activity1);
+        final TestWindowState appWindow1 = createWindowState(attrs, activity1);
         appWindow1.mWillReplaceWindow = true;
         activity1.addWindow(appWindow1);
 
         final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
+        final ActivityRecord activity2 = createTestActivityRecord(stack2);
         activity2.setVisible(false);
         activity2.mVisibleRequested = false;
         attrs.setTitle("AppWindow2");
-        final WindowTestUtils.TestWindowState appWindow2 = createWindowState(attrs, activity2);
+        final TestWindowState appWindow2 = createWindowState(attrs, activity2);
         appWindow2.mWillReplaceWindow = true;
         activity2.addWindow(appWindow2);
 
@@ -262,21 +262,17 @@
         //                                              +- [ActivityRecord4] (invisible)
         final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-        final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task1);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task1);
         activity1.setVisible(false);
         activity1.mVisibleRequested = true;
-        final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task1);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task1);
         activity2.setVisible(false);
         activity2.mVisibleRequested = false;
 
         final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
         final Task task2 = createTaskInStack(stack2, 0 /* userId */);
-        final ActivityRecord activity3 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task2);
-        final ActivityRecord activity4 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task2);
+        final ActivityRecord activity3 = createActivityRecordInTask(mDisplayContent, task2);
+        final ActivityRecord activity4 = createActivityRecordInTask(mDisplayContent, task2);
         activity4.setVisible(false);
         activity4.mVisibleRequested = false;
 
@@ -303,12 +299,10 @@
         //                                          +- [ActivityRecord2] (closing, visible)
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task);
         activity1.setVisible(false);
         activity1.mVisibleRequested = true;
-        final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task);
 
         final ArraySet<ActivityRecord> opening = new ArraySet<>();
         opening.add(activity1);
@@ -337,22 +331,18 @@
 
         final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-        final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task1);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task1);
         activity1.setVisible(false);
         activity1.mVisibleRequested = true;
         activity1.setOccludesParent(false);
 
-        final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task1);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task1);
 
         final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
         final Task task2 = createTaskInStack(stack2, 0 /* userId */);
-        final ActivityRecord activity3 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task2);
+        final ActivityRecord activity3 = createActivityRecordInTask(mDisplayContent, task2);
         activity3.setOccludesParent(false);
-        final ActivityRecord activity4 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task2);
+        final ActivityRecord activity4 = createActivityRecordInTask(mDisplayContent, task2);
 
         final ArraySet<ActivityRecord> opening = new ArraySet<>();
         opening.add(activity1);
@@ -381,24 +371,20 @@
 
         final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-        final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task1);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task1);
         activity1.setVisible(false);
         activity1.mVisibleRequested = true;
         activity1.setOccludesParent(false);
 
-        final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task1);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task1);
         activity2.setVisible(false);
         activity2.mVisibleRequested = true;
 
         final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
         final Task task2 = createTaskInStack(stack2, 0 /* userId */);
-        final ActivityRecord activity3 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task2);
+        final ActivityRecord activity3 = createActivityRecordInTask(mDisplayContent, task2);
         activity3.setOccludesParent(false);
-        final ActivityRecord activity4 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task2);
+        final ActivityRecord activity4 = createActivityRecordInTask(mDisplayContent, task2);
 
         final ArraySet<ActivityRecord> opening = new ArraySet<>();
         opening.add(activity1);
@@ -425,13 +411,11 @@
         //                                 +- [Task2] - [ActivityRecord2] (closing, visible)
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
-        final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task1);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task1);
         activity1.setVisible(false);
         activity1.mVisibleRequested = true;
         final Task task2 = createTaskInStack(stack, 0 /* userId */);
-        final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(
-                mDisplayContent, task2);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task2);
 
         final ArraySet<ActivityRecord> opening = new ArraySet<>();
         opening.add(activity1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 17914e7..ee030af 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -151,8 +151,7 @@
 
         final Task stack1 = createTaskStackOnDisplay(dc1);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-        final ActivityRecord activity1 =
-                WindowTestUtils.createTestActivityRecord(dc1);
+        final ActivityRecord activity1 = createTestActivityRecord(dc1);
         task1.addChild(activity1, 0);
 
         // Simulate same app is during opening / closing transition set stage.
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 888935e..085b8de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -92,7 +92,7 @@
     public void setUp() throws Exception {
         mStack = createTaskStackOnDisplay(mDisplayContent);
         mTask = createTaskInStack(mStack, 0 /* userId */);
-        mActivity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        mActivity = createTestActivityRecord(mDisplayContent);
 
         mTask.addChild(mActivity, 0);
     }
@@ -165,7 +165,7 @@
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mActivity);
+        final TestWindowState appWindow = createWindowState(attrs, mActivity);
         mActivity.addWindow(appWindow);
 
         // Set initial orientation and update.
@@ -198,7 +198,7 @@
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("RotationByPolicy");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mActivity);
+        final TestWindowState appWindow = createWindowState(attrs, mActivity);
         mActivity.addWindow(appWindow);
 
         // Set initial orientation and update.
@@ -244,7 +244,7 @@
                 TYPE_BASE_APPLICATION);
         attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mActivity);
+        final TestWindowState appWindow = createWindowState(attrs, mActivity);
 
         // Add window with show when locked flag
         mActivity.addWindow(appWindow);
@@ -307,7 +307,7 @@
         assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation);
         assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation);
 
-        final ActivityRecord topActivity = WindowTestUtils.createTestActivityRecord(mStack);
+        final ActivityRecord topActivity = createTestActivityRecord(mStack);
         topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         assertEquals(Configuration.ORIENTATION_LANDSCAPE, displayConfig.orientation);
@@ -490,8 +490,7 @@
     }
 
     private ActivityRecord createTestActivityRecordForGivenTask(Task task) {
-        final ActivityRecord activity =
-                WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
         task.addChild(activity, 0);
         waitUntilHandlersIdle();
         return activity;
@@ -562,7 +561,7 @@
     public void testHasStartingWindow() {
         final WindowManager.LayoutParams attrs =
                 new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING);
-        final WindowTestUtils.TestWindowState startingWindow = createWindowState(attrs, mActivity);
+        final TestWindowState startingWindow = createWindowState(attrs, mActivity);
         mActivity.startingDisplayed = true;
         mActivity.addWindow(startingWindow);
         assertTrue("Starting window should be present", mActivity.hasStartingWindow());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 0cc6159..d54b4a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -326,7 +326,7 @@
         assertEquals(dc, stack.getDisplayContent());
 
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(dc);
+        final ActivityRecord activity = createTestActivityRecord(dc);
         task.addChild(activity, 0);
         assertEquals(dc, task.getDisplayContent());
         assertEquals(dc, activity.getDisplayContent());
@@ -397,16 +397,14 @@
         // Add stack with activity.
         final Task stack0 = createTaskStackOnDisplay(dc0);
         final Task task0 = createTaskInStack(stack0, 0 /* userId */);
-        final ActivityRecord activity =
-                WindowTestUtils.createTestActivityRecord(dc0);
+        final ActivityRecord activity = createTestActivityRecord(dc0);
         task0.addChild(activity, 0);
         dc0.configureDisplayPolicy();
         assertNotNull(dc0.mTapDetector);
 
         final Task stack1 = createTaskStackOnDisplay(dc1);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-        final ActivityRecord activity1 =
-                WindowTestUtils.createTestActivityRecord(dc0);
+        final ActivityRecord activity1 = createTestActivityRecord(dc0);
         task1.addChild(activity1, 0);
         dc1.configureDisplayPolicy();
         assertNotNull(dc1.mTapDetector);
@@ -1296,7 +1294,7 @@
         final ActivityRecord pinnedActivity = createActivityRecord(displayContent,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
         final Task pinnedTask = pinnedActivity.getRootTask();
-        final ActivityRecord homeActivity = WindowTestUtils.createTestActivityRecord(
+        final ActivityRecord homeActivity = createTestActivityRecord(
                 displayContent.getDefaultTaskDisplayArea().getOrCreateRootHomeTask());
         if (displayConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
             homeActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 4ea5b97..0675c6d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -48,12 +48,14 @@
 import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
 import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
@@ -874,6 +876,19 @@
     }
 
     @Test
+    public void testFixedRotationInsetsSourceFrame() {
+        mDisplayPolicy.beginLayoutLw(mFrames, mDisplayContent.getConfiguration().uiMode);
+        doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
+                .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
+        final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+        mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
+        final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+
+        assertEquals(DISPLAY_WIDTH, frame.width());
+        assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
+    }
+
+    @Test
     public void testScreenDecorWindows() {
         final WindowState decorWindow = spy(
                 createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow"));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index e18d93d..4536997 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -95,8 +95,7 @@
      * Creates a window state which can be used as a drop target.
      */
     private WindowState createDropTargetWindow(String name, int ownerId) {
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(
-                mDisplayContent);
+        final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
         final Task stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         final Task task = createTaskInStack(stack, ownerId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 5e83e66..085230d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -28,6 +28,9 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -40,6 +43,7 @@
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.util.IntArray;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.test.InsetsModeSession;
@@ -328,6 +332,27 @@
         assertNull(getController().getControlsForDispatch(app));
     }
 
+    @Test
+    public void testTransientVisibilityOfFixedRotationState() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final InsetsSourceProvider provider = getController().getSourceProvider(ITYPE_STATUS_BAR);
+        provider.setWindow(statusBar, null, null);
+
+        final InsetsState rotatedState = new InsetsState(app.getInsetsState(),
+                true /* copySources */);
+        spyOn(app.mToken);
+        doReturn(rotatedState).when(app.mToken).getFixedRotationTransformInsetsState();
+        assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+
+        provider.getSource().setVisible(false);
+        mDisplayContent.getInsetsPolicy().showTransient(
+                IntArray.wrap(new int[] { ITYPE_STATUS_BAR }));
+
+        assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR));
+        assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
+    }
+
     private InsetsStateController getController() {
         return mDisplayContent.getInsetsStateController();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index a137cde..044f819 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -168,8 +168,8 @@
 
     @Test
     public void testStartLockTaskMode_once() throws Exception {
-        // GIVEN a task record with whitelisted auth
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN a task record with allowlisted auth
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
 
         // WHEN calling setLockTaskMode for LOCKED mode without resuming
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
@@ -185,9 +185,9 @@
 
     @Test
     public void testStartLockTaskMode_twice() throws Exception {
-        // GIVEN two task records with whitelisted auth
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
 
         // WHEN calling setLockTaskMode for LOCKED mode on both tasks
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
@@ -205,7 +205,7 @@
 
     @Test
     public void testStartLockTaskMode_pinningRequest() {
-        // GIVEN a task record that is not whitelisted, i.e. with pinned auth
+        // GIVEN a task record that is not allowlisted, i.e. with pinned auth
         Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE);
 
         // WHEN calling startLockTaskMode
@@ -236,23 +236,23 @@
 
     @Test
     public void testLockTaskViolation() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN it's not a lock task violation to try and launch this task without clearing
         assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
 
-        // THEN it's a lock task violation to launch another task that is not whitelisted
+        // THEN it's a lock task violation to launch another task that is not allowlisted
         assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(
                 Task.LOCK_TASK_AUTH_PINNABLE)));
         // THEN it's a lock task violation to launch another task that is disallowed from lock task
         assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(
                 Task.LOCK_TASK_AUTH_DONT_LOCK)));
 
-        // THEN it's no a lock task violation to launch another task that is whitelisted
+        // THEN it's no a lock task violation to launch another task that is allowlisted
         assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
-                Task.LOCK_TASK_AUTH_WHITELISTED)));
+                Task.LOCK_TASK_AUTH_ALLOWLISTED)));
         assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
                 Task.LOCK_TASK_AUTH_LAUNCHABLE)));
         // THEN it's not a lock task violation to launch another task that is priv launchable
@@ -262,8 +262,8 @@
 
     @Test
     public void testLockTaskViolation_emergencyCall() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // GIVEN tasks necessary for emergency calling
@@ -294,8 +294,8 @@
 
     @Test
     public void testStopLockTaskMode() throws Exception {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN the same caller calls stopLockTaskMode
@@ -311,8 +311,8 @@
 
     @Test(expected = SecurityException.class)
     public void testStopLockTaskMode_differentCaller() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN a different caller calls stopLockTaskMode
@@ -323,8 +323,8 @@
 
     @Test
     public void testStopLockTaskMode_systemCaller() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN system calls stopLockTaskMode
@@ -336,9 +336,9 @@
 
     @Test
     public void testStopLockTaskMode_twoTasks() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth that is in lock task mode
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
 
@@ -357,9 +357,9 @@
 
     @Test
     public void testStopLockTaskMode_rootTask() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth that is in lock task mode
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
 
@@ -405,9 +405,9 @@
 
     @Test
     public void testClearLockedTasks() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth that is in lock task mode
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
 
@@ -434,7 +434,7 @@
                 .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -454,7 +454,7 @@
                 .thenReturn(true);
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -471,7 +471,7 @@
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId());
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -488,7 +488,7 @@
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId());
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -500,45 +500,45 @@
 
     @Test
     public void testUpdateLockTaskPackages() {
-        String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
-        String[] whitelist2 = {TEST_PACKAGE_NAME};
+        String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
+        String[] allowlist2 = {TEST_PACKAGE_NAME};
 
-        // No package is whitelisted initially
-        for (String pkg : whitelist1) {
-            assertFalse("Package shouldn't be whitelisted: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
-            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(0, pkg));
+        // No package is allowlisted initially
+        for (String pkg : allowlist1) {
+            assertFalse("Package shouldn't be allowlisted: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg));
+            assertFalse("Package shouldn't be allowlisted for user 0: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(0, pkg));
         }
 
-        // Apply whitelist
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1);
+        // Apply allowlist
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist1);
 
-        // Assert the whitelist is applied to the correct user
-        for (String pkg : whitelist1) {
-            assertTrue("Package should be whitelisted: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
-            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(0, pkg));
+        // Assert the allowlist is applied to the correct user
+        for (String pkg : allowlist1) {
+            assertTrue("Package should be allowlisted: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg));
+            assertFalse("Package shouldn't be allowlisted for user 0: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(0, pkg));
         }
 
-        // Update whitelist
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2);
+        // Update allowlist
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist2);
 
-        // Assert the new whitelist is applied
-        assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME,
-                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME));
-        assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2,
-                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
+        // Assert the new allowlist is applied
+        assertTrue("Package should remain allowlisted: " + TEST_PACKAGE_NAME,
+                mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME));
+        assertFalse("Package should no longer be allowlisted: " + TEST_PACKAGE_NAME_2,
+                mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
     }
 
     @Test
     public void testUpdateLockTaskPackages_taskRemoved() throws Exception {
-        // GIVEN two tasks which are whitelisted initially
+        // GIVEN two tasks which are allowlisted initially
         Task tr1 = getTaskForUpdate(TEST_PACKAGE_NAME, true);
         Task tr2 = getTaskForUpdate(TEST_PACKAGE_NAME_2, false);
-        String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        String[] allowlist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
         // GIVEN the tasks are launched into LockTask mode
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
@@ -548,9 +548,9 @@
         assertTrue(mLockTaskController.isTaskLocked(tr2));
         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
 
-        // WHEN removing one package from whitelist
-        whitelist = new String[] {TEST_PACKAGE_NAME};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        // WHEN removing one package from allowlist
+        allowlist = new String[] {TEST_PACKAGE_NAME};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
         // THEN the task running that package should be stopped
         verify(tr2).performClearTaskLocked();
@@ -560,9 +560,9 @@
         assertTrue(mLockTaskController.isTaskLocked(tr1));
         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
 
-        // WHEN removing the last package from whitelist
-        whitelist = new String[] {};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        // WHEN removing the last package from allowlist
+        allowlist = new String[] {};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
         // THEN the last task should be cleared, and the system should quit LockTask mode
         verify(tr1).performClearTaskLocked();
@@ -574,7 +574,7 @@
     @Test
     public void testUpdateLockTaskFeatures() throws Exception {
         // GIVEN a locked task
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN lock task mode should be started with default status bar masks
@@ -616,7 +616,7 @@
     @Test
     public void testUpdateLockTaskFeatures_differentUser() throws Exception {
         // GIVEN a locked task
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN lock task mode should be started with default status bar masks
@@ -638,7 +638,7 @@
     @Test
     public void testUpdateLockTaskFeatures_keyguard() {
         // GIVEN a locked task
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN keyguard should be disabled
@@ -704,7 +704,7 @@
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
 
         // Start lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled
@@ -719,15 +719,15 @@
         assertTrue(mLockTaskController.isActivityAllowed(
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
 
-        // unwhitelisted package should not be allowed
+        // unallowlisted package should not be allowed
         assertFalse(mLockTaskController.isActivityAllowed(
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
 
-        // update the whitelist
-        String[] whitelist = new String[] { TEST_PACKAGE_NAME };
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        // update the allowlist
+        String[] allowlist = new String[] { TEST_PACKAGE_NAME };
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
-        // whitelisted package should be allowed
+        // allowlisted package should be allowed
         assertTrue(mLockTaskController.isActivityAllowed(
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
 
@@ -755,17 +755,17 @@
     }
 
     /**
-     * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest
+     * @param isAppAware {@code true} if the app has marked if allowlisted in its manifest
      */
     private Task getTaskForUpdate(String pkg, boolean isAppAware) {
-        final int authIfWhitelisted = isAppAware
+        final int authIfAllowlisted = isAppAware
                 ? Task.LOCK_TASK_AUTH_LAUNCHABLE
-                : Task.LOCK_TASK_AUTH_WHITELISTED;
-        Task tr = getTask(pkg, authIfWhitelisted);
+                : Task.LOCK_TASK_AUTH_ALLOWLISTED;
+        Task tr = getTask(pkg, authIfAllowlisted);
         doAnswer((invocation) -> {
-            boolean isWhitelisted =
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg);
-            tr.mLockTaskAuth = isWhitelisted ? authIfWhitelisted : Task.LOCK_TASK_AUTH_PINNABLE;
+            boolean isAllowlisted =
+                    mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg);
+            tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : Task.LOCK_TASK_AUTH_PINNABLE;
             return null;
         }).when(tr).setLockTaskAuth();
         return tr;
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/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 0e1d4dc..982e469 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -568,7 +568,9 @@
     private static WindowState addWindowToActivity(ActivityRecord activity) {
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
         params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-        final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
+        params.setFitInsetsSides(0);
+        params.setFitInsetsTypes(0);
+        final TestWindowState w = new TestWindowState(
                 activity.mWmService, mock(Session.class), new TestIWindow(), params, activity);
         WindowTestsBase.makeWindowVisible(w);
         w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -581,7 +583,7 @@
         doReturn(true).when(displayPolicy).hasStatusBar();
         displayPolicy.onConfigurationChanged();
 
-        final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(
+        final TestWindowToken token = createTestWindowToken(
                 WindowManager.LayoutParams.TYPE_STATUS_BAR, displayContent);
         final WindowManager.LayoutParams attrs =
                 new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_STATUS_BAR);
@@ -589,7 +591,7 @@
         attrs.layoutInDisplayCutoutMode =
                 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         attrs.setFitInsetsTypes(0 /* types */);
-        final WindowTestUtils.TestWindowState statusBar = new WindowTestUtils.TestWindowState(
+        final TestWindowState statusBar = new TestWindowState(
                 displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token);
         token.addWindow(statusBar);
         statusBar.setRequestedSize(displayContent.mBaseDisplayWidth,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 3492556..260f1e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -76,8 +76,7 @@
         // Stack should contain visible app window to be considered visible.
         final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
         assertFalse(mPinnedStack.isVisible());
-        final ActivityRecord pinnedApp =
-                WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord pinnedApp = createTestActivityRecord(mDisplayContent);
         pinnedTask.addChild(pinnedApp, 0 /* addPos */);
         assertTrue(mPinnedStack.isVisible());
     }
@@ -92,7 +91,7 @@
         final Task stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
         task.addChild(activity, 0 /* addPos */);
         final TaskDisplayArea taskDisplayArea = activity.getDisplayArea();
         activity.mNeedsAnimationBoundsLayer = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index d950344..b4a1337 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -87,7 +88,7 @@
                 0 /* systemUiVisibility */, false /* isTranslucent */);
         mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
                 createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
-                taskBounds, ORIENTATION_PORTRAIT, new InsetsState());
+                taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState());
     }
 
     private static TaskDescription createTaskDescription(int background, int statusBar,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 205b842..7cf30c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -85,14 +85,12 @@
     public void testClosingAppDifferentStackOrientation() {
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
-        ActivityRecord activity1 =
-                WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        ActivityRecord activity1 = createTestActivityRecord(mDisplayContent);
         task1.addChild(activity1, 0);
         activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         final Task task2 = createTaskInStack(stack, 1 /* userId */);
-        ActivityRecord activity2=
-                WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        ActivityRecord activity2 = createTestActivityRecord(mDisplayContent);
         task2.addChild(activity2, 0);
         activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
@@ -105,14 +103,12 @@
     public void testMoveTaskToBackDifferentStackOrientation() {
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
-        ActivityRecord activity1 =
-                WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        ActivityRecord activity1 = createTestActivityRecord(mDisplayContent);
         task1.addChild(activity1, 0);
         activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         final Task task2 = createTaskInStack(stack, 1 /* userId */);
-        ActivityRecord activity2 =
-                WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        ActivityRecord activity2 = createTestActivityRecord(mDisplayContent);
         task2.addChild(activity2, 0);
         activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation());
@@ -221,7 +217,7 @@
     public void testActivityAndTaskGetsProperType() {
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
-        ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        ActivityRecord activity1 = createTestActivityRecord(mDisplayContent);
 
         // First activity should become standard
         task1.addChild(activity1, 0);
@@ -229,7 +225,7 @@
         assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
 
         // Second activity should also become standard
-        ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        ActivityRecord activity2 = createTestActivityRecord(mDisplayContent);
         task1.addChild(activity2, WindowContainer.POSITION_TOP);
         assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity2.getActivityType());
         assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 92b6e6e..ace0400 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -56,8 +56,7 @@
     public void testRemoveContainer() {
         final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stackController1, 0 /* userId */);
-        final ActivityRecord activity =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity = createActivityRecordInTask(mDisplayContent, task);
 
         task.removeIfPossible();
         // Assert that the container was removed.
@@ -70,8 +69,7 @@
     public void testRemoveContainer_deferRemoval() {
         final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stackController1, 0 /* userId */);
-        final ActivityRecord activity =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity = createActivityRecordInTask(mDisplayContent, task);
 
         doReturn(true).when(task).shouldDeferRemoval();
 
@@ -153,10 +151,8 @@
     public void testIsInStack() {
         final Task task1 = createTaskStackOnDisplay(mDisplayContent);
         final Task task2 = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity1 =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task1);
-        final ActivityRecord activity2 =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task2);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task1);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task2);
         assertEquals(activity1, task1.isInTask(activity1));
         assertNull(task1.isInTask(activity2));
     }
@@ -165,12 +161,9 @@
     public void testRemoveChildForOverlayTask() {
         final Task task = createTaskStackOnDisplay(mDisplayContent);
         final int taskId = task.mTaskId;
-        final ActivityRecord activity1 =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
-        final ActivityRecord activity2 =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
-        final ActivityRecord activity3 =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity3 = createActivityRecordInTask(mDisplayContent, task);
         activity1.setTaskOverlay(true);
         activity2.setTaskOverlay(true);
         activity3.setTaskOverlay(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 75ed928..6ed7622 100644
--- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -44,7 +44,7 @@
 
     @Test
     public void testFlow() {
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
         mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(activity);
         mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity);
@@ -56,8 +56,8 @@
 
     @Test
     public void testMultiple() {
-        final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(mDisplayContent);
-        final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity1 = createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity2 = createTestActivityRecord(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity1);
         mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(activity1);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity2);
@@ -72,7 +72,7 @@
 
     @Test
     public void testClear() {
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
         mDisplayContent.mUnknownAppVisibilityController.clear();
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
@@ -80,7 +80,7 @@
 
     @Test
     public void testRemoveFinishingInvisibleActivityFromUnknown() {
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
         activity.finishing = true;
         activity.mVisibleRequested = true;
@@ -90,7 +90,7 @@
 
     @Test
     public void testAppRemoved() {
-        final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+        final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
         mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(activity);
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 8ac44f2..4163a9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -810,8 +810,7 @@
     public void testOnDisplayChanged() {
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        final ActivityRecord activity =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity = createActivityRecordInTask(mDisplayContent, task);
 
         final DisplayContent newDc = createNewDisplay();
         stack.getDisplayArea().removeStack(stack);
@@ -854,19 +853,17 @@
     public void testTaskCanApplyAnimation() {
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        final ActivityRecord activity2 =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
-        final ActivityRecord activity1 =
-                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, task);
         verifyWindowContainerApplyAnimation(task, activity1, activity2);
     }
 
     @Test
     public void testStackCanApplyAnimation() {
         final Task stack = createTaskStackOnDisplay(mDisplayContent);
-        final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
+        final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent,
                 createTaskInStack(stack, 0 /* userId */));
-        final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
+        final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent,
                 createTaskInStack(stack, 0 /* userId */));
         verifyWindowContainerApplyAnimation(stack, activity1, activity2);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 8cfa4f0..9135297 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -931,8 +931,7 @@
 
         final Task stack = createStack();
         final Task task = createTask(stack);
-        final ActivityRecord record = WindowTestUtils.createActivityRecordInTask(
-                stack.mDisplayContent, task);
+        final ActivityRecord record = createActivityRecordInTask(stack.mDisplayContent, task);
 
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
@@ -966,8 +965,7 @@
     public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
         final Task stack = createStack();
         final Task task = createTask(stack);
-        final ActivityRecord activity = WindowTestUtils.createActivityRecordInTask(
-                stack.mDisplayContent, task);
+        final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task);
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
 
         // Setup the task to be controlled by the MW mode organizer
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 3894a2e..f095fd4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -357,8 +357,7 @@
 
         // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an
         // activity. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup
-        final WindowToken windowToken = WindowTestUtils.createTestWindowToken(FIRST_SUB_WINDOW,
-                mDisplayContent);
+        final WindowToken windowToken = createTestWindowToken(FIRST_SUB_WINDOW, mDisplayContent);
         final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken,
                 "firstWindow");
         final WindowState secondWindow = createWindow(null, TYPE_APPLICATION, windowToken,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
deleted file mode 100644
index 0180d87..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2017 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.wm;
-
-import static android.app.AppOpsManager.OP_NONE;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-
-import android.os.IBinder;
-import android.view.IWindow;
-import android.view.WindowManager;
-
-import com.android.server.wm.WindowTestsBase.ActivityBuilder;
-
-/**
- * A collection of static functions that provide access to WindowManager related test functionality.
- */
-class WindowTestUtils {
-
-    /** Creates a {@link Task} and adds it to the specified {@link Task}. */
-    static Task createTaskInStack(WindowManagerService service, Task stack, int userId) {
-        final Task task = new WindowTestsBase.TaskBuilder(stack.mStackSupervisor)
-                .setUserId(userId)
-                .setStack(stack)
-                .build();
-        return task;
-    }
-
-    /** Creates an {@link ActivityRecord} and adds it to the specified {@link Task}. */
-    static ActivityRecord createActivityRecordInTask(DisplayContent dc, Task task) {
-        final ActivityRecord activity = createTestActivityRecord(dc);
-        task.addChild(activity, POSITION_TOP);
-        return activity;
-    }
-
-    static ActivityRecord createTestActivityRecord(Task stack) {
-        final ActivityRecord activity = new ActivityBuilder(stack.mAtmService)
-                .setStack(stack)
-                .setCreateTask(true)
-                .build();
-        postCreateActivitySetup(activity, stack.getDisplayContent());
-        return activity;
-    }
-
-    static ActivityRecord createTestActivityRecord(DisplayContent dc) {
-        final ActivityRecord activity = new ActivityBuilder(dc.mWmService.mAtmService).build();
-        postCreateActivitySetup(activity, dc);
-        return activity;
-    }
-
-    private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) {
-        activity.onDisplayChanged(dc);
-        activity.setOccludesParent(true);
-        activity.setVisible(true);
-        activity.mVisibleRequested = true;
-    }
-
-    static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
-        return createTestWindowToken(type, dc, false /* persistOnEmpty */);
-    }
-
-    static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
-            boolean persistOnEmpty) {
-        SystemServicesTestRule.checkHoldsLock(dc.mWmService.mGlobalLock);
-
-        return new TestWindowToken(type, dc, persistOnEmpty);
-    }
-
-    /* Used so we can gain access to some protected members of the {@link WindowToken} class */
-    static class TestWindowToken extends WindowToken {
-
-        private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
-            super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
-                    false /* ownerCanManageAppTokens */);
-        }
-
-        int getWindowsCount() {
-            return mChildren.size();
-        }
-
-        boolean hasWindow(WindowState w) {
-            return mChildren.contains(w);
-        }
-    }
-
-    /** Used to track resize reports. */
-    static class TestWindowState extends WindowState {
-        boolean mResizeReported;
-
-        TestWindowState(WindowManagerService service, Session session, IWindow window,
-                WindowManager.LayoutParams attrs, WindowToken token) {
-            super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, 0,
-                    false /* ownerCanAddInternalSystemWindow */);
-        }
-
-        @Override
-        void reportResized() {
-            super.reportResized();
-            mResizeReported = true;
-        }
-
-        @Override
-        public boolean isGoneForLayoutLw() {
-            return false;
-        }
-
-        @Override
-        void updateResizingWindowIfNeeded() {
-            // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
-            // the system that it can actually update the window.
-            boolean hadSurface = mHasSurface;
-            mHasSurface = true;
-
-            super.updateResizingWindowIfNeeded();
-
-            mHasSurface = hadSurface;
-        }
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ec19a58..5ce61b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -47,6 +47,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -66,6 +67,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
@@ -239,7 +241,7 @@
     private WindowToken createWindowToken(
             DisplayContent dc, int windowingMode, int activityType, int type) {
         if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
-            return WindowTestUtils.createTestWindowToken(type, dc);
+            return createTestWindowToken(type, dc);
         }
 
         return createActivityRecord(dc, windowingMode, activityType);
@@ -249,10 +251,39 @@
         return createTestActivityRecord(dc, windowingMode, activityType);
     }
 
-    ActivityRecord createTestActivityRecord(DisplayContent dc, int
-            windowingMode, int activityType) {
+    ActivityRecord createTestActivityRecord(DisplayContent dc, int windowingMode,
+            int activityType) {
         final Task stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
-        return WindowTestUtils.createTestActivityRecord(stack);
+        return createTestActivityRecord(stack);
+    }
+
+    /** Creates an {@link ActivityRecord} and adds it to the specified {@link Task}. */
+    static ActivityRecord createActivityRecordInTask(DisplayContent dc, Task task) {
+        final ActivityRecord activity = createTestActivityRecord(dc);
+        task.addChild(activity, POSITION_TOP);
+        return activity;
+    }
+
+    static ActivityRecord createTestActivityRecord(DisplayContent dc) {
+        final ActivityRecord activity = new ActivityBuilder(dc.mWmService.mAtmService).build();
+        postCreateActivitySetup(activity, dc);
+        return activity;
+    }
+
+    static ActivityRecord createTestActivityRecord(Task stack) {
+        final ActivityRecord activity = new ActivityBuilder(stack.mAtmService)
+                .setStack(stack)
+                .setCreateTask(true)
+                .build();
+        postCreateActivitySetup(activity, stack.getDisplayContent());
+        return activity;
+    }
+
+    private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) {
+        activity.onDisplayChanged(dc);
+        activity.setOccludesParent(true);
+        activity.setVisible(true);
+        activity.mVisibleRequested = true;
     }
 
     WindowState createWindow(WindowState parent, int type, String name) {
@@ -274,8 +305,7 @@
     }
 
     WindowState createAppWindow(Task task, int type, String name) {
-        final ActivityRecord activity =
-                WindowTestUtils.createTestActivityRecord(task.getDisplayContent());
+        final ActivityRecord activity = createTestActivityRecord(task.getDisplayContent());
         task.addChild(activity, 0);
         return createWindow(null, type, activity, name);
     }
@@ -390,7 +420,11 @@
 
     /** Creates a {@link Task} and adds it to the specified {@link Task}. */
     Task createTaskInStack(Task stack, int userId) {
-        return WindowTestUtils.createTaskInStack(mWm, stack, userId);
+        final Task task = new TaskBuilder(stack.mStackSupervisor)
+                .setUserId(userId)
+                .setStack(stack)
+                .build();
+        return task;
     }
 
     /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
@@ -432,12 +466,11 @@
         return createNewDisplay(displayInfo, true /* supportIme */);
     }
 
-    /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
-    WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs,
-            WindowToken token) {
+    /** Creates a {@link TestWindowState} */
+    TestWindowState createWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
         SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
 
-        return new WindowTestUtils.TestWindowState(mWm, mMockSession, mIWindow, attrs, token);
+        return new TestWindowState(mWm, mMockSession, mIWindow, attrs, token);
     }
 
     /** Creates a {@link DisplayContent} as parts of simulate display info for test. */
@@ -1055,4 +1088,66 @@
         public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
         }
     }
+
+    static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
+        return createTestWindowToken(type, dc, false /* persistOnEmpty */);
+    }
+
+    static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
+            boolean persistOnEmpty) {
+        SystemServicesTestRule.checkHoldsLock(dc.mWmService.mGlobalLock);
+
+        return new TestWindowToken(type, dc, persistOnEmpty);
+    }
+
+    /** Used so we can gain access to some protected members of the {@link WindowToken} class */
+    static class TestWindowToken extends WindowToken {
+
+        private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
+            super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
+                    false /* ownerCanManageAppTokens */);
+        }
+
+        int getWindowsCount() {
+            return mChildren.size();
+        }
+
+        boolean hasWindow(WindowState w) {
+            return mChildren.contains(w);
+        }
+    }
+
+    /** Used to track resize reports. */
+    static class TestWindowState extends WindowState {
+        boolean mResizeReported;
+
+        TestWindowState(WindowManagerService service, Session session, IWindow window,
+                WindowManager.LayoutParams attrs, WindowToken token) {
+            super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, 0,
+                    false /* ownerCanAddInternalSystemWindow */);
+        }
+
+        @Override
+        void reportResized() {
+            super.reportResized();
+            mResizeReported = true;
+        }
+
+        @Override
+        public boolean isGoneForLayoutLw() {
+            return false;
+        }
+
+        @Override
+        void updateResizingWindowIfNeeded() {
+            // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
+            // the system that it can actually update the window.
+            boolean hadSurface = mHasSurface;
+            mHasSurface = true;
+
+            super.updateResizingWindowIfNeeded();
+
+            mHasSurface = hadSurface;
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index f185da3..d9c48fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -58,8 +58,7 @@
 
     @Test
     public void testAddWindow() {
-        final WindowTestUtils.TestWindowToken token =
-                WindowTestUtils.createTestWindowToken(0, mDisplayContent);
+        final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
 
         assertEquals(0, token.getWindowsCount());
 
@@ -93,7 +92,7 @@
     @Test
     public void testChildRemoval() {
         final DisplayContent dc = mDisplayContent;
-        final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(0, dc);
+        final TestWindowToken token = createTestWindowToken(0, dc);
 
         assertEquals(token, dc.getWindowToken(token.token));
 
@@ -116,7 +115,7 @@
      */
     @Test
     public void testTokenRemovalProcess() {
-        final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(
+        final TestWindowToken token = createTestWindowToken(
                 TYPE_TOAST, mDisplayContent, true /* persistOnEmpty */);
 
         // Verify that the token is on the display
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2fd6c42..78556ef 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -763,11 +763,12 @@
                 return;
             }
 
-            final LinkedList<Event> events = mReportedEvents.get(userId, new LinkedList<>());
-            events.add(event);
-            if (mReportedEvents.get(userId) == null) {
+            LinkedList<Event> events = mReportedEvents.get(userId);
+            if (events == null) {
+                events = new LinkedList<>();
                 mReportedEvents.put(userId, events);
             }
+            events.add(event);
             if (events.size() == 1) {
                 // Every time a file is persisted to disk, mReportedEvents is cleared for this user
                 // so trigger a flush to disk every time the first event has been added.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index bcb1736..464f318 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1209,11 +1209,14 @@
     /**
      *  Returns a list of all {@link PhoneAccount}s registered for the calling package.
      *
+     * @deprecated Use {@link #getSelfManagedPhoneAccounts()} instead to get only self-managed
+     * {@link PhoneAccountHandle} for the calling package.
      * @return A list of {@code PhoneAccountHandle} objects.
      * @hide
      */
     @SystemApi
     @SuppressLint("Doclava125")
+    @Deprecated
     public List<PhoneAccountHandle> getPhoneAccountsForPackage() {
         try {
             if (isServiceConnected()) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 2e4d390..c0658fe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -84,7 +84,7 @@
                     navBarLayerIsAlwaysVisible(bugId = 140855415)
                     statusBarLayerIsAlwaysVisible(bugId = 140855415)
                     noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0)
+                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
                     statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
                     imeLayerBecomesInvisible(bugId = 141458352)
                     imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 1c0da4f..dcf3085 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -92,7 +92,7 @@
                     navBarLayerIsAlwaysVisible(bugId = 140855415)
                     statusBarLayerIsAlwaysVisible(bugId = 140855415)
                     noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0)
+                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
                     statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
                     imeLayerBecomesInvisible(bugId = 153739621)
                     imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index e078f26..91ec211 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -90,7 +90,7 @@
                     navBarLayerIsAlwaysVisible()
                     statusBarLayerIsAlwaysVisible()
                     noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
+                    navBarLayerRotatesAndScales(rotation, bugId = 140855415)
                     statusBarLayerRotatesScales(rotation)
 
                     all("dividerLayerBecomesVisible") {
@@ -102,7 +102,8 @@
 
                 eventLog {
                     focusChanges(testApp.`package`,
-                            "recents_animation_input_consumer", "NexusLauncherActivity")
+                            "recents_animation_input_consumer", "NexusLauncherActivity",
+                            bugId = 151179149)
                 }
             }
         }
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
new file mode 100644
index 0000000..9d35cbc
--- /dev/null
+++ b/tests/Input/Android.bp
@@ -0,0 +1,12 @@
+android_test {
+    name: "InputTests",
+    srcs: ["src/**/*.kt"],
+    platform_apis: true,
+    certificate: "platform",
+    static_libs: [
+            "androidx.test.ext.junit",
+            "androidx.test.rules",
+            "android-support-test",
+            "ub-uiautomator",
+        ],
+}
diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml
new file mode 100644
index 0000000..4195df7
--- /dev/null
+++ b/tests/Input/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="com.android.test.input">
+    <uses-permission android:name="android.permission.MONITOR_INPUT"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+
+    <application android:label="InputTest">
+
+        <activity android:name=".UnresponsiveGestureMonitorActivity"
+             android:label="Unresponsive gesture monitor"
+             android:process=":externalProcess">
+        </activity>
+
+
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.test.input"
+                     android:label="Input Tests"/>
+</manifest>
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
new file mode 100644
index 0000000..c62db1ea
--- /dev/null
+++ b/tests/Input/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2020 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs Input Tests">
+    <option name="test-tag" value="InputTests" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="InputTests.apk"/>
+
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.test.input"/>
+        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+        <option name="shell-timeout" value="660s" />
+        <option name="test-timeout" value="600s" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
new file mode 100644
index 0000000..4da3eca
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.test.input
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.filters.MediumTest
+
+import android.graphics.Rect
+import android.os.SystemClock
+import android.provider.Settings
+import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.UiObject2
+import android.support.test.uiautomator.Until
+import android.view.InputDevice
+import android.view.MotionEvent
+
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * This test makes sure that an unresponsive gesture monitor gets an ANR.
+ *
+ * The gesture monitor must be registered from a different process than the instrumented process.
+ * Otherwise, when the test runs, you will get:
+ * Test failed to run to completion.
+ * Reason: 'Instrumentation run failed due to 'keyDispatchingTimedOut''.
+ * Check device logcat for details
+ * RUNNER ERROR: Instrumentation run failed due to 'keyDispatchingTimedOut'
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class AnrTest {
+    companion object {
+        private const val TAG = "AnrTest"
+    }
+
+    val mInstrumentation = InstrumentationRegistry.getInstrumentation()
+    var mHideErrorDialogs = 0
+
+    @Before
+    fun setUp() {
+        val contentResolver = mInstrumentation.targetContext.contentResolver
+        mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+    }
+
+    @After
+    fun tearDown() {
+        val contentResolver = mInstrumentation.targetContext.contentResolver
+        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs)
+    }
+
+    @Test
+    fun testGestureMonitorAnr() {
+        startUnresponsiveActivity()
+        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+        val obj: UiObject2? = uiDevice.wait(Until.findObject(
+                By.text("Unresponsive gesture monitor")), 10000)
+
+        if (obj == null) {
+            fail("Could not find unresponsive activity")
+            return
+        }
+
+        val rect: Rect = obj.visibleBounds
+        val downTime = SystemClock.uptimeMillis()
+        val downEvent = MotionEvent.obtain(downTime, downTime,
+                MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
+        downEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+
+        mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
+
+        // Todo: replace using timeout from android.hardware.input.IInputManager
+        SystemClock.sleep(5000) // default ANR timeout for gesture monitors
+
+        clickCloseAppOnAnrDialog()
+    }
+
+    private fun clickCloseAppOnAnrDialog() {
+        // Find anr dialog and kill app
+        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+        val closeAppButton: UiObject2? =
+                uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
+        if (closeAppButton == null) {
+            fail("Could not find anr dialog")
+            return
+        }
+        closeAppButton.click()
+    }
+
+    private fun startUnresponsiveActivity() {
+        val flags = " -W -n "
+        val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity"
+        mInstrumentation.uiAutomation.executeShellCommand(startCmd)
+    }
+}
\ No newline at end of file
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
new file mode 100644
index 0000000..d83a457
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -0,0 +1,52 @@
+/**
+ * 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.test.input
+
+import android.app.Activity
+import android.hardware.input.InputManager
+import android.os.Bundle
+import android.os.Looper
+import android.util.Log
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputMonitor
+
+class UnresponsiveReceiver(channel: InputChannel, looper: Looper) :
+        InputEventReceiver(channel, looper) {
+    companion object {
+        const val TAG = "UnresponsiveReceiver"
+    }
+    override fun onInputEvent(event: InputEvent) {
+        Log.i(TAG, "Received $event")
+        // Not calling 'finishInputEvent' in order to trigger the ANR
+    }
+}
+
+class UnresponsiveGestureMonitorActivity : Activity() {
+    companion object {
+        const val MONITOR_NAME = "unresponsive gesture monitor"
+    }
+    private lateinit var mInputEventReceiver: InputEventReceiver
+    private lateinit var mInputMonitor: InputMonitor
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mInputMonitor = InputManager.getInstance().monitorGestureInput(MONITOR_NAME, displayId)
+        mInputEventReceiver = UnresponsiveReceiver(
+                mInputMonitor.getInputChannel(), Looper.myLooper())
+    }
+}
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/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp
new file mode 100644
index 0000000..ca0a091
--- /dev/null
+++ b/tests/SilkFX/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2010 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_test {
+    name: "SilkFX",
+    srcs: ["**/*.java", "**/*.kt"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
new file mode 100644
index 0000000..ca9550a
--- /dev/null
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.test.silkfx">
+
+    <uses-sdk android:minSdkVersion="30"/>
+
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+
+    <application android:label="SilkFX"
+         android:theme="@android:style/Theme.Material">
+
+        <activity android:name=".Main"
+             android:label="SilkFX Demos"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".app.CommonDemoActivity" />
+
+        <activity android:name=".hdr.GlowActivity"
+            android:label="Glow Examples"/>
+
+    </application>
+</manifest>
diff --git a/tests/SilkFX/res/drawable-nodpi/dark_notification.png b/tests/SilkFX/res/drawable-nodpi/dark_notification.png
new file mode 100644
index 0000000..6de6c2a
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/dark_notification.png
Binary files differ
diff --git a/tests/SilkFX/res/drawable-nodpi/light_notification.png b/tests/SilkFX/res/drawable-nodpi/light_notification.png
new file mode 100644
index 0000000..81a67cd
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/light_notification.png
Binary files differ
diff --git a/tests/SilkFX/res/layout/bling_notifications.xml b/tests/SilkFX/res/layout/bling_notifications.xml
new file mode 100644
index 0000000..6d266b7
--- /dev/null
+++ b/tests/SilkFX/res/layout/bling_notifications.xml
@@ -0,0 +1,35 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <com.android.test.silkfx.hdr.BlingyNotification
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="8dp"
+        android:src="@drawable/dark_notification" />
+
+    <com.android.test.silkfx.hdr.BlingyNotification
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="8dp"
+        android:src="@drawable/light_notification" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/SilkFX/res/layout/color_mode_controls.xml b/tests/SilkFX/res/layout/color_mode_controls.xml
new file mode 100644
index 0000000..c0c0bab
--- /dev/null
+++ b/tests/SilkFX/res/layout/color_mode_controls.xml
@@ -0,0 +1,64 @@
+<?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.
+  -->
+
+<com.android.test.silkfx.common.ColorModeControls
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/current_mode"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/mode_default"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="Default (sRGB)" />
+
+        <Button
+            android:id="@+id/mode_wide"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="Wide Gamut (P3)" />
+
+        <Button
+            android:id="@+id/mode_hdr"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="HDR" />
+
+        <Button
+            android:id="@+id/mode_hdr10"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="HDR10" />
+
+    </LinearLayout>
+
+</com.android.test.silkfx.common.ColorModeControls>
\ No newline at end of file
diff --git a/tests/SilkFX/res/layout/common_base.xml b/tests/SilkFX/res/layout/common_base.xml
new file mode 100644
index 0000000..944c684
--- /dev/null
+++ b/tests/SilkFX/res/layout/common_base.xml
@@ -0,0 +1,39 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <include layout="@layout/color_mode_controls" />
+
+    <FrameLayout android:id="@+id/demo_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <com.android.test.silkfx.common.HDRIndicator
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_margin="8dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/SilkFX/res/layout/hdr_glows.xml b/tests/SilkFX/res/layout/hdr_glows.xml
new file mode 100644
index 0000000..b6050645
--- /dev/null
+++ b/tests/SilkFX/res/layout/hdr_glows.xml
@@ -0,0 +1,51 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <include layout="@layout/color_mode_controls" />
+
+    <com.android.test.silkfx.hdr.GlowingCard
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:layout_margin="8dp" />
+
+    <com.android.test.silkfx.hdr.GlowingCard
+        android:id="@+id/card2"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:layout_margin="8dp"/>
+
+    <com.android.test.silkfx.hdr.RadialGlow
+        android:layout_width="match_parent"
+        android:layout_height="200dp"
+        android:layout_margin="8dp" />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <com.android.test.silkfx.common.HDRIndicator
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_margin="8dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
new file mode 100644
index 0000000..76e62a6
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -0,0 +1,125 @@
+/*
+ * 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.test.silkfx
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseExpandableListAdapter
+import android.widget.ExpandableListView
+import android.widget.TextView
+import com.android.test.silkfx.app.CommonDemoActivity
+import com.android.test.silkfx.app.EXTRA_LAYOUT
+import com.android.test.silkfx.app.EXTRA_TITLE
+import com.android.test.silkfx.hdr.GlowActivity
+import kotlin.reflect.KClass
+
+class Demo(val name: String, val makeIntent: (Context) -> Intent) {
+    constructor(name: String, activity: KClass<out Activity>) : this(name, { context ->
+        Intent(context, activity.java)
+    })
+    constructor(name: String, layout: Int) : this(name, { context ->
+        Intent(context, CommonDemoActivity::class.java).apply {
+            putExtra(EXTRA_LAYOUT, layout)
+            putExtra(EXTRA_TITLE, name)
+        }
+    })
+}
+data class DemoGroup(val groupName: String, val demos: List<Demo>)
+
+private val AllDemos = listOf(
+        DemoGroup("HDR", listOf(
+                Demo("Glow", GlowActivity::class),
+                Demo("Blingy Notifications", R.layout.bling_notifications)
+        ))
+)
+
+class Main : Activity() {
+
+    public override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        val list = ExpandableListView(this)
+
+        setContentView(list)
+
+        val inflater = LayoutInflater.from(this)
+        list.setAdapter(object : BaseExpandableListAdapter() {
+            override fun getGroup(groupPosition: Int): DemoGroup {
+                return AllDemos[groupPosition]
+            }
+
+            override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean = true
+
+            override fun hasStableIds(): Boolean = true
+
+            override fun getGroupView(
+                groupPosition: Int,
+                isExpanded: Boolean,
+                convertView: View?,
+                parent: ViewGroup?
+            ): View {
+                val view = (convertView ?: inflater.inflate(
+                        android.R.layout.simple_expandable_list_item_1, parent, false)) as TextView
+                view.text = AllDemos[groupPosition].groupName
+                return view
+            }
+
+            override fun getChildrenCount(groupPosition: Int): Int {
+                return AllDemos[groupPosition].demos.size
+            }
+
+            override fun getChild(groupPosition: Int, childPosition: Int): Demo {
+                return AllDemos[groupPosition].demos[childPosition]
+            }
+
+            override fun getGroupId(groupPosition: Int): Long = groupPosition.toLong()
+
+            override fun getChildView(
+                groupPosition: Int,
+                childPosition: Int,
+                isLastChild: Boolean,
+                convertView: View?,
+                parent: ViewGroup?
+            ): View {
+                val view = (convertView ?: inflater.inflate(
+                        android.R.layout.simple_expandable_list_item_1, parent, false)) as TextView
+                view.text = AllDemos[groupPosition].demos[childPosition].name
+                return view
+            }
+
+            override fun getChildId(groupPosition: Int, childPosition: Int): Long {
+                return (groupPosition.toLong() shl 32) or childPosition.toLong()
+            }
+
+            override fun getGroupCount(): Int {
+                return AllDemos.size
+            }
+        })
+
+        list.setOnChildClickListener { _, _, groupPosition, childPosition, _ ->
+            val demo = AllDemos[groupPosition].demos[childPosition]
+            startActivity(demo.makeIntent(this))
+            return@setOnChildClickListener true
+        }
+
+        AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) }
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt
new file mode 100644
index 0000000..89011b5
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.test.silkfx.app
+
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+
+open class BaseDemoActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        val inflater = LayoutInflater.from(this)
+        inflater.factory2 = object : LayoutInflater.Factory2 {
+            private val sClassPrefixList = arrayOf(
+                    "android.widget.",
+                    "android.webkit.",
+                    "android.app.",
+                    null
+            )
+            override fun onCreateView(
+                parent: View?,
+                name: String,
+                context: Context,
+                attrs: AttributeSet
+            ): View? {
+                return onCreateView(name, context, attrs)
+            }
+
+            override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
+                for (prefix in sClassPrefixList) {
+                    try {
+                        val view = inflater.createView(name, prefix, attrs)
+                        if (view != null) {
+                            if (view is WindowObserver) {
+                                view.setWindow(window)
+                            }
+                            return view
+                        }
+                    } catch (e: ClassNotFoundException) { }
+                }
+                return null
+            }
+        }
+    }
+
+    override fun onStart() {
+        super.onStart()
+        actionBar?.setDisplayHomeAsUpEnabled(true)
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        if (item.itemId == android.R.id.home) {
+            onBackPressed()
+            return true
+        }
+        return super.onOptionsItemSelected(item)
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
new file mode 100644
index 0000000..e0a0a20
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.test.silkfx.app
+
+import com.android.test.silkfx.R
+import android.os.Bundle
+import android.view.LayoutInflater
+
+const val EXTRA_LAYOUT = "layout"
+const val EXTRA_TITLE = "title"
+
+class CommonDemoActivity : BaseDemoActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        val extras = intent.extras ?: return finish()
+
+        val layout = extras.getInt(EXTRA_LAYOUT, -1)
+        if (layout == -1) {
+            finish()
+            return
+        }
+        val title = extras.getString(EXTRA_TITLE, "SilkFX")
+        window.setTitle(title)
+
+        setContentView(R.layout.common_base)
+        actionBar?.title = title
+        LayoutInflater.from(this).inflate(layout, findViewById(R.id.demo_container), true)
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java b/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
similarity index 63%
copy from services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
copy to tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
index 7bb27b2..3d989a5 100644
--- a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
@@ -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,10 @@
  * limitations under the License.
  */
 
-package com.android.server.protolog.common;
+package com.android.test.silkfx.app
 
-/**
- * Error while converting a bitmask representing a list of LogDataTypes.
- */
-public class BitmaskConversionException extends RuntimeException {
-    BitmaskConversionException(String msg) {
-        super(msg);
-    }
-}
+import android.view.Window
+
+interface WindowObserver {
+    fun setWindow(window: Window)
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
new file mode 100644
index 0000000..4b85953
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.test.silkfx.common
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.ColorSpace
+import android.util.AttributeSet
+import android.view.View
+
+open class BaseDrawingView : View {
+    val scRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
+    val bt2020 = ColorSpace.get(ColorSpace.Named.BT2020)
+    val lab = ColorSpace.get(ColorSpace.Named.CIE_LAB)
+
+    val density: Float
+    val dp: Int.() -> Float
+
+    fun color(red: Float, green: Float, blue: Float, alpha: Float = 1f): Long {
+        return Color.pack(red, green, blue, alpha, scRGB)
+    }
+
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+        setWillNotDraw(false)
+        isClickable = true
+        density = resources.displayMetrics.density
+        dp = { this * density }
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
new file mode 100644
index 0000000..9b15b04
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -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.test.silkfx.common
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.hardware.display.DisplayManager
+import android.util.AttributeSet
+import android.view.Window
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.android.test.silkfx.app.WindowObserver
+
+class ColorModeControls : LinearLayout, WindowObserver {
+    private val COLOR_MODE_HDR10 = 3
+    private val SDR_WHITE_POINTS = floatArrayOf(200f, 250f, 300f, 350f, 400f, 100f, 150f)
+
+    constructor(context: Context) : this(context, null)
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+        displayManager = context.getSystemService(DisplayManager::class.java)!!
+    }
+
+    private var window: Window? = null
+    private var currentModeDisplay: TextView? = null
+    private val displayManager: DisplayManager
+    private var targetSdrWhitePointIndex = 0
+
+    private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex]
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        val window = window ?: throw IllegalStateException("Failed to attach window")
+
+        currentModeDisplay = findViewById(R.id.current_mode)!!
+        setColorMode(window.colorMode)
+
+        findViewById<Button>(R.id.mode_default)!!.setOnClickListener {
+            setColorMode(ActivityInfo.COLOR_MODE_DEFAULT)
+        }
+        findViewById<Button>(R.id.mode_wide)!!.setOnClickListener {
+            setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT)
+        }
+        findViewById<Button>(R.id.mode_hdr)!!.setOnClickListener {
+            setColorMode(ActivityInfo.COLOR_MODE_HDR)
+        }
+        findViewById<Button>(R.id.mode_hdr10)!!.setOnClickListener {
+            setColorMode(COLOR_MODE_HDR10)
+        }
+    }
+
+    private fun setColorMode(newMode: Int) {
+        val window = window!!
+        var sdrWhitepointChanged = false
+        // Need to do this before setting the colorMode, as setting the colorMode will
+        // trigger the attribute change listener
+        if (newMode == ActivityInfo.COLOR_MODE_HDR ||
+                newMode == COLOR_MODE_HDR10) {
+            if (window.colorMode == newMode) {
+                targetSdrWhitePointIndex = (targetSdrWhitePointIndex + 1) % SDR_WHITE_POINTS.size
+                sdrWhitepointChanged = true
+            }
+            setBrightness(1.0f)
+        } else {
+            setBrightness(.4f)
+        }
+        window.colorMode = newMode
+        if (sdrWhitepointChanged) {
+            threadedRenderer?.setColorMode(newMode, whitePoint)
+        }
+        val whitePoint = whitePoint.toInt()
+        currentModeDisplay?.run {
+            text = "Current Mode: " + when (newMode) {
+                ActivityInfo.COLOR_MODE_DEFAULT -> "Default/SRGB"
+                ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT -> "Wide Gamut"
+                ActivityInfo.COLOR_MODE_HDR -> "HDR (sdr white point $whitePoint)"
+                COLOR_MODE_HDR10 -> "HDR10 (sdr white point $whitePoint)"
+                else -> "Unknown"
+            }
+        }
+    }
+
+    override fun setWindow(window: Window) {
+        this.window = window
+    }
+
+    private fun setBrightness(level: Float) {
+        // To keep window state in sync
+        window?.attributes?.screenBrightness = level
+        invalidate()
+        // To force an 'immediate' snap to what we want
+        // Imperfect, but close enough, synchronization by waiting for frame commit to set the value
+        viewTreeObserver.registerFrameCommitCallback {
+            try {
+                displayManager.setTemporaryBrightness(level)
+            } catch (ex: Exception) {
+                // Ignore a permission denied rejection - it doesn't meaningfully change much
+            }
+        }
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+
+        threadedRenderer?.setColorMode(window!!.colorMode, whitePoint)
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt b/tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt
new file mode 100644
index 0000000..f42161f
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.test.silkfx.common
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorSpace
+import android.graphics.Paint
+import android.graphics.RectF
+import android.util.AttributeSet
+import android.view.View
+
+class HDRIndicator(context: Context) : View(context) {
+    constructor(context: Context, attrs: AttributeSet?) : this(context)
+
+    val scRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+
+        val paint = Paint()
+        paint.isAntiAlias = true
+        val rect = RectF(0f, 0f, width.toFloat(), height.toFloat())
+        paint.textSize = height.toFloat()
+
+        canvas.drawColor(Color.pack(1f, 1f, 1f, 1f, scRGB))
+
+        paint.setColor(Color.pack(1.1f, 1.1f, 1.1f, 1f, scRGB))
+        canvas.drawText("H", rect.left, rect.bottom, paint)
+        paint.setColor(Color.pack(1.2f, 1.2f, 1.2f, 1f, scRGB))
+        canvas.drawText("D", rect.left + height.toFloat(), rect.bottom, paint)
+        paint.setColor(Color.pack(1.3f, 1.3f, 1.3f, 1f, scRGB))
+        canvas.drawText("R", rect.left + height.toFloat() * 2, rect.bottom, paint)
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt
new file mode 100644
index 0000000..4ad21fa
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.test.silkfx.hdr
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BlendMode
+import android.graphics.Canvas
+import android.graphics.LinearGradient
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.Shader
+import android.graphics.drawable.BitmapDrawable
+import android.util.AttributeSet
+import com.android.test.silkfx.common.BaseDrawingView
+
+class BlingyNotification : BaseDrawingView {
+
+    private val image: Bitmap?
+    private val bounds = Rect()
+    private val paint = Paint()
+
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+        val typed = context.obtainStyledAttributes(attrs, intArrayOf(android.R.attr.src))
+        val drawable = typed.getDrawable(0)
+        image = if (drawable is BitmapDrawable) {
+            drawable.bitmap
+        } else {
+            null
+        }
+        typed.recycle()
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        val image = image ?: return super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+
+        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
+        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
+
+        // Currently only used in this mode, so that's all we'll bother to support
+        if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
+            val width = MeasureSpec.getSize(widthMeasureSpec)
+
+            var height = image.height * width / image.width
+            if (heightMode == MeasureSpec.AT_MOST) {
+                height = minOf(MeasureSpec.getSize(heightMeasureSpec), height)
+            }
+            setMeasuredDimension(width, height)
+        } else {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+        }
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        bounds.set(0, 0, w, h)
+        paint.shader = LinearGradient(0f, 0f, w.toFloat(), 0f,
+                longArrayOf(
+                        color(1f, 1f, 1f, 0f),
+                        color(1f, 1f, 1f, .1f),
+                        color(2f, 2f, 2f, .3f),
+                        color(1f, 1f, 1f, .2f),
+                        color(1f, 1f, 1f, 0f)
+                        ),
+                floatArrayOf(.2f, .4f, .5f, .6f, .8f),
+                Shader.TileMode.CLAMP)
+        paint.blendMode = BlendMode.PLUS
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+
+        val image = image ?: return
+
+        canvas.drawBitmap(image, null, bounds, null)
+
+        canvas.save()
+        val frac = ((drawingTime % 2000) / 300f) - 1f
+        canvas.translate(width * frac, 0f)
+        canvas.rotate(-45f)
+        canvas.drawPaint(paint)
+        canvas.restore()
+        invalidate()
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt
new file mode 100644
index 0000000..64dbb22
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.test.silkfx.hdr
+
+import android.os.Bundle
+import com.android.test.silkfx.R
+import com.android.test.silkfx.app.BaseDemoActivity
+
+class GlowActivity : BaseDemoActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.hdr_glows)
+        findViewById<GlowingCard>(R.id.card2)!!.setGlowIntensity(4f)
+    }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt
new file mode 100644
index 0000000..b388bb6
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.test.silkfx.hdr
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.LinearGradient
+import android.graphics.Paint
+import android.graphics.RectF
+import android.graphics.Shader
+import android.util.AttributeSet
+import com.android.test.silkfx.common.BaseDrawingView
+
+class GlowingCard : BaseDrawingView {
+
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+    val radius: Float
+    var COLOR_MAXIMIZER = 1f
+
+    init {
+        radius = 10.dp()
+    }
+
+    fun setGlowIntensity(multiplier: Float) {
+        COLOR_MAXIMIZER = multiplier
+        invalidate()
+    }
+
+    override fun setPressed(pressed: Boolean) {
+        super.setPressed(pressed)
+        invalidate()
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+        val paint = Paint()
+        paint.isAntiAlias = true
+        val rect = RectF(0f, 0f, width.toFloat(), height.toFloat())
+        val glowColor = Color.pack(.5f * COLOR_MAXIMIZER, .4f * COLOR_MAXIMIZER,
+                .75f * COLOR_MAXIMIZER, 1f, scRGB)
+
+        if (isPressed) {
+            paint.setColor(Color.pack(2f, 2f, 2f, 1f, scRGB))
+            paint.strokeWidth = 4.dp()
+            paint.style = Paint.Style.FILL
+            paint.shader = LinearGradient(rect.left, rect.bottom, rect.right, rect.top,
+                glowColor,
+                Color.pack(0f, 0f, 0f, 0f, scRGB),
+                Shader.TileMode.CLAMP)
+            canvas.drawRoundRect(rect, radius, radius, paint)
+        }
+
+        rect.inset(3.dp(), 3.dp())
+
+        paint.setColor(Color.pack(.14f, .14f, .14f, .8f, scRGB))
+        paint.style = Paint.Style.FILL
+        paint.shader = null
+        canvas.drawRoundRect(rect, radius, radius, paint)
+
+        rect.inset(5.dp(), 5.dp())
+        paint.textSize = 14.dp()
+        paint.isFakeBoldText = true
+
+        paint.color = Color.WHITE
+        canvas.drawText("glow = scRGB{${Color.red(glowColor)}, ${Color.green(glowColor)}, " +
+                "${Color.blue(glowColor)}}", rect.left, rect.centerY(), paint)
+        canvas.drawText("(press to activate)", rect.left, rect.bottom, paint)
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
new file mode 100644
index 0000000..599585e
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.test.silkfx.hdr
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.RadialGradient
+import android.graphics.RectF
+import android.graphics.Shader
+import android.util.AttributeSet
+import com.android.test.silkfx.common.BaseDrawingView
+import kotlin.math.min
+
+class RadialGlow : BaseDrawingView {
+
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+    var glowToggle = false
+
+    val glowColor = color(4f, 3.3f, 2.8f)
+    val bgColor = color(.15f, .15f, .15f)
+    val fgColor = color(.51f, .52f, .50f, .4f)
+    var glow: RadialGradient
+
+    init {
+        glow = RadialGradient(0f, 0f, 100.dp(), glowColor, bgColor, Shader.TileMode.CLAMP)
+        isClickable = true
+        setOnClickListener {
+            glowToggle = !glowToggle
+            invalidate()
+        }
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        glow = RadialGradient(0f, 0f,
+            min(w, h).toFloat(), glowColor, bgColor, Shader.TileMode.CLAMP)
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+        val radius = 10.dp()
+
+        val paint = Paint()
+        paint.isDither = true
+        paint.isAntiAlias = true
+        paint.textSize = 18.dp()
+        paint.textAlign = Paint.Align.CENTER
+
+        val rect = RectF(0f, 0f, width.toFloat(), height.toFloat())
+
+        paint.setColor(bgColor)
+        canvas.drawRoundRect(rect, radius, radius, paint)
+
+        if (glowToggle) {
+            paint.shader = glow
+            canvas.save()
+            val frac = (drawingTime % 5000) / 5000f
+            canvas.translate(rect.width() * frac, rect.height() - (rect.height() * frac))
+            canvas.drawPaint(paint)
+            canvas.restore()
+            paint.shader = null
+            invalidate()
+        }
+
+        paint.setColor(fgColor)
+        val innerRect = RectF(rect)
+        innerRect.inset(rect.width() / 4, rect.height() / 4)
+        canvas.drawRoundRect(innerRect, radius, radius, paint)
+
+        paint.setColor(color(1f, 1f, 1f))
+        canvas.drawText("Tap to toggle animation", rect.centerX(), innerRect.top - 4.dp(), paint)
+        canvas.drawText("Outside text", rect.centerX(), rect.bottom - 4.dp(), paint)
+        canvas.drawText("Inside text", innerRect.centerX(), innerRect.bottom - 4.dp(), paint)
+    }
+}
\ No newline at end of file
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index da6018e..530d0e4 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -29,6 +29,7 @@
         "compatibility-tradefed",
         "frameworks-base-hostutils",
         "module_test_util",
+        "cts-install-lib-host",
     ],
     data: [
         ":com.android.apex.cts.shim.v2_prebuilt",
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 7cfbdc2..55def49 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -22,6 +22,8 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import android.cts.install.lib.host.InstallUtilsHost;
+
 import com.android.ddmlib.Log;
 import com.android.tests.rollback.host.AbandonSessionsRule;
 import com.android.tests.util.ModuleTestUtils;
@@ -49,6 +51,7 @@
     private static final String APK_A = "TestAppAv1.apk";
 
     private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this);
+    private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
 
     /**
      * Runs the given phase of a test by calling into the device.
@@ -93,7 +96,7 @@
     @Test
     public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
-                mTestUtils.isApexUpdateSupported());
+                mHostUtils.isApexUpdateSupported());
 
         File apexFile = mTestUtils.getTestFile(SHIM_V2);
         String output = getDevice().executeAdbCommand("install", "--staged",
@@ -107,7 +110,7 @@
     @Test
     public void testAdbStagedInstallNoWaitFlagWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
-                mTestUtils.isApexUpdateSupported());
+                mHostUtils.isApexUpdateSupported());
 
         File apexFile = mTestUtils.getTestFile(SHIM_V2);
         String output = getDevice().executeAdbCommand("install", "--staged",
@@ -122,7 +125,7 @@
     @Test
     public void testAdbInstallMultiPackageCommandWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
-                mTestUtils.isApexUpdateSupported());
+                mHostUtils.isApexUpdateSupported());
 
         File apexFile = mTestUtils.getTestFile(SHIM_V2);
         File apkFile = mTestUtils.getTestFile(APK_A);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 4ccf79a..de1c5759 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -30,6 +30,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -49,6 +50,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
@@ -65,6 +67,7 @@
 import android.net.IpPrefix;
 import android.net.IpSecManager;
 import android.net.LinkProperties;
+import android.net.LocalSocket;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo.DetailedState;
@@ -74,6 +77,7 @@
 import android.net.VpnService;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import android.os.ConditionVariable;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Process;
@@ -101,13 +105,20 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.net.Inet4Address;
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Stream;
 
 /**
@@ -133,7 +144,8 @@
         managedProfileA.profileGroupId = primaryUser.id;
     }
 
-    static final String TEST_VPN_PKG = "com.dummy.vpn";
+    static final String EGRESS_IFACE = "wlan0";
+    static final String TEST_VPN_PKG = "com.testvpn.vpn";
     private static final String TEST_VPN_SERVER = "1.2.3.4";
     private static final String TEST_VPN_IDENTITY = "identity";
     private static final byte[] TEST_VPN_PSK = "psk".getBytes();
@@ -1012,31 +1024,190 @@
         // a subsequent CL.
     }
 
-    @Test
-    public void testStartLegacyVpn() throws Exception {
+    public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
         setMockedUsers(primaryUser);
 
         // Dummy egress interface
-        final String egressIface = "DUMMY0";
         final LinkProperties lp = new LinkProperties();
-        lp.setInterfaceName(egressIface);
+        lp.setInterfaceName(EGRESS_IFACE);
 
         final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
-                        InetAddresses.parseNumericAddress("192.0.2.0"), egressIface);
+                        InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
         lp.addRoute(defaultRoute);
 
-        vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp);
+        vpn.startLegacyVpn(vpnProfile, mKeyStore, lp);
+        return vpn;
+    }
 
+    @Test
+    public void testStartPlatformVpn() throws Exception {
+        startLegacyVpn(mVpnProfile);
         // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
-        // a subsequent CL.
+        // a subsequent patch.
+    }
+
+    @Test
+    public void testStartRacoonNumericAddress() throws Exception {
+        startRacoon("1.2.3.4", "1.2.3.4");
+    }
+
+    @Test
+    public void testStartRacoonHostname() throws Exception {
+        startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve
+    }
+
+    public void startRacoon(final String serverAddr, final String expectedAddr)
+            throws Exception {
+        final ConditionVariable legacyRunnerReady = new ConditionVariable();
+        final VpnProfile profile = new VpnProfile("testProfile" /* key */);
+        profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK;
+        profile.name = "testProfileName";
+        profile.username = "userName";
+        profile.password = "thePassword";
+        profile.server = serverAddr;
+        profile.ipsecIdentifier = "id";
+        profile.ipsecSecret = "secret";
+        profile.l2tpSecret = "l2tpsecret";
+        when(mConnectivityManager.getAllNetworks())
+            .thenReturn(new Network[] { new Network(101) });
+        when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
+                anyInt(), any(), anyInt())).thenAnswer(invocation -> {
+                    // The runner has registered an agent and is now ready.
+                    legacyRunnerReady.open();
+                    return new Network(102);
+                });
+        final Vpn vpn = startLegacyVpn(profile);
+        final TestDeps deps = (TestDeps) vpn.mDeps;
+        try {
+            // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK
+            assertArrayEquals(
+                    new String[] { EGRESS_IFACE, expectedAddr, "udppsk",
+                            profile.ipsecIdentifier, profile.ipsecSecret, "1701" },
+                    deps.racoonArgs.get(10, TimeUnit.SECONDS));
+            // literal values are hardcoded in Vpn.java for mtpd args
+            assertArrayEquals(
+                    new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret,
+                            "name", profile.username, "password", profile.password,
+                            "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
+                            "idle", "1800", "mtu", "1400", "mru", "1400" },
+                    deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+            // Now wait for the runner to be ready before testing for the route.
+            legacyRunnerReady.block(10_000);
+            // In this test the expected address is always v4 so /32
+            final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
+                    RouteInfo.RTN_THROW);
+            assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
+                    + vpn.mConfig.routes,
+                    vpn.mConfig.routes.contains(expectedRoute));
+        } finally {
+            // Now interrupt the thread, unblock the runner and clean up.
+            vpn.mVpnRunner.exitVpnRunner();
+            deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier
+            vpn.mVpnRunner.join(10_000); // wait for up to 10s for the runner to die and cleanup
+        }
+    }
+
+    private static final class TestDeps extends Vpn.Dependencies {
+        public final CompletableFuture<String[]> racoonArgs = new CompletableFuture();
+        public final CompletableFuture<String[]> mtpdArgs = new CompletableFuture();
+        public final File mStateFile;
+
+        private final HashMap<String, Boolean> mRunningServices = new HashMap<>();
+
+        TestDeps() {
+            try {
+                mStateFile = File.createTempFile("vpnTest", ".tmp");
+                mStateFile.deleteOnExit();
+            } catch (final IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void startService(final String serviceName) {
+            mRunningServices.put(serviceName, true);
+        }
+
+        @Override
+        public void stopService(final String serviceName) {
+            mRunningServices.put(serviceName, false);
+        }
+
+        @Override
+        public boolean isServiceRunning(final String serviceName) {
+            return mRunningServices.getOrDefault(serviceName, false);
+        }
+
+        @Override
+        public boolean isServiceStopped(final String serviceName) {
+            return !isServiceRunning(serviceName);
+        }
+
+        @Override
+        public File getStateFile() {
+            return mStateFile;
+        }
+
+        @Override
+        public void sendArgumentsToDaemon(
+                final String daemon, final LocalSocket socket, final String[] arguments,
+                final Vpn.RetryScheduler interruptChecker) throws IOException {
+            if ("racoon".equals(daemon)) {
+                racoonArgs.complete(arguments);
+            } else if ("mtpd".equals(daemon)) {
+                writeStateFile(arguments);
+                mtpdArgs.complete(arguments);
+            } else {
+                throw new UnsupportedOperationException("Unsupported daemon : " + daemon);
+            }
+        }
+
+        private void writeStateFile(final String[] arguments) throws IOException {
+            mStateFile.delete();
+            mStateFile.createNewFile();
+            mStateFile.deleteOnExit();
+            final BufferedWriter writer = new BufferedWriter(
+                    new FileWriter(mStateFile, false /* append */));
+            writer.write(EGRESS_IFACE);
+            writer.write("\n");
+            // addresses
+            writer.write("10.0.0.1/24\n");
+            // routes
+            writer.write("192.168.6.0/24\n");
+            // dns servers
+            writer.write("192.168.6.1\n");
+            // search domains
+            writer.write("vpn.searchdomains.com\n");
+            // endpoint - intentionally empty
+            writer.write("\n");
+            writer.flush();
+            writer.close();
+        }
+
+        @Override
+        @NonNull
+        public InetAddress resolve(final String endpoint) {
+            try {
+                // If a numeric IP address, return it.
+                return InetAddress.parseNumericAddress(endpoint);
+            } catch (IllegalArgumentException e) {
+                // Otherwise, return some token IP to test for.
+                return InetAddress.parseNumericAddress("5.6.7.8");
+            }
+        }
+
+        @Override
+        public boolean checkInterfacePresent(final Vpn vpn, final String iface) {
+            return true;
+        }
     }
 
     /**
      * Mock some methods of vpn object.
      */
     private Vpn createVpn(@UserIdInt int userId) {
-        return new Vpn(Looper.myLooper(), mContext, mNetService,
+        return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService,
                 userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
     }
 
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
index f30c35a..c2a5459 100644
--- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -34,6 +34,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 
 import javax.annotation.Nullable;
@@ -49,7 +51,7 @@
 public class SystemPreparer extends ExternalResource {
     private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
 
-    // The paths of the files pushed onto the device through this rule.
+    // The paths of the files pushed onto the device through this rule to be removed after.
     private ArrayList<String> mPushedFiles = new ArrayList<>();
 
     // The package names of packages installed through this rule.
@@ -81,7 +83,7 @@
         final ITestDevice device = mDeviceProvider.getDevice();
         remount();
         assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath));
-        mPushedFiles.add(outputPath);
+        addPushedFile(device, outputPath);
         return this;
     }
 
@@ -91,10 +93,23 @@
         final ITestDevice device = mDeviceProvider.getDevice();
         remount();
         assertTrue(device.pushFile(file, outputPath));
-        mPushedFiles.add(outputPath);
+        addPushedFile(device, outputPath);
         return this;
     }
 
+    private void addPushedFile(ITestDevice device, String outputPath)
+            throws DeviceNotAvailableException {
+        Path pathCreated = Paths.get(outputPath);
+
+        // Find the top most parent that is new to the device
+        while (pathCreated.getParent() != null
+                && !device.doesFileExist(pathCreated.getParent().toString())) {
+            pathCreated = pathCreated.getParent();
+        }
+
+        mPushedFiles.add(pathCreated.toString());
+    }
+
     /** Deletes the given path from the device */
     public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException {
         final ITestDevice device = mDeviceProvider.getDevice();
@@ -203,7 +218,7 @@
 
     /** Removes installed packages and files that were pushed to the device. */
     @Override
-    protected void after() {
+    public void after() {
         final ITestDevice device = mDeviceProvider.getDevice();
         try {
             remount();
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index f9c54f6..d84ca3d 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -78,6 +78,8 @@
 
 namespace aapt {
 
+constexpr uint8_t kAndroidPackageId = 0x01;
+
 class LinkContext : public IAaptContext {
  public:
   explicit LinkContext(IDiagnostics* diagnostics)
@@ -1805,7 +1807,7 @@
 
     // Override the package ID when it is "android".
     if (context_->GetCompilationPackage() == "android") {
-      context_->SetPackageId(0x01);
+      context_->SetPackageId(kAndroidPackageId);
 
       // Verify we're building a regular app.
       if (context_->GetPackageType() != PackageType::kApp) {
@@ -1862,7 +1864,8 @@
 
     if (context_->GetPackageType() != PackageType::kStaticLib) {
       PrivateAttributeMover mover;
-      if (!mover.Consume(context_, &final_table_)) {
+      if (context_->GetPackageId() == kAndroidPackageId &&
+          !mover.Consume(context_, &final_table_)) {
         context_->GetDiagnostics()->Error(DiagMessage() << "failed moving private attributes");
         return 1;
       }
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/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3cdfb00..e493789 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -122,7 +122,7 @@
 
     DhcpInfo getDhcpInfo();
 
-    void setScanAlwaysAvailable(boolean isAvailable);
+    void setScanAlwaysAvailable(boolean isAvailable, String packageName);
 
     boolean isScanAlwaysAvailable();
 
@@ -142,9 +142,9 @@
 
     void updateInterfaceIpState(String ifaceName, int mode);
 
-    boolean startSoftAp(in WifiConfiguration wifiConfig);
+    boolean startSoftAp(in WifiConfiguration wifiConfig, String packageName);
 
-    boolean startTetheredHotspot(in SoftApConfiguration softApConfig);
+    boolean startTetheredHotspot(in SoftApConfiguration softApConfig, String packageName);
 
     boolean stopSoftAp();
 
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
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ae834f9..b28b902 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2802,7 +2802,7 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void setScanAlwaysAvailable(boolean isAvailable) {
         try {
-            mService.setScanAlwaysAvailable(isAvailable);
+            mService.setScanAlwaysAvailable(isAvailable, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3035,7 +3035,7 @@
     })
     public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
         try {
-            return mService.startSoftAp(wifiConfig);
+            return mService.startSoftAp(wifiConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3059,7 +3059,7 @@
     })
     public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) {
         try {
-            return mService.startTetheredHotspot(softApConfig);
+            return mService.startTetheredHotspot(softApConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index cba1690..e7f1916c 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -218,10 +218,10 @@
      */
     @Test
     public void testStartSoftApCallsServiceWithWifiConfig() throws Exception {
-        when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(true);
+        when(mWifiService.startSoftAp(mApConfig, TEST_PACKAGE_NAME)).thenReturn(true);
         assertTrue(mWifiManager.startSoftAp(mApConfig));
 
-        when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(false);
+        when(mWifiService.startSoftAp(mApConfig, TEST_PACKAGE_NAME)).thenReturn(false);
         assertFalse(mWifiManager.startSoftAp(mApConfig));
     }
 
@@ -231,10 +231,10 @@
      */
     @Test
     public void testStartSoftApCallsServiceWithNullConfig() throws Exception {
-        when(mWifiService.startSoftAp(eq(null))).thenReturn(true);
+        when(mWifiService.startSoftAp(null, TEST_PACKAGE_NAME)).thenReturn(true);
         assertTrue(mWifiManager.startSoftAp(null));
 
-        when(mWifiService.startSoftAp(eq(null))).thenReturn(false);
+        when(mWifiService.startSoftAp(null, TEST_PACKAGE_NAME)).thenReturn(false);
         assertFalse(mWifiManager.startSoftAp(null));
     }
 
@@ -257,10 +257,12 @@
     @Test
     public void testStartTetheredHotspotCallsServiceWithSoftApConfig() throws Exception {
         SoftApConfiguration softApConfig = generatorTestSoftApConfig();
-        when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(true);
+        when(mWifiService.startTetheredHotspot(softApConfig, TEST_PACKAGE_NAME))
+                .thenReturn(true);
         assertTrue(mWifiManager.startTetheredHotspot(softApConfig));
 
-        when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(false);
+        when(mWifiService.startTetheredHotspot(softApConfig, TEST_PACKAGE_NAME))
+                .thenReturn(false);
         assertFalse(mWifiManager.startTetheredHotspot(softApConfig));
     }
 
@@ -270,10 +272,10 @@
      */
     @Test
     public void testStartTetheredHotspotCallsServiceWithNullConfig() throws Exception {
-        when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(true);
+        when(mWifiService.startTetheredHotspot(null, TEST_PACKAGE_NAME)).thenReturn(true);
         assertTrue(mWifiManager.startTetheredHotspot(null));
 
-        when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(false);
+        when(mWifiService.startTetheredHotspot(null, TEST_PACKAGE_NAME)).thenReturn(false);
         assertFalse(mWifiManager.startTetheredHotspot(null));
     }
 
@@ -2375,7 +2377,7 @@
     @Test
     public void testScanAvailable() throws Exception {
         mWifiManager.setScanAlwaysAvailable(true);
-        verify(mWifiService).setScanAlwaysAvailable(true);
+        verify(mWifiService).setScanAlwaysAvailable(true, TEST_PACKAGE_NAME);
 
         when(mWifiService.isScanAlwaysAvailable()).thenReturn(false);
         assertFalse(mWifiManager.isScanAlwaysAvailable());