Merge "Merge caller targets and ranked targets." into qt-dev
diff --git a/api/current.txt b/api/current.txt
index 4bbae3b..03648ea 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23079,8 +23079,8 @@
     method @NonNull public android.media.AudioAttributes.Builder setAllowedCapturePolicy(int);
     method public android.media.AudioAttributes.Builder setContentType(int);
     method public android.media.AudioAttributes.Builder setFlags(int);
+    method @NonNull public android.media.AudioAttributes.Builder setHapticChannelsMuted(boolean);
     method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
-    method public android.media.AudioAttributes.Builder setMuteHapticChannels(boolean);
     method public android.media.AudioAttributes.Builder setUsage(int);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 76b8f66..c0da879 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -553,7 +553,7 @@
   }
 
   public class NotificationManager {
-    method @NonNull public java.util.List<java.lang.String> getAllowedAssistantCapabilities();
+    method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
     method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
     method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
     method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
@@ -1242,30 +1242,29 @@
 package android.bluetooth {
 
   public final class BluetoothAdapter {
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
     method public boolean disableBLE();
     method public boolean enableBLE();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean registerMetadataListener(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothAdapter.MetadataListener, android.os.Handler);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean unregisterMetadataListener(android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
     field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
     field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
   }
 
-  public abstract static class BluetoothAdapter.MetadataListener {
-    ctor public BluetoothAdapter.MetadataListener();
-    method public void onMetadataChanged(android.bluetooth.BluetoothDevice, int, String);
+  public static interface BluetoothAdapter.OnMetadataChangedListener {
+    method public void onMetadataChanged(@NonNull android.bluetooth.BluetoothDevice, int, @Nullable byte[]);
   }
 
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
     field public static final int ACCESS_ALLOWED = 1; // 0x1
@@ -1275,21 +1274,21 @@
     field public static final int METADATA_COMPANION_APP = 4; // 0x4
     field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
     field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
-    field public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; // 0x6
+    field public static final int METADATA_IS_UNTETHERED_HEADSET = 6; // 0x6
     field public static final int METADATA_MAIN_ICON = 5; // 0x5
     field public static final int METADATA_MANUFACTURER_NAME = 0; // 0x0
     field public static final int METADATA_MAX_LENGTH = 2048; // 0x800
     field public static final int METADATA_MODEL_NAME = 1; // 0x1
     field public static final int METADATA_SOFTWARE_VERSION = 2; // 0x2
-    field public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; // 0xc
-    field public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; // 0xf
-    field public static final int METADATA_UNTHETHERED_CASE_ICON = 9; // 0x9
-    field public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; // 0xa
-    field public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; // 0xd
-    field public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; // 0x7
-    field public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; // 0xb
-    field public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; // 0xe
-    field public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; // 0x8
+    field public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; // 0xc
+    field public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; // 0xf
+    field public static final int METADATA_UNTETHERED_CASE_ICON = 9; // 0x9
+    field public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; // 0xa
+    field public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; // 0xd
+    field public static final int METADATA_UNTETHERED_LEFT_ICON = 7; // 0x7
+    field public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; // 0xb
+    field public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; // 0xe
+    field public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; // 0x8
   }
 
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -6631,8 +6630,8 @@
     method public final void adjustNotification(@NonNull android.service.notification.Adjustment);
     method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>);
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
+    method public void onAllowedAdjustmentsChanged();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method public void onCapabilitiesChanged();
     method public void onNotificationDirectReplied(@NonNull String);
     method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
     method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
diff --git a/api/test-current.txt b/api/test-current.txt
index 5d7144c..c3215a6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+    field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
     field public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
@@ -328,9 +329,9 @@
   }
 
   public class NotificationManager {
-    method public void allowAssistantCapability(String);
-    method public void disallowAssistantCapability(String);
-    method @NonNull public java.util.List<java.lang.String> getAllowedAssistantCapabilities();
+    method public void allowAssistantAdjustment(String);
+    method public void disallowAssistantAdjustment(String);
+    method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
     method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
     method public android.content.ComponentName getEffectsSuppressor();
     method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
@@ -2488,8 +2489,8 @@
     method public final void adjustNotification(@NonNull android.service.notification.Adjustment);
     method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>);
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
+    method public void onAllowedAdjustmentsChanged();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method public void onCapabilitiesChanged();
     method public void onNotificationDirectReplied(@NonNull String);
     method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
     method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 017cb6d..15d248f 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -235,6 +235,7 @@
         "tests/condition/CombinationConditionTracker_test.cpp",
         "tests/condition/SimpleConditionTracker_test.cpp",
         "tests/condition/StateTracker_test.cpp",
+        "tests/condition/ConditionTimer_test.cpp",
         "tests/metrics/OringDurationTracker_test.cpp",
         "tests/metrics/MaxDurationTracker_test.cpp",
         "tests/metrics/CountMetricProducer_test.cpp",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 82d177a..90ba7ce 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -176,23 +176,38 @@
         FlagFlipUpdateOccurred flag_flip_update_occurred = 101;
         BinaryPushStateChanged binary_push_state_changed = 102;
         DevicePolicyEvent device_policy_event = 103;
-        DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104;
-        DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported = 105;
-        DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106;
-        DocsUIFileOperationReported docs_ui_provider_file_op = 107;
-        DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request = 108;
-        DocsUILaunchReported docs_ui_launch_reported = 109;
-        DocsUIRootVisitedReported docs_ui_root_visited = 110;
-        DocsUIStartupMsReported docs_ui_startup_ms = 111;
-        DocsUIUserActionReported docs_ui_user_action_reported = 112;
+        DocsUIFileOperationCanceledReported docs_ui_file_op_canceled =
+            104 [(log_from_module) = "docsui"];
+        DocsUIFileOperationCopyMoveModeReported
+            docs_ui_file_op_copy_move_mode_reported =
+            105 [(log_from_module) = "docsui"];
+        DocsUIFileOperationFailureReported docs_ui_file_op_failure =
+            106 [(log_from_module) = "docsui"];
+        DocsUIFileOperationReported docs_ui_provider_file_op =
+            107 [(log_from_module) = "docsui"];
+        DocsUIInvalidScopedAccessRequestReported
+            docs_ui_invalid_scoped_access_request =
+            108 [(log_from_module) = "docsui"];
+        DocsUILaunchReported docs_ui_launch_reported =
+            109 [(log_from_module) = "docsui"];
+        DocsUIRootVisitedReported docs_ui_root_visited =
+            110 [(log_from_module) = "docsui"];
+        DocsUIStartupMsReported docs_ui_startup_ms =
+            111 [(log_from_module) = "docsui"];
+        DocsUIUserActionReported docs_ui_user_action_reported =
+            112 [(log_from_module) = "docsui"];
         WifiEnabledStateChanged wifi_enabled_state_changed = 113;
         WifiRunningStateChanged wifi_running_state_changed = 114;
         AppCompacted app_compacted = 115;
         NetworkDnsEventReported network_dns_event_reported = 116 [(log_from_module) = "resolv"];
-        DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = 117;
-        DocsUIPickResultReported docs_ui_pick_result_reported = 118;
-        DocsUISearchModeReported docs_ui_search_mode_reported = 119;
-        DocsUISearchTypeReported docs_ui_search_type_reported = 120;
+        DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported =
+            117 [(log_from_module) = "docsui"];
+        DocsUIPickResultReported docs_ui_pick_result_reported =
+            118 [(log_from_module) = "docsui"];
+        DocsUISearchModeReported docs_ui_search_mode_reported =
+            119 [(log_from_module) = "docsui"];
+        DocsUISearchTypeReported docs_ui_search_type_reported =
+            120 [(log_from_module) = "docsui"];
         DataStallEvent data_stall_event = 121;
         RescuePartyResetReported rescue_party_reset_reported = 122;
         SignedConfigReported signed_config_reported = 123;
diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h
new file mode 100644
index 0000000..442bc11
--- /dev/null
+++ b/cmds/statsd/src/condition/ConditionTimer.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include <stdint.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * A simple stopwatch to time the duration of condition being true.
+ *
+ * The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition
+ * changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps
+ * should be elapsedRealTime in nano seconds.
+ *
+ * Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is
+ * responsible for thread safety.
+ */
+class ConditionTimer {
+public:
+    explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
+        if (initCondition) {
+            mLastConditionTrueTimestampNs = bucketStartNs;
+        }
+    };
+
+    // Tracks how long the condition has been stayed true in the *current* bucket.
+    // When a new bucket is created, this value will be reset to 0.
+    int64_t mTimerNs = 0;
+
+    // Last elapsed real timestamp when condition turned to true
+    // When a new bucket is created and the condition is true, then the timestamp is set
+    // to be the bucket start timestamp.
+    int64_t mLastConditionTrueTimestampNs = 0;
+
+    bool mCondition = false;
+
+    int64_t newBucketStart(int64_t nextBucketStartNs) {
+        if (mCondition) {
+            mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs);
+            mLastConditionTrueTimestampNs = nextBucketStartNs;
+        }
+
+        int64_t temp = mTimerNs;
+        mTimerNs = 0;
+        return temp;
+    }
+
+    void onConditionChanged(bool newCondition, int64_t timestampNs) {
+        if (newCondition == mCondition) {
+            return;
+        }
+        mCondition = newCondition;
+        if (newCondition) {
+            mLastConditionTrueTimestampNs = timestampNs;
+        } else {
+            mTimerNs += (timestampNs - mLastConditionTrueTimestampNs);
+        }
+    }
+
+    FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
+    FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 13eee5d..d6411a7 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -276,7 +276,8 @@
 }
 
 bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
-    return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
+    // Vendor pulled atoms might be registered after we parse the config.
+    return isVendorPulledAtom(tagId) || kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
 }
 
 void StatsPullerManager::updateAlarmLocked() {
@@ -449,9 +450,8 @@
         const sp<IStatsPullerCallback>& callback) {
     AutoMutex _l(mLock);
     // Platform pullers cannot be changed.
-    if (atomTag < StatsdStats::kMaxPlatformAtomTag) {
-        VLOG("RegisterPullerCallback: atom tag %d is less than min tag %d",
-                atomTag, StatsdStats::kMaxPlatformAtomTag);
+    if (!isVendorPulledAtom(atomTag)) {
+        VLOG("RegisterPullerCallback: atom tag %d is not vendor pulled", atomTag);
         return;
     }
     VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
@@ -462,7 +462,7 @@
 void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) {
     AutoMutex _l(mLock);
     // Platform pullers cannot be changed.
-    if (atomTag < StatsdStats::kMaxPlatformAtomTag) {
+    if (!isVendorPulledAtom(atomTag)) {
         return;
     }
     StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 88ecccc..53f12ac 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -160,6 +160,12 @@
     // Max platform atom tag number.
     static const int32_t kMaxPlatformAtomTag = 100000;
 
+    // Vendor pulled atom start id.
+    static const int32_t kVendorPulledAtomStartTag = 150000;
+
+    // Max accepted atom id.
+    static const int32_t kMaxAtomTag = 200000;
+
     static const int64_t kInt64Max = 0x7fffffffffffffffLL;
 
     /**
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 18bfdfc..90a4e8b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -72,6 +72,7 @@
 const int FIELD_ID_BUCKET_NUM = 4;
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
+const int FIELD_ID_CONDITION_TRUE_NS = 10;
 
 const Value ZERO_LONG((int64_t)0);
 const Value ZERO_DOUBLE((int64_t)0);
@@ -107,7 +108,8 @@
       mCurrentBucketIsInvalid(false),
       mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
                                                       : StatsdStats::kPullMaxDelayNs),
-      mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) {
+      mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
+      mConditionTimer(mCondition == ConditionState::kTrue, timeBaseNs) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
         bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -153,6 +155,7 @@
     // flushIfNeeded to adjust start and end to bucket boundaries.
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
+    mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
     // Kicks off the puller immediately if condition is true and diff based.
     if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
         pullAndMatchEventsLocked(startTimeNs, mCondition);
@@ -293,6 +296,11 @@
                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
                                    (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
             }
+            // only write the condition timer value if the metric has a condition.
+            if (mConditionTrackerIndex >= 0) {
+                protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
+                                   (long long)bucket.mConditionTrueNs);
+            }
             for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) {
                 int index = bucket.valueIndex[i];
                 const Value& value = bucket.values[i];
@@ -386,19 +394,19 @@
             resetBase();
         }
         mCondition = newCondition;
-
     } else {
         VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
         StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
         invalidateCurrentBucket();
-        // Something weird happened. If we received another event if the future, the condition might
+        // Something weird happened. If we received another event in the future, the condition might
         // be wrong.
         mCondition = initialCondition(mConditionTrackerIndex);
     }
 
     // This part should alway be called.
     flushIfNeededLocked(eventTimeNs);
+    mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
 }
 
 void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition) {
@@ -799,12 +807,14 @@
          (int)mCurrentSlicedBucket.size());
     int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
     int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
-
+    // Close the current bucket.
+    int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
     bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
     if (isBucketLargeEnough && !mCurrentBucketIsInvalid) {
         // The current bucket is large enough to keep.
         for (const auto& slice : mCurrentSlicedBucket) {
             ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
+            bucket.mConditionTrueNs = conditionTrueDuration;
             // it will auto create new vector of ValuebucketInfo if the key is not found.
             if (bucket.valueIndex.size() > 0) {
                 auto& bucketList = mPastBuckets[slice.first];
@@ -817,6 +827,8 @@
 
     appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
     initCurrentSlicedBucket(nextBucketStartTimeNs);
+    // Update the condition timer again, in case we skipped buckets.
+    mConditionTimer.newBucketStart(nextBucketStartTimeNs);
     mCurrentBucketNum += numBucketsForward;
 }
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 12cec5d..0f56337 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -19,12 +19,13 @@
 #include <gtest/gtest_prod.h>
 #include <utils/threads.h>
 #include <list>
-#include "../anomaly/AnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../external/PullDataReceiver.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "../stats_log_util.h"
+#include "anomaly/AnomalyTracker.h"
+#include "condition/ConditionTimer.h"
+#include "condition/ConditionTracker.h"
+#include "external/PullDataReceiver.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/EventMatcherWizard.h"
+#include "stats_log_util.h"
 #include "MetricProducer.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
@@ -37,6 +38,9 @@
     int64_t mBucketEndNs;
     std::vector<int> valueIndex;
     std::vector<Value> values;
+    // If the metric has no condition, then this field is just wasted.
+    // When we tune statsd memory usage in the future, this is a candidate to optimize.
+    int64_t mConditionTrueNs;
 };
 
 
@@ -228,6 +232,8 @@
 
     const bool mSplitBucketForAppUpgrade;
 
+    ConditionTimer mConditionTimer;
+
     FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
     FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 1dfc433..54ca757 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -129,6 +129,8 @@
   optional int64 start_bucket_elapsed_millis = 5;
 
   optional int64 end_bucket_elapsed_millis = 6;
+
+  optional int64 condition_true_nanos = 10;
 }
 
 message ValueMetricData {
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index cdef874..2a18e22 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -96,6 +96,10 @@
     return atomId <= util::kMaxPushedAtomId && atomId > 1;
 }
 
+inline bool isVendorPulledAtom(int atomId) {
+    return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag;
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp
new file mode 100644
index 0000000..ea02cd3
--- /dev/null
+++ b/cmds/statsd/tests/condition/ConditionTimer_test.cpp
@@ -0,0 +1,68 @@
+// 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.
+
+#include "src/condition/ConditionTimer.h"
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static int64_t time_base = 10;
+static int64_t ct_start_time = 200;
+
+TEST(ConditionTimerTest, TestTimer_Inital_False) {
+    ConditionTimer timer(false, time_base);
+    EXPECT_EQ(false, timer.mCondition);
+    EXPECT_EQ(0, timer.mTimerNs);
+
+    EXPECT_EQ(0, timer.newBucketStart(ct_start_time));
+    EXPECT_EQ(0, timer.mTimerNs);
+
+    timer.onConditionChanged(true, ct_start_time + 5);
+    EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs);
+    EXPECT_EQ(true, timer.mCondition);
+
+    EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100));
+    EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs);
+    EXPECT_EQ(true, timer.mCondition);
+}
+
+TEST(ConditionTimerTest, TestTimer_Inital_True) {
+    ConditionTimer timer(true, time_base);
+    EXPECT_EQ(true, timer.mCondition);
+    EXPECT_EQ(0, timer.mTimerNs);
+
+    EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time));
+    EXPECT_EQ(true, timer.mCondition);
+    EXPECT_EQ(0, timer.mTimerNs);
+    EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs);
+
+    timer.onConditionChanged(false, ct_start_time + 5);
+    EXPECT_EQ(5, timer.mTimerNs);
+
+    EXPECT_EQ(5, timer.newBucketStart(ct_start_time + 100));
+    EXPECT_EQ(0, timer.mTimerNs);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index afa05a9..c12a5900 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -55,8 +55,11 @@
 
 static void assertPastBucketValuesSingleKey(
         const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
-        const std::initializer_list<int>& expectedValuesList) {
+        const std::initializer_list<int>& expectedValuesList,
+        const std::initializer_list<int64_t>& expectedDurationNsList) {
     std::vector<int> expectedValues(expectedValuesList);
+    std::vector<int64_t> expectedDurationNs(expectedDurationNsList);
+    ASSERT_EQ(expectedValues.size(), expectedDurationNs.size());
     if (expectedValues.size() == 0) {
         ASSERT_EQ(0, mPastBuckets.size());
         return;
@@ -69,10 +72,11 @@
     for (int i = 0; i < expectedValues.size(); i++) {
         EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
                 << "Values differ at index " << i;
+        EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
+                << "Condition duration value differ at index " << i;
     }
 }
 
-
 class ValueMetricProducerTestHelper {
 
  public:
@@ -237,6 +241,7 @@
     EXPECT_EQ(8, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -256,7 +261,9 @@
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
     EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
     EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -275,8 +282,11 @@
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
     EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
     EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
     EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
 }
 
 TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
@@ -326,8 +336,11 @@
     EXPECT_EQ(2UL, buckets.size());
     // Full bucket (2 - 1)
     EXPECT_EQ(1, buckets[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
     // Full bucket (5 - 3)
     EXPECT_EQ(3, buckets[1].values[0].long_value);
+    // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
+    EXPECT_EQ(2, buckets[1].mConditionTrueNs);
 }
 
 /*
@@ -385,6 +398,7 @@
     EXPECT_EQ(8, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -402,6 +416,7 @@
     EXPECT_EQ(8, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -420,6 +435,7 @@
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
     EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
 }
 
 /*
@@ -468,6 +484,7 @@
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -485,14 +502,16 @@
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
     EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
     EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
 }
 
 /*
  * Tests pulled atoms with no conditions and take zero value after reset
  */
 TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
-    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
     sp<ValueMetricProducer> valueProducer =
@@ -546,6 +565,7 @@
     EXPECT_EQ(26, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
 }
 
 /*
@@ -574,6 +594,15 @@
                 event->init();
                 data->push_back(event);
                 return true;
+            }))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+                event->write(tagId);
+                event->write(180);
+                event->init();
+                data->push_back(event);
+                return true;
             }));
 
     sp<ValueMetricProducer> valueProducer =
@@ -598,7 +627,7 @@
     event->init();
     allData.push_back(event);
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -609,7 +638,7 @@
     EXPECT_EQ(10, curInterval.value.long_value);
 
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -617,6 +646,9 @@
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(false, curInterval.hasBase);
+
+    valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1});
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
@@ -705,8 +737,7 @@
     valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
     EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(20L,
-              valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150});
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -719,10 +750,12 @@
     EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(20L,
               valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30},
+                                    {150, bucketSizeNs - 150});
 }
 
 TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
-    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_split_bucket_for_app_upgrade(false);
 
     UidMap uidMap;
@@ -791,8 +824,10 @@
     // Expect one full buckets already done and starting a partial bucket.
     EXPECT_EQ(bucket2StartTimeNs-50, valueProducer->mCurrentBucketStartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-    EXPECT_EQ(bucketStartTimeNs, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
-    EXPECT_EQ(20L, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+    EXPECT_EQ(bucketStartTimeNs,
+              valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
+                                    {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)});
     EXPECT_FALSE(valueProducer->mCondition);
 }
 
@@ -835,7 +870,7 @@
     EXPECT_EQ(30, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30});
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -872,7 +907,8 @@
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
@@ -900,7 +936,7 @@
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50});
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
 }
 
 TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -1008,7 +1044,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     // startUpdated:true sum:0 start:11
     EXPECT_EQ(true, curInterval.hasBase);
@@ -1031,7 +1068,7 @@
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(23, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
 
     // pull 3 come late.
     // The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -1050,7 +1087,7 @@
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(36, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
 }
 
 /*
@@ -1089,7 +1126,8 @@
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1098,7 +1136,7 @@
     // pull on bucket boundary come late, condition change happens before it
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
     EXPECT_EQ(false, curInterval.hasBase);
 
     // Now the alarm is delivered.
@@ -1107,7 +1145,7 @@
     allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(false, curInterval.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1160,7 +1198,8 @@
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
@@ -1169,7 +1208,7 @@
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(false, curInterval.hasBase);
@@ -1177,7 +1216,7 @@
 
     // condition changed to true again, before the pull alarm is delivered
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(130, curInterval.base.long_value);
@@ -1194,12 +1233,13 @@
     EXPECT_EQ(140, curInterval.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(10, curInterval.value.long_value);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
 
     allData.clear();
     allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 160));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30},
+                                    {bucketSizeNs - 8, bucketSizeNs - 24});
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
@@ -1230,7 +1270,8 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1242,7 +1283,7 @@
     EXPECT_EQ(10, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10});
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
@@ -1273,7 +1314,8 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1335,7 +1377,9 @@
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - 12.5) < epsilon);
+
+    EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value -
+                         12.5) < epsilon);
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
@@ -1366,7 +1410,8 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1378,7 +1423,7 @@
     EXPECT_EQ(25, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25});
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
 }
 
 TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
@@ -1410,7 +1455,8 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(10, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1449,7 +1495,7 @@
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
 }
 
 TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
@@ -1546,11 +1592,13 @@
     EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size());
 
+    EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs);
     EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
     EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]);
     EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value);
     EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]);
 
+    EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[1].mConditionTrueNs);
     EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
     EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]);
 }
@@ -1625,8 +1673,10 @@
 
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
     auto iterator = valueProducer->mPastBuckets.begin();
+    EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
     EXPECT_EQ(8, iterator->second[0].values[0].long_value);
     iterator++;
+    EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
     EXPECT_EQ(4, iterator->second[0].values[0].long_value);
 }
 
@@ -1795,7 +1845,7 @@
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(8, interval1.value.long_value);
     EXPECT_FALSE(interval1.seenNewData);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
 
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
@@ -1810,7 +1860,7 @@
     EXPECT_EQ(4, interval2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
 
     // next pull somehow did not happen, skip to end of bucket 3
     allData.clear();
@@ -1828,7 +1878,7 @@
     EXPECT_EQ(5, interval2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
 
     allData.clear();
     event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
@@ -1846,8 +1896,10 @@
     ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
     auto iterator = valueProducer->mPastBuckets.begin();
     EXPECT_EQ(9, iterator->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
     iterator++;
     EXPECT_EQ(8, iterator->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
 }
 
 TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
@@ -1932,6 +1984,15 @@
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write(tagId);
+                event->write(50);
+                event->init();
+                data->push_back(event);
+                return false;
+            }))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
                 event->write(100);
@@ -1943,10 +2004,11 @@
     sp<ValueMetricProducer> valueProducer =
             ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer->mCondition = ConditionState::kTrue;
+    // Don't directly set mCondition; the real code never does that. Go through regular code path
+    // to avoid unexpected behaviors.
+    // valueProducer->mCondition = ConditionState::kTrue;
+    valueProducer->onConditionChanged(true, bucketStartTimeNs);
 
-    vector<shared_ptr<LogEvent>> allData;
-    valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
     EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
 
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
@@ -2406,7 +2468,7 @@
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-    EXPECT_EQ(1, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1});
 }
 
 TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
@@ -2539,13 +2601,15 @@
             // Second onConditionChanged.
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5));
+                data->push_back(
+                        ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5));
                 return true;
             }))
             // Third onConditionChanged.
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7));
+                data->push_back(
+                        ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7));
                 return true;
             }));
 
@@ -2572,7 +2636,7 @@
     valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
 
     // Bucket should have been completed.
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10});
 }
 
 TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
@@ -2592,7 +2656,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
     // Bucket should have been completed.
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs});
 }
 
 TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
@@ -2619,7 +2683,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
     // Bucket should have been completed.
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs});
 }
 
 TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
@@ -2636,7 +2700,8 @@
             // notifyAppUpgrade.
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10));
+                data->push_back(
+                        ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10));
                 return true;
             }));
 
@@ -2646,7 +2711,7 @@
     valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
 
     // Bucket should have been completed.
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs});
 }
 
 TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
@@ -2678,6 +2743,12 @@
     auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 10));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
+
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2});
 }
 
 TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
@@ -2724,7 +2795,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
     // There was not global base available so all buckets are invalid.
-    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {});
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
 }
 
 static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) {
@@ -2890,6 +2961,7 @@
     EXPECT_EQ(1, report.value_metrics().data_size());
     EXPECT_EQ(1, report.value_metrics().data(0).bucket_info_size());
     EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(10, report.value_metrics().data(0).bucket_info(0).condition_true_nanos());
 }
 
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 54fe65d..9079ace 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -132,6 +132,7 @@
 import android.widget.Toast;
 import android.widget.Toolbar;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
@@ -4904,6 +4905,17 @@
             mTaskDescription.setNavigationBarColor(navigationBarColor);
         }
 
+        final int targetSdk = getApplicationInfo().targetSdkVersion;
+        final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
+        if (!targetPreQ) {
+            mTaskDescription.setEnsureStatusBarContrastWhenTransparent(a.getBoolean(
+                    R.styleable.ActivityTaskDescription_ensureStatusBarContrastWhenTransparent,
+                    false));
+            mTaskDescription.setEnsureNavigationBarContrastWhenTransparent(a.getBoolean(
+                    R.styleable.ActivityTaskDescription_ensureNavigationBarContrastWhenTransparent,
+                    true));
+        }
+
         a.recycle();
         setTaskDescription(mTaskDescription);
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 395c867..b80f781 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -986,6 +986,8 @@
         private int mColorBackground;
         private int mStatusBarColor;
         private int mNavigationBarColor;
+        private boolean mEnsureStatusBarContrastWhenTransparent;
+        private boolean mEnsureNavigationBarContrastWhenTransparent;
 
         /**
          * Creates the TaskDescription to the specified values.
@@ -998,7 +1000,7 @@
          */
         @Deprecated
         public TaskDescription(String label, Bitmap icon, int colorPrimary) {
-            this(label, icon, 0, null, colorPrimary, 0, 0, 0);
+            this(label, icon, 0, null, colorPrimary, 0, 0, 0, false, false);
             if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
                 throw new RuntimeException("A TaskDescription's primary color should be opaque");
             }
@@ -1014,7 +1016,7 @@
          *                     opaque.
          */
         public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) {
-            this(label, null, iconRes, null, colorPrimary, 0, 0, 0);
+            this(label, null, iconRes, null, colorPrimary, 0, 0, 0, false, false);
             if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
                 throw new RuntimeException("A TaskDescription's primary color should be opaque");
             }
@@ -1029,7 +1031,7 @@
          */
         @Deprecated
         public TaskDescription(String label, Bitmap icon) {
-            this(label, icon, 0, null, 0, 0, 0, 0);
+            this(label, icon, 0, null, 0, 0, 0, 0, false, false);
         }
 
         /**
@@ -1040,7 +1042,7 @@
          *                activity.
          */
         public TaskDescription(String label, @DrawableRes int iconRes) {
-            this(label, null, iconRes, null, 0, 0, 0, 0);
+            this(label, null, iconRes, null, 0, 0, 0, 0, false, false);
         }
 
         /**
@@ -1049,19 +1051,21 @@
          * @param label A label and description of the current state of this activity.
          */
         public TaskDescription(String label) {
-            this(label, null, 0, null, 0, 0, 0, 0);
+            this(label, null, 0, null, 0, 0, 0, 0, false, false);
         }
 
         /**
          * Creates an empty TaskDescription.
          */
         public TaskDescription() {
-            this(null, null, 0, null, 0, 0, 0, 0);
+            this(null, null, 0, null, 0, 0, 0, 0, false, false);
         }
 
         /** @hide */
         public TaskDescription(String label, Bitmap bitmap, int iconRes, String iconFilename,
-                int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor) {
+                int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor,
+                boolean ensureStatusBarContrastWhenTransparent,
+                boolean ensureNavigationBarContrastWhenTransparent) {
             mLabel = label;
             mIcon = bitmap;
             mIconRes = iconRes;
@@ -1070,6 +1074,9 @@
             mColorBackground = colorBackground;
             mStatusBarColor = statusBarColor;
             mNavigationBarColor = navigationBarColor;
+            mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
+            mEnsureNavigationBarContrastWhenTransparent =
+                    ensureNavigationBarContrastWhenTransparent;
         }
 
         /**
@@ -1092,6 +1099,9 @@
             mColorBackground = other.mColorBackground;
             mStatusBarColor = other.mStatusBarColor;
             mNavigationBarColor = other.mNavigationBarColor;
+            mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
+            mEnsureNavigationBarContrastWhenTransparent =
+                    other.mEnsureNavigationBarContrastWhenTransparent;
         }
 
         /**
@@ -1114,6 +1124,9 @@
             if (other.mNavigationBarColor != 0) {
                 mNavigationBarColor = other.mNavigationBarColor;
             }
+            mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
+            mEnsureNavigationBarContrastWhenTransparent =
+                    other.mEnsureNavigationBarContrastWhenTransparent;
         }
 
         private TaskDescription(Parcel source) {
@@ -1272,6 +1285,37 @@
             return mNavigationBarColor;
         }
 
+        /**
+         * @hide
+         */
+        public boolean getEnsureStatusBarContrastWhenTransparent() {
+            return mEnsureStatusBarContrastWhenTransparent;
+        }
+
+        /**
+         * @hide
+         */
+        public void setEnsureStatusBarContrastWhenTransparent(
+                boolean ensureStatusBarContrastWhenTransparent) {
+            mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
+        }
+
+        /**
+         * @hide
+         */
+        public boolean getEnsureNavigationBarContrastWhenTransparent() {
+            return mEnsureNavigationBarContrastWhenTransparent;
+        }
+
+        /**
+         * @hide
+         */
+        public void setEnsureNavigationBarContrastWhenTransparent(
+                boolean ensureNavigationBarContrastWhenTransparent) {
+            mEnsureNavigationBarContrastWhenTransparent =
+                    ensureNavigationBarContrastWhenTransparent;
+        }
+
         /** @hide */
         public void saveToXml(XmlSerializer out) throws IOException {
             if (mLabel != null) {
@@ -1332,6 +1376,8 @@
             dest.writeInt(mColorBackground);
             dest.writeInt(mStatusBarColor);
             dest.writeInt(mNavigationBarColor);
+            dest.writeBoolean(mEnsureStatusBarContrastWhenTransparent);
+            dest.writeBoolean(mEnsureNavigationBarContrastWhenTransparent);
             if (mIconFilename == null) {
                 dest.writeInt(0);
             } else {
@@ -1348,6 +1394,8 @@
             mColorBackground = source.readInt();
             mStatusBarColor = source.readInt();
             mNavigationBarColor = source.readInt();
+            mEnsureStatusBarContrastWhenTransparent = source.readBoolean();
+            mEnsureNavigationBarContrastWhenTransparent = source.readBoolean();
             mIconFilename = source.readInt() > 0 ? source.readString() : null;
         }
 
@@ -1366,8 +1414,11 @@
             return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
                     " IconRes: " + mIconRes + " IconFilename: " + mIconFilename +
                     " colorPrimary: " + mColorPrimary + " colorBackground: " + mColorBackground +
-                    " statusBarColor: " + mColorBackground +
-                    " navigationBarColor: " + mNavigationBarColor;
+                    " statusBarColor: " + mStatusBarColor + (
+                    mEnsureStatusBarContrastWhenTransparent ? " (contrast when transparent)"
+                            : "") + " navigationBarColor: " + mNavigationBarColor + (
+                    mEnsureNavigationBarContrastWhenTransparent
+                            ? " (contrast when transparent)" : "");
         }
     }
 
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7884872..b3c2429 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -70,9 +70,9 @@
     boolean areNotificationsEnabled(String pkg);
     int getPackageImportance(String pkg);
 
-    List<String> getAllowedAssistantCapabilities(String pkg);
-    void allowAssistantCapability(String adjustmentType);
-    void disallowAssistantCapability(String adjustmentType);
+    List<String> getAllowedAssistantAdjustments(String pkg);
+    void allowAssistantAdjustment(String adjustmentType);
+    void disallowAssistantAdjustment(String adjustmentType);
 
     boolean shouldHideSilentStatusIcons(String callingPkg);
     void setHideSilentStatusIcons(boolean hide);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index d54aca8..dd39376 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1206,10 +1206,10 @@
      */
     @SystemApi
     @TestApi
-    public @NonNull @Adjustment.Keys List<String> getAllowedAssistantCapabilities() {
+    public @NonNull @Adjustment.Keys List<String> getAllowedAssistantAdjustments() {
         INotificationManager service = getService();
         try {
-            return service.getAllowedAssistantCapabilities(mContext.getOpPackageName());
+            return service.getAllowedAssistantAdjustments(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1219,10 +1219,10 @@
      * @hide
      */
     @TestApi
-    public void allowAssistantCapability(String capability) {
+    public void allowAssistantAdjustment(String capability) {
         INotificationManager service = getService();
         try {
-            service.allowAssistantCapability(capability);
+            service.allowAssistantAdjustment(capability);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1232,10 +1232,10 @@
      * @hide
      */
     @TestApi
-    public void disallowAssistantCapability(String capability) {
+    public void disallowAssistantAdjustment(String capability) {
         INotificationManager service = getService();
         try {
-            service.disallowAssistantCapability(capability);
+            service.disallowAssistantAdjustment(capability);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b8a741a..31bbd16 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -20,6 +20,7 @@
 import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -37,7 +38,6 @@
 import android.content.Context;
 import android.os.BatteryStats;
 import android.os.Binder;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -61,6 +61,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -650,7 +651,7 @@
 
     private final Object mLock = new Object();
     private final Map<LeScanCallback, ScanCallback> mLeScanClients;
-    private static final Map<BluetoothDevice, List<Pair<MetadataListener, Handler>>>
+    private static final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
                 sMetadataListeners = new HashMap<>();
 
     /**
@@ -660,14 +661,15 @@
     private static final IBluetoothMetadataListener sBluetoothMetadataListener =
             new IBluetoothMetadataListener.Stub() {
         @Override
-        public void onMetadataChanged(BluetoothDevice device, int key, String value) {
+        public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
             synchronized (sMetadataListeners) {
                 if (sMetadataListeners.containsKey(device)) {
-                    List<Pair<MetadataListener, Handler>> list = sMetadataListeners.get(device);
-                    for (Pair<MetadataListener, Handler> pair : list) {
-                        MetadataListener listener = pair.first;
-                        Handler handler = pair.second;
-                        handler.post(() -> {
+                    List<Pair<OnMetadataChangedListener, Executor>> list =
+                            sMetadataListeners.get(device);
+                    for (Pair<OnMetadataChangedListener, Executor> pair : list) {
+                        OnMetadataChangedListener listener = pair.first;
+                        Executor executor = pair.second;
+                        executor.execute(() -> {
                             listener.onMetadataChanged(device, key, value);
                         });
                     }
@@ -3153,30 +3155,30 @@
     }
 
     /**
-     * Register a {@link #MetadataListener} to receive update about metadata
+     * Register a {@link #OnMetadataChangedListener} to receive update about metadata
      * changes for this {@link BluetoothDevice}.
      * Registration must be done when Bluetooth is ON and will last until
-     * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth
+     * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth
      * restarted in the middle.
      * All input parameters should not be null or {@link NullPointerException} will be triggered.
-     * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered
-     * once, double registration would cause {@link IllegalArgumentException}.
+     * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be
+     * registered once, double registration would cause {@link IllegalArgumentException}.
      *
      * @param device {@link BluetoothDevice} that will be registered
-     * @param listener {@link #MetadataListener} that will receive asynchronous callbacks
-     * @param handler the handler for listener callback
+     * @param executor the executor for listener callback
+     * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks
      * @return true on success, false on error
-     * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler}
+     * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor}
      * is null.
-     * @throws IllegalArgumentException The same {@link #MetadataListener} and
+     * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and
      * {@link BluetoothDevice} are registered twice.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener,
-            Handler handler) {
-        if (DBG) Log.d(TAG, "registerMetdataListener()");
+    public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
+            @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
+        if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");
 
         final IBluetooth service = mService;
         if (service == null) {
@@ -3189,14 +3191,15 @@
         if (device == null) {
             throw new NullPointerException("device is null");
         }
-        if (handler == null) {
-            throw new NullPointerException("handler is null");
+        if (executor == null) {
+            throw new NullPointerException("executor is null");
         }
 
         synchronized (sMetadataListeners) {
-            List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device);
+            List<Pair<OnMetadataChangedListener, Executor>> listenerList =
+                    sMetadataListeners.get(device);
             if (listenerList == null) {
-                // Create new listener/handler list for registeration
+                // Create new listener/executor list for registeration
                 listenerList = new ArrayList<>();
                 sMetadataListeners.put(device, listenerList);
             } else {
@@ -3207,7 +3210,7 @@
                 }
             }
 
-            Pair<MetadataListener, Handler> listenerPair = new Pair(listener, handler);
+            Pair<OnMetadataChangedListener, Executor> listenerPair = new Pair(listener, executor);
             listenerList.add(listenerPair);
 
             boolean ret = false;
@@ -3230,63 +3233,74 @@
     }
 
     /**
-     * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}.
+     * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}.
      * Unregistration can be done when Bluetooth is either ON or OFF.
-     * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must
-     * be called before unregisteration.
-     * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}.
+     * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)}
+     * must be called before unregisteration.
      *
-     * @param device {@link BluetoothDevice} that will be unregistered. it
+     * @param device {@link BluetoothDevice} that will be unregistered. It
+     * should not be null or {@link NullPointerException} will be triggered.
+     * @param listener {@link OnMetadataChangedListener} that will be unregistered. It
      * should not be null or {@link NullPointerException} will be triggered.
      * @return true on success, false on error
-     * @throws NullPointerException If {@code device} is null.
+     * @throws NullPointerException If {@code listener} or {@code device} is null.
      * @throws IllegalArgumentException If {@code device} has not been registered before.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean unregisterMetadataListener(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "unregisterMetdataListener()");
+    public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device,
+            @NonNull OnMetadataChangedListener listener) {
+        if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()");
         if (device == null) {
             throw new NullPointerException("device is null");
         }
+        if (listener == null) {
+            throw new NullPointerException("listener is null");
+        }
 
         synchronized (sMetadataListeners) {
-            if (sMetadataListeners.containsKey(device)) {
-                sMetadataListeners.remove(device);
-            } else {
+            if (!sMetadataListeners.containsKey(device)) {
                 throw new IllegalArgumentException("device was not registered");
             }
+            // Remove issued listener from the registered device
+            sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener)));
 
-            final IBluetooth service = mService;
-            if (service == null) {
-                // Bluetooth is OFF, do nothing to Bluetooth service.
-                return true;
-            }
-            try {
-                return service.unregisterMetadataListener(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "unregisterMetadataListener fail", e);
-                return false;
+            if (sMetadataListeners.get(device).isEmpty()) {
+                // Unregister to Bluetooth service if all listeners are removed from
+                // the registered device
+                sMetadataListeners.remove(device);
+                final IBluetooth service = mService;
+                if (service == null) {
+                    // Bluetooth is OFF, do nothing to Bluetooth service.
+                    return true;
+                }
+                try {
+                    return service.unregisterMetadataListener(device);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "unregisterMetadataListener fail", e);
+                    return false;
+                }
             }
         }
+        return true;
     }
 
     /**
-     * This abstract class is used to implement {@link BluetoothAdapter} metadata listener.
+     * This interface is used to implement {@link BluetoothAdapter} metadata listener.
      * @hide
      */
     @SystemApi
-    public abstract static class MetadataListener {
+    public interface OnMetadataChangedListener {
         /**
          * Callback triggered if the metadata of {@link BluetoothDevice} registered in
-         * {@link #registerMetadataListener}.
+         * {@link #addOnMetadataChangedListener}.
          *
          * @param device changed {@link BluetoothDevice}.
          * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
-         * @param value the new value of metadata.
+         * @param value the new value of metadata as byte array.
          */
-        public void onMetadataChanged(BluetoothDevice device, int key, String value) {
-        }
+        void onMetadataChanged(@NonNull BluetoothDevice device, int key,
+                @Nullable byte[] value);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 204d7e3..74ceeb9 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -351,6 +352,7 @@
 
     /**
      * Manufacturer name of this Bluetooth device
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
@@ -358,6 +360,7 @@
 
     /**
      * Model name of this Bluetooth device
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
@@ -365,6 +368,7 @@
 
     /**
      * Software version of this Bluetooth device
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
@@ -372,6 +376,7 @@
 
     /**
      * Hardware version of this Bluetooth device
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
@@ -379,6 +384,7 @@
 
     /**
      * Package name of the companion app, if any
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
@@ -386,6 +392,7 @@
 
     /**
      * URI to the main icon shown on the settings UI
+     * Data type should be {@link Byte} array.
      * @hide
      */
     @SystemApi
@@ -393,80 +400,91 @@
 
     /**
      * Whether this device is an untethered headset with left, right and case
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_IS_UNTHETHERED_HEADSET = 6;
+    public static final int METADATA_IS_UNTETHERED_HEADSET = 6;
 
     /**
      * URI to icon of the left headset
+     * Data type should be {@link Byte} array.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_LEFT_ICON = 7;
+    public static final int METADATA_UNTETHERED_LEFT_ICON = 7;
 
     /**
      * URI to icon of the right headset
+     * Data type should be {@link Byte} array.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8;
+    public static final int METADATA_UNTETHERED_RIGHT_ICON = 8;
 
     /**
      * URI to icon of the headset charging case
+     * Data type should be {@link Byte} array.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_CASE_ICON = 9;
+    public static final int METADATA_UNTETHERED_CASE_ICON = 9;
 
     /**
-     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
-     * is invalid, of the left headset
+     * Battery level of left headset
+     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
+     * as invalid.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10;
+    public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10;
 
     /**
-     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
-     * is invalid, of the right headset
+     * Battery level of rigth headset
+     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
+     * as invalid.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11;
+    public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11;
 
     /**
-     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
-     * is invalid, of the headset charging case
+     * Battery level of the headset charging case
+     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
+     * as invalid.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12;
+    public static final int METADATA_UNTETHERED_CASE_BATTERY = 12;
 
     /**
      * Whether the left headset is charging
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13;
+    public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13;
 
     /**
      * Whether the right headset is charging
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14;
+    public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14;
 
     /**
      * Whether the headset charging case is charging
+     * Data type should be {@String} as {@link Byte} array.
      * @hide
      */
     @SystemApi
-    public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15;
+    public static final int METADATA_UNTETHERED_CASE_CHARGING = 15;
 
     /**
-     * URI to the enhanced settings UI slice, null or empty String means
-     * the UI does not exist
+     * URI to the enhanced settings UI slice
+     * Data type should be {@String} as {@link Byte} array, null means
+     * the UI does not exist.
      * @hide
      */
     @SystemApi
@@ -2243,21 +2261,21 @@
      * {@link #BOND_NONE}.
      *
      * @param key must be within the list of BluetoothDevice.METADATA_*
-     * @param value the string data to set for key. Must be less than
+     * @param value a byte array data to set for key. Must be less than
      * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
      * @return true on success, false on error
      * @hide
     */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean setMetadata(int key, String value) {
+    public boolean setMetadata(int key, @NonNull byte[] value) {
         final IBluetooth service = sService;
         if (service == null) {
             Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
             return false;
         }
-        if (value.length() > METADATA_MAX_LENGTH) {
-            throw new IllegalArgumentException("value length is " + value.length()
+        if (value.length > METADATA_MAX_LENGTH) {
+            throw new IllegalArgumentException("value length is " + value.length
                     + ", should not over " + METADATA_MAX_LENGTH);
         }
         try {
@@ -2272,12 +2290,13 @@
      * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
      *
      * @param key must be within the list of BluetoothDevice.METADATA_*
-     * @return Metadata of the key as string, null on error or not found
+     * @return Metadata of the key as byte array, null on error or not found
      * @hide
      */
     @SystemApi
+    @Nullable
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public String getMetadata(int key) {
+    public byte[] getMetadata(int key) {
         final IBluetooth service = sService;
         if (service == null) {
             Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 7fe840c..a71f7d2 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -30,7 +30,6 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.PackageManager.DeleteFlags;
 import android.content.pm.PackageManager.InstallReason;
@@ -504,12 +503,14 @@
      *
      * <p>Staged session is active iff:
      * <ul>
-     *     <li>It is committed.
-     *     <li>It is not applied.
-     *     <li>It is not failed.
+     *     <li>It is committed, i.e. {@link SessionInfo#isCommitted()} is {@code true}, and
+     *     <li>it is not applied, i.e. {@link SessionInfo#isStagedSessionApplied()} is {@code
+     *     false}, and
+     *     <li>it is not failed, i.e. {@link SessionInfo#isStagedSessionFailed()} is {@code false}.
      * </ul>
      *
-     * <p>In case of a multi-apk session, parent session will be returned.
+     * <p>In case of a multi-apk session, reasoning above is applied to the parent session, since
+     * that is the one that should been {@link Session#commit committed}.
      */
     public @Nullable SessionInfo getActiveStagedSession() {
         final List<SessionInfo> stagedSessions = getStagedSessions();
@@ -2307,7 +2308,8 @@
         }
 
         /**
-         * Whenever this session was committed.
+         * Returns {@code true} if {@link Session#commit(IntentSender)}} was called for this
+         * session.
          */
         public boolean isCommitted() {
             return isCommitted;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b2b4e0e..bdd80e32 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -591,6 +591,8 @@
      */
     public interface Callback {
         boolean hasFeature(String feature);
+        String[] getOverlayPaths(String targetPackageName, String targetPath);
+        String[] getOverlayApks(String targetPackageName);
     }
 
     /**
@@ -607,6 +609,14 @@
         @Override public boolean hasFeature(String feature) {
             return mPm.hasSystemFeature(feature);
         }
+
+        @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) {
+            return null;
+        }
+
+        @Override public String[] getOverlayApks(String targetPackageName) {
+            return null;
+        }
     }
 
     /**
@@ -1158,7 +1168,19 @@
             }
 
             final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
-            return fromCacheEntry(bytes);
+            Package p = fromCacheEntry(bytes);
+            if (mCallback != null) {
+                String[] overlayApks = mCallback.getOverlayApks(p.packageName);
+                if (overlayApks != null && overlayApks.length > 0) {
+                    for (String overlayApk : overlayApks) {
+                        // If a static RRO is updated, return null.
+                        if (!isCacheUpToDate(new File(overlayApk), cacheFile)) {
+                            return null;
+                        }
+                    }
+                }
+            }
+            return p;
         } catch (Throwable e) {
             Slog.w(TAG, "Error reading package cache: ", e);
 
@@ -1332,7 +1354,7 @@
             final Resources res = new Resources(assets, mMetrics, null);
 
             final String[] outError = new String[1];
-            final Package pkg = parseBaseApk(res, parser, flags, outError);
+            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
             if (pkg == null) {
                 throw new PackageParserException(mParseError,
                         apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
@@ -1895,6 +1917,7 @@
      * need to consider whether they should be supported by split APKs and child
      * packages.
      *
+     * @param apkPath The package apk file path
      * @param res The resources from which to resolve values
      * @param parser The manifest parser
      * @param flags Flags how to parse
@@ -1904,7 +1927,8 @@
      * @throws XmlPullParserException
      * @throws IOException
      */
-    private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
             String[] outError) throws XmlPullParserException, IOException {
         final String splitName;
         final String pkgName;
@@ -1924,6 +1948,15 @@
             return null;
         }
 
+        if (mCallback != null) {
+            String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
+            if (overlayPaths != null && overlayPaths.length > 0) {
+                for (String overlayPath : overlayPaths) {
+                    res.getAssets().addOverlayPath(overlayPath);
+                }
+            }
+        }
+
         final Package pkg = new Package(pkgName);
 
         TypedArray sa = res.obtainAttributes(parser,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6e89797..e3b2d89 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -955,6 +955,20 @@
             "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
 
     /**
+     * Activity Action: Open the advanced power usage details page of an associated app.
+     * <p>
+     * Input: Intent's data URI set with an application name, using the
+     * "package" schema (like "package:com.my.app")
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL =
+            "android.settings.VIEW_ADVANCED_POWER_USAGE_DETAIL";
+
+    /**
      * Activity Action: Show screen for controlling background data
      * restrictions for a particular application.
      * <p>
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 8bb5f97..5977baf 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -53,5 +53,5 @@
     void onNotificationDirectReply(String key);
     void onSuggestedReplySent(String key, in CharSequence reply, int source);
     void onActionClicked(String key, in Notification.Action action, int source);
-    void onCapabilitiesChanged();
+    void onAllowedAdjustmentsChanged();
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index b4fd397..cafeb87 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -220,10 +220,10 @@
     /**
      * Implement this to know when a user has changed which features of
      * their notifications the assistant can modify.
-     * <p> Query {@link NotificationManager#getAllowedAssistantCapabilities()} to see what
+     * <p> Query {@link NotificationManager#getAllowedAssistantAdjustments()} to see what
      * {@link Adjustment adjustments} you are currently allowed to make.</p>
      */
-    public void onCapabilitiesChanged() {
+    public void onAllowedAdjustmentsChanged() {
     }
 
     /**
@@ -361,8 +361,8 @@
         }
 
         @Override
-        public void onCapabilitiesChanged() {
-            mHandler.obtainMessage(MyHandler.MSG_ON_CAPABILITIES_CHANGED).sendToTarget();
+        public void onAllowedAdjustmentsChanged() {
+            mHandler.obtainMessage(MyHandler.MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED).sendToTarget();
         }
     }
 
@@ -374,7 +374,7 @@
         public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
         public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
         public static final int MSG_ON_ACTION_INVOKED = 7;
-        public static final int MSG_ON_CAPABILITIES_CHANGED = 8;
+        public static final int MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED = 8;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -456,8 +456,8 @@
                     onActionInvoked(key, action, source);
                     break;
                 }
-                case MSG_ON_CAPABILITIES_CHANGED: {
-                    onCapabilitiesChanged();
+                case MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED: {
+                    onAllowedAdjustmentsChanged();
                     break;
                 }
             }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 6780723..3ec21e3 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1400,7 +1400,7 @@
         }
 
         @Override
-        public void onCapabilitiesChanged() {
+        public void onAllowedAdjustmentsChanged() {
             // no-op in the listener
         }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 3544a87..a9463e9 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2329,6 +2329,70 @@
         return 0;
     }
 
+    /**
+     * Sets whether the system should ensure that the status bar has enough
+     * contrast when a fully transparent background is requested.
+     *
+     * <p>If set to this value, the system will determine whether a scrim is necessary
+     * to ensure that the status bar has enough contrast with the contents of
+     * this app, and set an appropriate effective bar background color accordingly.
+     *
+     * <p>When the status bar color has a non-zero alpha value, the value of this
+     * property has no effect.
+     *
+     * @see android.R.attr#ensureStatusBarContrastWhenTransparent
+     * @hide pending API
+     */
+    public void setEnsureStatusBarContrastWhenTransparent(boolean ensureContrast) {
+    }
+
+    /**
+     * Returns whether the system is ensuring that the status bar has enough contrast when a
+     * fully transparent background is requested.
+     *
+     * <p>When the status bar color has a non-zero alpha value, the value of this
+     * property has no effect.
+     *
+     * @see android.R.attr#ensureStatusBarContrastWhenTransparent
+     * @return true, if the system is ensuring contrast, false otherwise.
+     * @hide pending API
+     */
+    public boolean isEnsureStatusBarContrastWhenTransparent() {
+        return false;
+    }
+
+    /**
+     * Sets whether the system should ensure that the navigation bar has enough
+     * contrast when a fully transparent background is requested.
+     *
+     * <p>If set to this value, the system will determine whether a scrim is necessary
+     * to ensure that the navigation bar has enough contrast with the contents of
+     * this app, and set an appropriate effective bar background color accordingly.
+     *
+     * <p>When the navigation bar color has a non-zero alpha value, the value of this
+     * property has no effect.
+     *
+     * @see android.R.attr#ensureNavigationBarContrastWhenTransparent
+     * @hide pending API
+     */
+    public void setEnsureNavigationBarContrastWhenTransparent(boolean ensureContrast) {
+    }
+
+    /**
+     * Returns whether the system is ensuring that the navigation bar has enough contrast when a
+     * fully transparent background is requested.
+     *
+     * <p>When the navigation bar color has a non-zero alpha value, the value of this
+     * property has no effect.
+     *
+     * @return true, if the system is ensuring contrast, false otherwise.
+     * @see android.R.attr#ensureNavigationBarContrastWhenTransparent
+     * @hide pending API
+     */
+    public boolean isEnsureNavigationBarContrastWhenTransparent() {
+        return false;
+    }
+
     /** @hide */
     public void setTheme(int resId) {
     }
diff --git a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
deleted file mode 100644
index bf151c3..0000000
--- a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
+++ /dev/null
@@ -1,228 +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.internal.colorextraction.drawable;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.graphics.Xfermode;
-import android.graphics.drawable.Drawable;
-import android.view.animation.DecelerateInterpolator;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.graphics.ColorUtils;
-
-/**
- * Draws a gradient based on a Palette
- */
-public class GradientDrawable extends Drawable {
-    private static final String TAG = "GradientDrawable";
-
-    private static final float CENTRALIZED_CIRCLE_1 = -2;
-    private static final int GRADIENT_RADIUS = 480; // in dp
-    private static final long COLOR_ANIMATION_DURATION = 2000;
-
-    private int mAlpha = 255;
-
-    private float mDensity;
-    private final Paint mPaint;
-    private final Rect mWindowBounds;
-    private final Splat mSplat;
-
-    private int mMainColor;
-    private int mSecondaryColor;
-    private ValueAnimator mColorAnimation;
-    private int mMainColorTo;
-    private int mSecondaryColorTo;
-
-    public GradientDrawable(@NonNull Context context) {
-        mDensity = context.getResources().getDisplayMetrics().density;
-        mSplat = new Splat(0.50f, 1.00f, GRADIENT_RADIUS, CENTRALIZED_CIRCLE_1);
-        mWindowBounds = new Rect();
-
-        mPaint = new Paint();
-        mPaint.setStyle(Paint.Style.FILL);
-    }
-
-    public void setColors(@NonNull ColorExtractor.GradientColors colors) {
-        setColors(colors.getMainColor(), colors.getSecondaryColor(), true);
-    }
-
-    public void setColors(@NonNull ColorExtractor.GradientColors colors, boolean animated) {
-        setColors(colors.getMainColor(), colors.getSecondaryColor(), animated);
-    }
-
-    public void setColors(int mainColor, int secondaryColor, boolean animated) {
-        if (mainColor == mMainColorTo && secondaryColor == mSecondaryColorTo) {
-            return;
-        }
-
-        if (mColorAnimation != null && mColorAnimation.isRunning()) {
-            mColorAnimation.cancel();
-        }
-
-        mMainColorTo = mainColor;
-        mSecondaryColorTo = mainColor;
-
-        if (animated) {
-            final int mainFrom = mMainColor;
-            final int secFrom = mSecondaryColor;
-
-            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-            anim.setDuration(COLOR_ANIMATION_DURATION);
-            anim.addUpdateListener(animation -> {
-                float ratio = (float) animation.getAnimatedValue();
-                mMainColor = ColorUtils.blendARGB(mainFrom, mainColor, ratio);
-                mSecondaryColor = ColorUtils.blendARGB(secFrom, secondaryColor, ratio);
-                buildPaints();
-                invalidateSelf();
-            });
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation, boolean isReverse) {
-                    if (mColorAnimation == animation) {
-                        mColorAnimation = null;
-                    }
-                }
-            });
-            anim.setInterpolator(new DecelerateInterpolator());
-            anim.start();
-            mColorAnimation = anim;
-        } else {
-            mMainColor = mainColor;
-            mSecondaryColor = secondaryColor;
-            buildPaints();
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        if (alpha != mAlpha) {
-            mAlpha = alpha;
-            mPaint.setAlpha(mAlpha);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public int getAlpha() {
-        return mAlpha;
-    }
-
-    @Override
-    public void setXfermode(@Nullable Xfermode mode) {
-        mPaint.setXfermode(mode);
-        invalidateSelf();
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        mPaint.setColorFilter(colorFilter);
-    }
-
-    @Override
-    public ColorFilter getColorFilter() {
-        return mPaint.getColorFilter();
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    public void setScreenSize(int width, int height) {
-        mWindowBounds.set(0, 0, width, height);
-        setBounds(0, 0, width, height);
-        buildPaints();
-    }
-
-    private void buildPaints() {
-        Rect bounds = mWindowBounds;
-        if (bounds.width() == 0) {
-            return;
-        }
-
-        float w = bounds.width();
-        float h = bounds.height();
-
-        float x = mSplat.x * w;
-        float y = mSplat.y * h;
-
-        float radius = mSplat.radius * mDensity;
-
-        // When we have only a single alpha gradient, we increase quality
-        // (avoiding banding) by merging the background solid color into
-        // the gradient directly
-        RadialGradient radialGradient = new RadialGradient(x, y, radius,
-                mSecondaryColor, mMainColor, Shader.TileMode.CLAMP);
-        mPaint.setShader(radialGradient);
-    }
-
-    @Override
-    public void draw(@NonNull Canvas canvas) {
-        Rect bounds = mWindowBounds;
-        if (bounds.width() == 0) {
-            throw new IllegalStateException("You need to call setScreenSize before drawing.");
-        }
-
-        // Splat each gradient
-        float w = bounds.width();
-        float h = bounds.height();
-
-        float x = mSplat.x * w;
-        float y = mSplat.y * h;
-
-        float radius = Math.max(w, h);
-        canvas.drawRect(x - radius, y - radius, x + radius, y + radius, mPaint);
-    }
-
-    @VisibleForTesting
-    public int getMainColor() {
-        return mMainColor;
-    }
-
-    @VisibleForTesting
-    public int getSecondaryColor() {
-        return mSecondaryColor;
-    }
-
-    static final class Splat {
-        final float x;
-        final float y;
-        final float radius;
-        final float colorIndex;
-
-        Splat(float x, float y, float radius, float colorIndex) {
-            this.x = x;
-            this.y = y;
-            this.radius = radius;
-            this.colorIndex = colorIndex;
-        }
-    }
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
new file mode 100644
index 0000000..7bd7acf
--- /dev/null
+++ b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
@@ -0,0 +1,141 @@
+/*
+ * 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.internal.colorextraction.drawable;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Xfermode;
+import android.graphics.drawable.Drawable;
+import android.view.animation.DecelerateInterpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
+
+/**
+ * Drawable used on SysUI scrims.
+ */
+public class ScrimDrawable extends Drawable {
+    private static final String TAG = "ScrimDrawable";
+    private static final long COLOR_ANIMATION_DURATION = 2000;
+
+    private final Paint mPaint;
+    private int mAlpha = 255;
+    private int mMainColor;
+    private ValueAnimator mColorAnimation;
+    private int mMainColorTo;
+
+    public ScrimDrawable() {
+        mPaint = new Paint();
+        mPaint.setStyle(Paint.Style.FILL);
+    }
+
+    /**
+     * Sets the background color.
+     * @param mainColor the color.
+     * @param animated if transition should be interpolated.
+     */
+    public void setColor(int mainColor, boolean animated) {
+        if (mainColor == mMainColorTo) {
+            return;
+        }
+
+        if (mColorAnimation != null && mColorAnimation.isRunning()) {
+            mColorAnimation.cancel();
+        }
+
+        mMainColorTo = mainColor;
+
+        if (animated) {
+            final int mainFrom = mMainColor;
+
+            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+            anim.setDuration(COLOR_ANIMATION_DURATION);
+            anim.addUpdateListener(animation -> {
+                float ratio = (float) animation.getAnimatedValue();
+                mMainColor = ColorUtils.blendARGB(mainFrom, mainColor, ratio);
+                invalidateSelf();
+            });
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation, boolean isReverse) {
+                    if (mColorAnimation == animation) {
+                        mColorAnimation = null;
+                    }
+                }
+            });
+            anim.setInterpolator(new DecelerateInterpolator());
+            anim.start();
+            mColorAnimation = anim;
+        } else {
+            mMainColor = mainColor;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        if (alpha != mAlpha) {
+            mAlpha = alpha;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public int getAlpha() {
+        return mAlpha;
+    }
+
+    @Override
+    public void setXfermode(@Nullable Xfermode mode) {
+        mPaint.setXfermode(mode);
+        invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+    }
+
+    @Override
+    public ColorFilter getColorFilter() {
+        return mPaint.getColorFilter();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        mPaint.setColor(mMainColor);
+        mPaint.setAlpha(mAlpha);
+        canvas.drawRect(getBounds(), mPaint);
+    }
+
+    @VisibleForTesting
+    public int getMainColor() {
+        return mMainColor;
+    }
+}
diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java
index b9aab21..d2e71c8 100644
--- a/core/java/com/android/internal/colorextraction/types/Tonal.java
+++ b/core/java/com/android/internal/colorextraction/types/Tonal.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.app.WallpaperColors;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.util.Log;
 import android.util.MathUtils;
@@ -51,11 +52,13 @@
 
     private static final boolean DEBUG = true;
 
-    public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0;
-    public static final int MAIN_COLOR_DARK = 0xff212121;
+    public static final int MAIN_COLOR_LIGHT = 0xffdadce0;
+    public static final int MAIN_COLOR_DARK = 0xff202124;
+    public static final int MAIN_COLOR_REGULAR = 0xff000000;
 
     private final TonalPalette mGreyPalette;
     private final ArrayList<TonalPalette> mTonalPalettes;
+    private final Context mContext;
 
     // Temporary variable to avoid allocations
     private float[] mTmpHSL = new float[3];
@@ -64,6 +67,7 @@
 
         ConfigParser parser = new ConfigParser(context);
         mTonalPalettes = parser.getTonalPalettes();
+        mContext = context;
 
         mGreyPalette = mTonalPalettes.get(0);
         mTonalPalettes.remove(0);
@@ -247,7 +251,20 @@
         boolean light = inWallpaperColors != null
                 && (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT)
                 != 0;
-        final int color = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK;
+        boolean dark = inWallpaperColors != null
+                && (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME)
+                != 0;
+        final int color;
+        final boolean inNightMode = (mContext.getResources().getConfiguration().uiMode
+                & android.content.res.Configuration.UI_MODE_NIGHT_MASK)
+                == Configuration.UI_MODE_NIGHT_YES;
+        if (light) {
+            color = MAIN_COLOR_LIGHT;
+        } else if (dark || inNightMode) {
+            color = MAIN_COLOR_DARK;
+        } else {
+            color = MAIN_COLOR_REGULAR;
+        }
         final float[] hsl = new float[3];
         ColorUtils.colorToHSL(color, hsl);
 
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index b0855f4..1aef573 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -60,7 +60,7 @@
     public static final boolean FW_SYSTEM_USER_SPLIT =
             SystemProperties.getBoolean("ro.fw.system_user_split", false);
     public static final boolean MULTIUSER_HEADLESS_SYSTEM_USER =
-            SystemProperties.getBoolean("ro.fw.multiuser.headless_system_user", false);
+            SystemProperties.getBoolean("ro.fw.multiuser.headless_system_user", true);
 
     // ------ ro.crypto.* -------- //
     public static final CryptoProperties.state_values CRYPTO_STATE =
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 1c0030d..d945e13 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -38,6 +38,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+
 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
 
 import android.animation.Animator;
@@ -125,6 +126,8 @@
     // The height of a window which has not in DIP.
     private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
 
+    private static final int SCRIM_LIGHT = 0x99ffffff; // 60% white
+
     public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
             new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
                     Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
@@ -1237,19 +1240,31 @@
 
     private int calculateStatusBarColor() {
         return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS,
-                mSemiTransparentBarColor, mWindow.mStatusBarColor);
+                mSemiTransparentBarColor, mWindow.mStatusBarColor,
+                getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+                mWindow.mEnsureStatusBarContrastWhenTransparent);
     }
 
     private int calculateNavigationBarColor() {
         return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION,
-                mSemiTransparentBarColor, mWindow.mNavigationBarColor);
+                mSemiTransparentBarColor, mWindow.mNavigationBarColor,
+                getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                mWindow.mEnsureNavigationBarContrastWhenTransparent
+                        && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim));
     }
 
     public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor,
-            int barColor) {
-        return (flags & translucentFlag) != 0 ? semiTransparentBarColor
-                : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? barColor
-                : Color.BLACK;
+            int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent) {
+        if ((flags & translucentFlag) != 0) {
+            return semiTransparentBarColor;
+        } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+            return Color.BLACK;
+        } else if (scrimTransparent && barColor == Color.TRANSPARENT) {
+            boolean light = (sysuiVis & lightSysuiFlag) != 0;
+            return light ? SCRIM_LIGHT : semiTransparentBarColor;
+        } else {
+            return barColor;
+        }
     }
 
     private int getCurrentColor(ColorViewState state) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 04559e4..16d6c52 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -50,6 +50,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcel;
@@ -247,6 +248,9 @@
     private boolean mForcedStatusBarColor = false;
     private boolean mForcedNavigationBarColor = false;
 
+    boolean mEnsureStatusBarContrastWhenTransparent;
+    boolean mEnsureNavigationBarContrastWhenTransparent;
+
     @UnsupportedAppUsage
     private CharSequence mTitle = null;
 
@@ -2439,6 +2443,7 @@
         final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
         final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
         final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
+        final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
         final boolean targetHcNeedsOptions = context.getResources().getBoolean(
                 R.bool.target_honeycomb_needs_options_menu);
         final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
@@ -2457,6 +2462,12 @@
             mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
                     0x00000000);
         }
+        if (!targetPreQ) {
+            mEnsureStatusBarContrastWhenTransparent = a.getBoolean(
+                    R.styleable.Window_ensureStatusBarContrastWhenTransparent, false);
+            mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(
+                    R.styleable.Window_ensureNavigationBarContrastWhenTransparent, true);
+        }
 
         WindowManager.LayoutParams params = getAttributes();
 
@@ -3845,6 +3856,32 @@
         return mNavigationBarDividerColor;
     }
 
+    @Override
+    public void setEnsureStatusBarContrastWhenTransparent(boolean ensureContrast) {
+        mEnsureStatusBarContrastWhenTransparent = ensureContrast;
+        if (mDecor != null) {
+            mDecor.updateColorViews(null, false /* animate */);
+        }
+    }
+
+    @Override
+    public boolean isEnsureStatusBarContrastWhenTransparent() {
+        return mEnsureStatusBarContrastWhenTransparent;
+    }
+
+    @Override
+    public void setEnsureNavigationBarContrastWhenTransparent(boolean ensureContrast) {
+        mEnsureNavigationBarContrastWhenTransparent = ensureContrast;
+        if (mDecor != null) {
+            mDecor.updateColorViews(null, false /* animate */);
+        }
+    }
+
+    @Override
+    public boolean isEnsureNavigationBarContrastWhenTransparent() {
+        return mEnsureNavigationBarContrastWhenTransparent;
+    }
+
     public void setIsStartingWindow(boolean isStartingWindow) {
         mIsStartingWindow = isStartingWindow;
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b94eb16..cc3b3a4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1637,8 +1637,8 @@
         android:label="@string/permlab_bluetooth"
         android:protectionLevel="normal" />
 
-    <!-- @SystemApi Allows an application to suspend other apps, which will prevent the user
-         from using them until they are unsuspended.
+    <!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the
+         user from using them until they are unsuspended.
          @hide
     -->
     <permission android:name="android.permission.SUSPEND_APPS"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9d48fe3..a510424 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2091,6 +2091,40 @@
              Corresponds to {@link android.view.Window#setNavigationBarDividerColor(int)}. -->
         <attr name="navigationBarDividerColor" format="color" />
 
+        <!-- Sets whether the system should ensure that the status bar has enough
+             contrast when a fully transparent background is requested.
+
+             <p>If set to this value, the system will determine whether a scrim is necessary
+             to ensure that the status bar has enough contrast with the contents of
+             this app, and set an appropriate effective bar background color accordingly.
+
+             <p>When the status bar color has a non-zero alpha value, the value of this
+             attribute has no effect.
+
+             <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q},
+             this attribute is ignored.
+
+             @see android.view.Window#setEnsureStatusBarContrastWhenTransparent 
+             @hide pendingAPI -->
+        <attr name="ensureStatusBarContrastWhenTransparent" format="boolean" />
+
+        <!-- Sets whether the system should ensure that the navigation bar has enough
+             contrast when a fully transparent background is requested.
+
+             <p>If set to this value, the system will determine whether a scrim is necessary
+             to ensure that the navigation bar has enough contrast with the contents of
+             this app, and set an appropriate effective bar background color accordingly.
+
+             <p>When the navigation bar color has a non-zero alpha value, the value of this
+             attribute has no effect.
+
+             <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q},
+             this attribute is ignored.
+
+             @see android.view.Window#setEnsureNavigationBarContrastWhenTransparent
+             @hide pendingApi -->
+        <attr name="ensureNavigationBarContrastWhenTransparent" format="boolean" />
+
         <!-- The duration, in milliseconds, of the window background fade duration
              when transitioning into or away from an Activity when called with an
              Activity Transition. Corresponds to
@@ -8980,6 +9014,10 @@
         <!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
                    color. -->
         <attr name="navigationBarColor"/>
+        <!-- @hide From Window.ensureStatusBarContrastWhenTransparent -->
+        <attr name="ensureStatusBarContrastWhenTransparent"/>
+        <!-- @hide From Window.ensureNavigationBarContrastWhenTransparent -->
+        <attr name="ensureNavigationBarContrastWhenTransparent"/>
     </declare-styleable>
 
     <declare-styleable name="Shortcut">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fe4fd13..58a4c18 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -313,14 +313,15 @@
          Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
     <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
 
-    <!-- The URL returned by ConnectivityManager#getCaptivePortalServerUrl. The actual returned
-         value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL. This is the default value
-         used if that setting is unset.
+    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
+         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
+         and if that value is empty, the framework will use a hard-coded default.
          This is *NOT* a URL that will always be used by the system network validation to detect
          captive portals: NetworkMonitor may use different strategies and will not necessarily use
          this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
          instead. -->
-    <string translatable="false" name="config_networkDefaultCaptivePortalServerUrl">http://connectivitycheck.gstatic.com/generate_204</string>
+    <!--suppress CheckTagEmptyBody -->
+    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
 
     <!-- If the hardware supports specially marking packets that caused a wakeup of the
          main CPU, set this value to the mark used. -->
@@ -3254,6 +3255,10 @@
     <!-- Controls the size of the back gesture inset. -->
     <dimen name="config_backGestureInset">0dp</dimen>
 
+    <!-- Controls whether the navbar needs a scrim with
+         {@link Window#setEnsureNavigationBarContrastWhenTransparent}. -->
+    <bool name="config_navBarNeedsScrim">true</bool>
+
     <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
          These values are in DPs and will be converted to pixel sizes internally. -->
     <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dc8c62c..0304d82 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2011,7 +2011,7 @@
   <java-symbol type="integer" name="config_networkNotifySwitchType" />
   <java-symbol type="array" name="config_networkNotifySwitches" />
   <java-symbol type="integer" name="config_networkAvoidBadWifi" />
-  <java-symbol type="string" name="config_networkDefaultCaptivePortalServerUrl" />
+  <java-symbol type="string" name="config_networkCaptivePortalServerUrl" />
   <java-symbol type="integer" name="config_networkWakeupPacketMark" />
   <java-symbol type="integer" name="config_networkWakeupPacketMask" />
   <java-symbol type="bool" name="config_apfDrop802_3Frames" />
@@ -2848,6 +2848,7 @@
   <java-symbol type="integer" name="config_navBarInteractionMode" />
   <java-symbol type="bool" name="config_navBarCanMove" />
   <java-symbol type="bool" name="config_navBarTapThrough" />
+  <java-symbol type="bool" name="config_navBarNeedsScrim" />
   <java-symbol type="dimen" name="config_backGestureInset" />
   <java-symbol type="color" name="system_bar_background_semi_transparent" />
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 78a8db42..03fb1fc 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1725,5 +1725,7 @@
     </style>
 
     <!-- @hide DeviceDefault theme for the DocumentsUI app.  -->
-    <style name="Theme.DeviceDefault.DocumentsUI" parent="Theme.DeviceDefault.DayNight" />
+    <style name="Theme.DeviceDefault.DocumentsUI" parent="Theme.DeviceDefault.DayNight">
+        <item name="actionModeCloseDrawable">@drawable/ic_clear_material</item>
+    </style>
 </resources>
diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd
index 2ef2d04..9520db7 100644
--- a/core/xsd/permission.xsd
+++ b/core/xsd/permission.xsd
@@ -20,33 +20,33 @@
            xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:element name="permissions">
         <xs:complexType>
-            <xs:sequence>
-                <xs:element name="group" type="group" maxOccurs="unbounded"/>
-                <xs:element name="permission" type="permission" maxOccurs="unbounded"/>
-                <xs:element name="assign-permission" type="assign-permission" maxOccurs="unbounded"/>
-                <xs:element name="split-permission" type="split-permission" maxOccurs="unbounded"/>
-                <xs:element name="library" type="library" maxOccurs="unbounded"/>
-                <xs:element name="feature" type="feature" maxOccurs="unbounded"/>
-                <xs:element name="unavailable-feature" type="unavailable-feature" maxOccurs="unbounded"/>
-                <xs:element name="allow-in-power-save-except-idle" type="allow-in-power-save-except-idle" maxOccurs="unbounded"/>
-                <xs:element name="allow-in-power-save" type="allow-in-power-save" maxOccurs="unbounded"/>
-                <xs:element name="allow-in-data-usage-save" type="allow-in-data-usage-save" maxOccurs="unbounded"/>
-                <xs:element name="allow-unthrottled-location" type="allow-unthrottled-location" maxOccurs="unbounded"/>
-                <xs:element name="allow-ignore-location-settings" type="allow-ignore-location-settings" maxOccurs="unbounded"/>
-                <xs:element name="allow-implicit-broadcast" type="allow-implicit-broadcast" maxOccurs="unbounded"/>
-                <xs:element name="app-link" type="app-link" maxOccurs="unbounded"/>
-                <xs:element name="system-user-whitelisted-app" type="system-user-whitelisted-app" maxOccurs="unbounded"/>
-                <xs:element name="system-user-blacklisted-app" type="system-user-blacklisted-app" maxOccurs="unbounded"/>
-                <xs:element name="default-enabled-vr-app" type="default-enabled-vr-app" maxOccurs="unbounded"/>
-                <xs:element name="backup-transport-whitelisted-service" type="backup-transport-whitelisted-service" maxOccurs="unbounded"/>
-                <xs:element name="disabled-until-used-preinstalled-carrier-associated-app" type="disabled-until-used-preinstalled-carrier-associated-app" maxOccurs="unbounded"/>
-                <xs:element name="disabled-until-used-preinstalled-carrier-app" type="disabled-until-used-preinstalled-carrier-app" maxOccurs="unbounded"/>
-                <xs:element name="privapp-permissions" type="privapp-permissions" maxOccurs="unbounded"/>
-                <xs:element name="oem-permissions" type="oem-permissions" maxOccurs="unbounded"/>
-                <xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app" maxOccurs="unbounded"/>
-                <xs:element name="allow-association" type="allow-association" maxOccurs="unbounded"/>
-                <xs:element name="bugreport-whitelisted" type="bugreport-whitelisted" maxOccurs="unbounded"/>
-            </xs:sequence>
+            <xs:choice minOccurs="0" maxOccurs="unbounded">
+                <xs:element name="group" type="group"/>
+                <xs:element name="permission" type="permission"/>
+                <xs:element name="assign-permission" type="assign-permission"/>
+                <xs:element name="split-permission" type="split-permission"/>
+                <xs:element name="library" type="library"/>
+                <xs:element name="feature" type="feature"/>
+                <xs:element name="unavailable-feature" type="unavailable-feature"/>
+                <xs:element name="allow-in-power-save-except-idle" type="allow-in-power-save-except-idle"/>
+                <xs:element name="allow-in-power-save" type="allow-in-power-save"/>
+                <xs:element name="allow-in-data-usage-save" type="allow-in-data-usage-save"/>
+                <xs:element name="allow-unthrottled-location" type="allow-unthrottled-location"/>
+                <xs:element name="allow-ignore-location-settings" type="allow-ignore-location-settings"/>
+                <xs:element name="allow-implicit-broadcast" type="allow-implicit-broadcast"/>
+                <xs:element name="app-link" type="app-link"/>
+                <xs:element name="system-user-whitelisted-app" type="system-user-whitelisted-app"/>
+                <xs:element name="system-user-blacklisted-app" type="system-user-blacklisted-app"/>
+                <xs:element name="default-enabled-vr-app" type="default-enabled-vr-app"/>
+                <xs:element name="backup-transport-whitelisted-service" type="backup-transport-whitelisted-service"/>
+                <xs:element name="disabled-until-used-preinstalled-carrier-associated-app" type="disabled-until-used-preinstalled-carrier-associated-app"/>
+                <xs:element name="disabled-until-used-preinstalled-carrier-app" type="disabled-until-used-preinstalled-carrier-app"/>
+                <xs:element name="privapp-permissions" type="privapp-permissions"/>
+                <xs:element name="oem-permissions" type="oem-permissions"/>
+                <xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app"/>
+                <xs:element name="allow-association" type="allow-association"/>
+                <xs:element name="bugreport-whitelisted" type="bugreport-whitelisted"/>
+            </xs:choice>
         </xs:complexType>
     </xs:element>
     <xs:complexType name="group">
diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt
index c25bc14..771c1df 100644
--- a/core/xsd/schema/current.txt
+++ b/core/xsd/schema/current.txt
@@ -153,31 +153,31 @@
 
   public class Permissions {
     ctor public Permissions();
-    method public java.util.List<com.android.xml.permission.configfile.AllowAssociation> getAllowAssociation();
-    method public java.util.List<com.android.xml.permission.configfile.AllowIgnoreLocationSettings> getAllowIgnoreLocationSettings();
-    method public java.util.List<com.android.xml.permission.configfile.AllowImplicitBroadcast> getAllowImplicitBroadcast();
-    method public java.util.List<com.android.xml.permission.configfile.AllowInDataUsageSave> getAllowInDataUsageSave();
-    method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSave> getAllowInPowerSave();
-    method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSaveExceptIdle> getAllowInPowerSaveExceptIdle();
-    method public java.util.List<com.android.xml.permission.configfile.AllowUnthrottledLocation> getAllowUnthrottledLocation();
-    method public java.util.List<com.android.xml.permission.configfile.AppLink> getAppLink();
-    method public java.util.List<com.android.xml.permission.configfile.AssignPermission> getAssignPermission();
-    method public java.util.List<com.android.xml.permission.configfile.BackupTransportWhitelistedService> getBackupTransportWhitelistedService();
-    method public java.util.List<com.android.xml.permission.configfile.BugreportWhitelisted> getBugreportWhitelisted();
-    method public java.util.List<com.android.xml.permission.configfile.DefaultEnabledVrApp> getDefaultEnabledVrApp();
-    method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierApp> getDisabledUntilUsedPreinstalledCarrierApp();
-    method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierAssociatedApp> getDisabledUntilUsedPreinstalledCarrierAssociatedApp();
-    method public java.util.List<com.android.xml.permission.configfile.Feature> getFeature();
-    method public java.util.List<com.android.xml.permission.configfile.Group> getGroup();
-    method public java.util.List<com.android.xml.permission.configfile.HiddenApiWhitelistedApp> getHiddenApiWhitelistedApp();
-    method public java.util.List<com.android.xml.permission.configfile.Library> getLibrary();
-    method public java.util.List<com.android.xml.permission.configfile.OemPermissions> getOemPermissions();
-    method public java.util.List<com.android.xml.permission.configfile.Permission> getPermission();
-    method public java.util.List<com.android.xml.permission.configfile.PrivappPermissions> getPrivappPermissions();
-    method public java.util.List<com.android.xml.permission.configfile.SplitPermission> getSplitPermission();
-    method public java.util.List<com.android.xml.permission.configfile.SystemUserBlacklistedApp> getSystemUserBlacklistedApp();
-    method public java.util.List<com.android.xml.permission.configfile.SystemUserWhitelistedApp> getSystemUserWhitelistedApp();
-    method public java.util.List<com.android.xml.permission.configfile.UnavailableFeature> getUnavailableFeature();
+    method public java.util.List<com.android.xml.permission.configfile.AllowAssociation> getAllowAssociation_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AllowIgnoreLocationSettings> getAllowIgnoreLocationSettings_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AllowImplicitBroadcast> getAllowImplicitBroadcast_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AllowInDataUsageSave> getAllowInDataUsageSave_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSaveExceptIdle> getAllowInPowerSaveExceptIdle_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSave> getAllowInPowerSave_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AllowUnthrottledLocation> getAllowUnthrottledLocation_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AppLink> getAppLink_optional();
+    method public java.util.List<com.android.xml.permission.configfile.AssignPermission> getAssignPermission_optional();
+    method public java.util.List<com.android.xml.permission.configfile.BackupTransportWhitelistedService> getBackupTransportWhitelistedService_optional();
+    method public java.util.List<com.android.xml.permission.configfile.BugreportWhitelisted> getBugreportWhitelisted_optional();
+    method public java.util.List<com.android.xml.permission.configfile.DefaultEnabledVrApp> getDefaultEnabledVrApp_optional();
+    method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierApp> getDisabledUntilUsedPreinstalledCarrierApp_optional();
+    method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierAssociatedApp> getDisabledUntilUsedPreinstalledCarrierAssociatedApp_optional();
+    method public java.util.List<com.android.xml.permission.configfile.Feature> getFeature_optional();
+    method public java.util.List<com.android.xml.permission.configfile.Group> getGroup_optional();
+    method public java.util.List<com.android.xml.permission.configfile.HiddenApiWhitelistedApp> getHiddenApiWhitelistedApp_optional();
+    method public java.util.List<com.android.xml.permission.configfile.Library> getLibrary_optional();
+    method public java.util.List<com.android.xml.permission.configfile.OemPermissions> getOemPermissions_optional();
+    method public java.util.List<com.android.xml.permission.configfile.Permission> getPermission_optional();
+    method public java.util.List<com.android.xml.permission.configfile.PrivappPermissions> getPrivappPermissions_optional();
+    method public java.util.List<com.android.xml.permission.configfile.SplitPermission> getSplitPermission_optional();
+    method public java.util.List<com.android.xml.permission.configfile.SystemUserBlacklistedApp> getSystemUserBlacklistedApp_optional();
+    method public java.util.List<com.android.xml.permission.configfile.SystemUserWhitelistedApp> getSystemUserWhitelistedApp_optional();
+    method public java.util.List<com.android.xml.permission.configfile.UnavailableFeature> getUnavailableFeature_optional();
   }
 
   public class PrivappPermissions {
diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp
new file mode 100644
index 0000000..9cf68c1
--- /dev/null
+++ b/core/xsd/vts/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "vts_permission_validate_test",
+    srcs: [
+        "ValidatePermission.cpp"
+    ],
+    static_libs: [
+        "android.hardware.audio.common.test.utility",
+        "libxml2",
+    ],
+    shared_libs: [
+        "liblog",
+	"libbase",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/core/xsd/vts/Android.mk b/core/xsd/vts/Android.mk
new file mode 100644
index 0000000..a5754a4
--- /dev/null
+++ b/core/xsd/vts/Android.mk
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsValidatePermission
+include test/vts/tools/build/Android.host_config.mk
diff --git a/core/xsd/vts/AndroidTest.xml b/core/xsd/vts/AndroidTest.xml
new file mode 100644
index 0000000..e5cc9a0
--- /dev/null
+++ b/core/xsd/vts/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<configuration description="Config for VTS VtsValidatePermission.">
+    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+        <option name="push" value="DATA/etc/permission.xsd->/data/local/tmp/permission.xsd"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+        <option name="test-module-name" value="VtsValidatePermission"/>
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_permission_validate_test/vts_permission_validate_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_permission_validate_test/vts_permission_validate_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="test-timeout" value="30s"/>
+    </test>
+</configuration>
diff --git a/core/xsd/vts/ValidatePermission.cpp b/core/xsd/vts/ValidatePermission.cpp
new file mode 100644
index 0000000..3499689
--- /dev/null
+++ b/core/xsd/vts/ValidatePermission.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#include <dirent.h>
+#include <regex>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string>
+
+#include "android-base/logging.h"
+#include "utility/ValidateXml.h"
+
+static void get_files_in_dirs(const char* dir_path, std::vector<std::string>& files) {
+    DIR* d;
+    struct dirent* de;
+
+    d = opendir(dir_path);
+    if (d == nullptr) {
+        return;
+    }
+
+    while ((de = readdir(d))) {
+        if (de->d_type != DT_REG) {
+            continue;
+        }
+        if (std::regex_match(de->d_name, std::regex("(.*)(.xml)"))) {
+            files.push_back(de->d_name);
+        }
+    }
+    closedir(d);
+}
+
+TEST(CheckConfig, permission) {
+    RecordProperty("description",
+                   "Verify that the permission file "
+                   "is valid according to the schema");
+
+    const char* location = "/vendor/etc/permissions";
+
+    std::vector<std::string> files;
+    get_files_in_dirs(location, files);
+
+    for (std::string file_name : files) {
+        EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(file_name.c_str(), {location},
+                                                "/data/local/tmp/permission.xsd");
+    }
+}
diff --git a/media/OWNERS b/media/OWNERS
index 72c8952..a33a990 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,3 +1,4 @@
+andrewlewis@google.com
 chz@google.com
 dwkang@google.com
 elaurent@google.com
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index db33e82..72c18f6 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -546,7 +546,7 @@
             @Override
             void process() {
                 if (getState() == PLAYER_STATE_PLAYING) {
-                    pause();
+                    native_pause();
                 }
                 playNextDataSource();
             }
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 3a33678..9d4bce7 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -893,7 +893,7 @@
          * @param muted true to force muting haptic channels.
          * @return the same Builder instance.
          */
-        public Builder setMuteHapticChannels(boolean muted) {
+        public @NonNull Builder setHapticChannelsMuted(boolean muted) {
             mMuteHapticChannels = muted;
             return this;
         }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
index fcbda1d..43d7d8f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
@@ -326,6 +326,8 @@
         } else if (status == STATUS_READY) {
             startForeground(NOTIFICATION_ID,
                     buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
+        } else {
+            stopSelf();
         }
     }
 
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 262e6f6..5817118 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -39,6 +39,7 @@
         ":services-networkstack-shared-srcs",
     ],
     static_libs: [
+        "androidx.annotation_annotation",
         "ipmemorystore-client",
         "netd_aidl_interface-java",
         "networkstack-aidl-interfaces-java",
diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml
index 52425e5..90f96e0 100644
--- a/packages/NetworkStack/res/values/config.xml
+++ b/packages/NetworkStack/res/values/config.xml
@@ -1,5 +1,38 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <!-- Captive portal http url -->
-    <string name="config_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string>
+    <!--
+    OEMs that wish to change the below settings must do so via a runtime resource overlay package
+    and *NOT* by changing this file. This file is part of the NetworkStack mainline module.
+    The overlays must apply to the config_* values, not the default_* values. The default_*
+    values are meant to be the default when no other configuration is specified.
+    -->
+
+    <!-- HTTP URL for network validation, to use for detecting captive portals. -->
+    <string name="default_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string>
+
+    <!-- HTTPS URL for network validation, to use for confirming internet connectivity. -->
+    <string name="default_captive_portal_https_url" translatable="false">https://www.google.com/generate_204</string>
+
+    <!-- List of fallback URLs to use for detecting captive portals. -->
+    <string-array name="default_captive_portal_fallback_urls" translatable="false">
+        <item>http://www.google.com/gen_204</item>
+        <item>http://play.googleapis.com/generate_204</item>
+    </string-array>
+
+    <!-- List of fallback probe specs to use for detecting captive portals.
+         This is an alternative to fallback URLs that provides more flexibility on detection rules.
+         Empty, so unused by default. -->
+    <string-array name="default_captive_portal_fallback_probe_specs" translatable="false">
+    </string-array>
+
+    <!-- Configuration hooks for the above settings.
+         Empty by default but may be overridden by RROs. -->
+    <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook -->
+    <string name="config_captive_portal_http_url" translatable="false"></string>
+    <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook -->
+    <string name="config_captive_portal_https_url" translatable="false"></string>
+    <string-array name="config_captive_portal_fallback_urls" translatable="false">
+    </string-array>
+    <string-array name="config_captive_portal_fallback_probe_specs" translatable="false">
+    </string-array>
 </resources>
\ No newline at end of file
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 7b77d66..588dcf2 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -28,6 +28,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.captiveportal.CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs;
 import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE;
 import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS;
 import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
@@ -52,6 +53,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
@@ -91,6 +93,9 @@
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.annotation.ArrayRes;
+import androidx.annotation.StringRes;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.RingBufferIndices;
 import com.android.internal.util.State;
@@ -105,7 +110,6 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -113,6 +117,7 @@
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
 
 /**
  * {@hide}
@@ -122,15 +127,6 @@
     private static final boolean DBG  = true;
     private static final boolean VDBG = false;
     private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
-    // TODO: use another permission for CaptivePortalLoginActivity once it has its own certificate
-    private static final String PERMISSION_NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
-    // Default configuration values for captive portal detection probes.
-    // TODO: append a random length parameter to the default HTTPS url.
-    // TODO: randomize browser version ids in the default User-Agent String.
-    private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204";
-    private static final String DEFAULT_FALLBACK_URL  = "http://www.google.com/gen_204";
-    private static final String DEFAULT_OTHER_FALLBACK_URLS =
-            "http://play.googleapis.com/generate_204";
     private static final String DEFAULT_USER_AGENT    = "Mozilla/5.0 (X11; Linux x86_64) "
                                                       + "AppleWebKit/537.36 (KHTML, like Gecko) "
                                                       + "Chrome/60.0.3112.32 Safari/537.36";
@@ -378,7 +374,7 @@
         mUseHttps = getUseHttpsValidation();
         mCaptivePortalUserAgent = getCaptivePortalUserAgent();
         mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
-        mCaptivePortalHttpUrl = makeURL(deps.getCaptivePortalServerHttpUrl(context));
+        mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl());
         mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
         mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
         mRandom = deps.getRandom();
@@ -1178,8 +1174,22 @@
     }
 
     private String getCaptivePortalServerHttpsUrl() {
-        return mDependencies.getSetting(mContext,
-                Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
+        return getSettingFromResource(mContext, R.string.config_captive_portal_https_url,
+                R.string.default_captive_portal_https_url,
+                Settings.Global.CAPTIVE_PORTAL_HTTPS_URL);
+    }
+
+    /**
+     * Get the captive portal server HTTP URL that is configured on the device.
+     *
+     * NetworkMonitor does not use {@link ConnectivityManager#getCaptivePortalServerUrl()} as
+     * it has its own updatable strategies to detect captive portals. The framework only advises
+     * on one URL that can be used, while NetworkMonitor may implement more complex logic.
+     */
+    public String getCaptivePortalServerHttpUrl() {
+        return getSettingFromResource(mContext, R.string.config_captive_portal_http_url,
+                R.string.default_captive_portal_http_url,
+                Settings.Global.CAPTIVE_PORTAL_HTTP_URL);
     }
 
     private int getConsecutiveDnsTimeoutThreshold() {
@@ -1208,24 +1218,23 @@
 
     private URL[] makeCaptivePortalFallbackUrls() {
         try {
-            String separator = ",";
-            String firstUrl = mDependencies.getSetting(mContext,
-                    Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
-            String joinedUrls = firstUrl + separator + mDependencies.getSetting(mContext,
-                    Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
-                    DEFAULT_OTHER_FALLBACK_URLS);
-            List<URL> urls = new ArrayList<>();
-            for (String s : joinedUrls.split(separator)) {
-                URL u = makeURL(s);
-                if (u == null) {
-                    continue;
-                }
-                urls.add(u);
+            final String firstUrl = mDependencies.getSetting(mContext,
+                    Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, null);
+
+            final URL[] settingProviderUrls;
+            if (!TextUtils.isEmpty(firstUrl)) {
+                final String otherUrls = mDependencies.getSetting(mContext,
+                        Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, "");
+                // otherUrls may be empty, but .split() ignores trailing empty strings
+                final String separator = ",";
+                final String[] urls = (firstUrl + separator + otherUrls).split(separator);
+                settingProviderUrls = convertStrings(urls, this::makeURL, new URL[0]);
+            } else {
+                settingProviderUrls = new URL[0];
             }
-            if (urls.isEmpty()) {
-                Log.e(TAG, String.format("could not create any url from %s", joinedUrls));
-            }
-            return urls.toArray(new URL[urls.size()]);
+
+            return getArrayConfig(settingProviderUrls, R.array.config_captive_portal_fallback_urls,
+                    R.array.default_captive_portal_fallback_urls, this::makeURL);
         } catch (Exception e) {
             // Don't let a misconfiguration bootloop the system.
             Log.e(TAG, "Error parsing configured fallback URLs", e);
@@ -1237,15 +1246,14 @@
         try {
             final String settingsValue = mDependencies.getSetting(
                     mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null);
-            // Probe specs only used if configured in settings
-            if (TextUtils.isEmpty(settingsValue)) {
-                return null;
-            }
+            final CaptivePortalProbeSpec[] emptySpecs = new CaptivePortalProbeSpec[0];
+            final CaptivePortalProbeSpec[] providerValue = TextUtils.isEmpty(settingsValue)
+                    ? emptySpecs
+                    : parseCaptivePortalProbeSpecs(settingsValue).toArray(emptySpecs);
 
-            final Collection<CaptivePortalProbeSpec> specs =
-                    CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs(settingsValue);
-            final CaptivePortalProbeSpec[] specsArray = new CaptivePortalProbeSpec[specs.size()];
-            return specs.toArray(specsArray);
+            return getArrayConfig(providerValue, R.array.config_captive_portal_fallback_probe_specs,
+                    R.array.default_captive_portal_fallback_probe_specs,
+                    CaptivePortalProbeSpec::parseSpecOrNull);
         } catch (Exception e) {
             // Don't let a misconfiguration bootloop the system.
             Log.e(TAG, "Error parsing configured fallback probe specs", e);
@@ -1253,6 +1261,83 @@
         }
     }
 
+    /**
+     * Read a setting from a resource or the settings provider.
+     *
+     * <p>The configuration resource is prioritized, then the provider value, then the default
+     * resource value.
+     * @param context The context
+     * @param configResource The resource id for the configuration parameter
+     * @param defaultResource The resource id for the default value
+     * @param symbol The symbol in the settings provider
+     * @return The best available value
+     */
+    @NonNull
+    private String getSettingFromResource(@NonNull final Context context,
+            @StringRes int configResource, @StringRes int defaultResource,
+            @NonNull String symbol) {
+        final Resources res = context.getResources();
+        String setting = res.getString(configResource);
+
+        if (!TextUtils.isEmpty(setting)) return setting;
+
+        setting = mDependencies.getSetting(context, symbol, null);
+        if (!TextUtils.isEmpty(setting)) return setting;
+
+        return res.getString(defaultResource);
+    }
+
+    /**
+     * Get an array configuration from resources or the settings provider.
+     *
+     * <p>The configuration resource is prioritized, then the provider values, then the default
+     * resource values.
+     * @param providerValue Values obtained from the setting provider.
+     * @param configResId ID of the configuration resource.
+     * @param defaultResId ID of the default resource.
+     * @param resourceConverter Converter from the resource strings to stored setting class. Null
+     *                          return values are ignored.
+     */
+    private <T> T[] getArrayConfig(@NonNull T[] providerValue, @ArrayRes int configResId,
+            @ArrayRes int defaultResId, @NonNull Function<String, T> resourceConverter) {
+        final Resources res = mContext.getResources();
+        String[] configValue = res.getStringArray(configResId);
+
+        if (configValue.length == 0) {
+            if (providerValue.length > 0) {
+                return providerValue;
+            }
+
+            configValue = res.getStringArray(defaultResId);
+        }
+
+        return convertStrings(configValue, resourceConverter, Arrays.copyOf(providerValue, 0));
+    }
+
+    /**
+     * Convert a String array to an array of some other type using the specified converter.
+     *
+     * <p>Any null value, or value for which the converter throws a {@link RuntimeException}, will
+     * not be added to the output array, so the output array may be smaller than the input.
+     */
+    private <T> T[] convertStrings(
+            @NonNull String[] strings, Function<String, T> converter, T[] emptyArray) {
+        final ArrayList<T> convertedValues = new ArrayList<>(strings.length);
+        for (String configString : strings) {
+            T convertedValue = null;
+            try {
+                convertedValue = converter.apply(configString);
+            } catch (Exception e) {
+                Log.e(TAG, "Error parsing configuration", e);
+                // Fall through
+            }
+            if (convertedValue != null) {
+                convertedValues.add(convertedValue);
+            }
+        }
+        return convertedValues.toArray(emptyArray);
+    }
+
     private String getCaptivePortalUserAgent() {
         return mDependencies.getSetting(mContext,
                 Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
@@ -1694,19 +1779,6 @@
         }
 
         /**
-         * Get the captive portal server HTTP URL that is configured on the device.
-         *
-         * NetworkMonitor does not use {@link ConnectivityManager#getCaptivePortalServerUrl()} as
-         * it has its own updatable strategies to detect captive portals. The framework only advises
-         * on one URL that can be used, while  NetworkMonitor may implement more complex logic.
-         */
-        public String getCaptivePortalServerHttpUrl(Context context) {
-            final String defaultUrl =
-                    context.getResources().getString(R.string.config_captive_portal_http_url);
-            return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(context, defaultUrl);
-        }
-
-        /**
          * Get the value of a global integer setting.
          * @param symbol Name of the setting
          * @param defaultValue Value to return if the setting is not defined.
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 9f6c7f8..48c91bf 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -34,7 +34,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
@@ -49,6 +48,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.INetworkMonitorCallbacks;
 import android.net.InetAddresses;
@@ -99,6 +99,7 @@
     private static final String LOCATION_HEADER = "location";
 
     private @Mock Context mContext;
+    private @Mock Resources mResources;
     private @Mock IpConnectivityLog mLogger;
     private @Mock SharedLog mValidationLogger;
     private @Mock NetworkInfo mNetworkInfo;
@@ -153,14 +154,20 @@
                 .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
         when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS),
                 anyInt())).thenReturn(1);
-        when(mDependencies.getCaptivePortalServerHttpUrl(any())).thenReturn(TEST_HTTP_URL);
-        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL),
-                anyString())).thenReturn(TEST_HTTPS_URL);
+        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()))
+                .thenReturn(TEST_HTTP_URL);
+        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any()))
+                .thenReturn(TEST_HTTPS_URL);
+
         doReturn(mNetwork).when(mNetwork).getPrivateDnsBypassingCopy();
 
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
         when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
+        when(mContext.getResources()).thenReturn(mResources);
+
+        when(mResources.getString(anyInt())).thenReturn("");
+        when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
 
         when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI);
         setFallbackUrl(TEST_FALLBACK_URL);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 46e9129..0d972c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -28,6 +28,8 @@
     public static final boolean V = false; // verbose logging
     public static final boolean D = true;  // regular logging
 
+    public static final int META_INT_ERROR = -1;
+
     private static ErrorListener sErrorListener;
 
     public static int getConnectionStateSummary(int connectionState) {
@@ -133,20 +135,16 @@
         final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
                 context, cachedDevice);
         final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
-        final boolean untetheredHeadset = bluetoothDevice != null
-                ? Boolean.parseBoolean(bluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
-                : false;
+        final boolean untetheredHeadset = getBooleanMetaData(
+                bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET);
         final int iconSize = context.getResources().getDimensionPixelSize(
                 R.dimen.bt_nearby_icon_size);
         final Resources resources = context.getResources();
 
         // Deal with untethered headset
         if (untetheredHeadset) {
-            final String uriString = bluetoothDevice != null
-                    ? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)
-                    : null;
-            final Uri iconUri = uriString != null ? Uri.parse(uriString) : null;
+            final Uri iconUri = getUriMetaData(bluetoothDevice,
+                    BluetoothDevice.METADATA_MAIN_ICON);
             if (iconUri != null) {
                 try {
                     context.getContentResolver().takePersistableUriPermission(iconUri,
@@ -194,4 +192,77 @@
 
         return adaptiveIcon;
     }
+
+    /**
+     * Get boolean Bluetooth metadata
+     *
+     * @param bluetoothDevice the BluetoothDevice to get metadata
+     * @param key key value within the list of BluetoothDevice.METADATA_*
+     * @return the boolean metdata
+     */
+    public static boolean getBooleanMetaData(BluetoothDevice bluetoothDevice, int key) {
+        if (bluetoothDevice == null) {
+            return false;
+        }
+        final byte[] data = bluetoothDevice.getMetadata(key);
+        if (data == null) {
+            return false;
+        }
+        return Boolean.parseBoolean(new String(data));
+    }
+
+    /**
+     * Get String Bluetooth metadata
+     *
+     * @param bluetoothDevice the BluetoothDevice to get metadata
+     * @param key key value within the list of BluetoothDevice.METADATA_*
+     * @return the String metdata
+     */
+    public static String getStringMetaData(BluetoothDevice bluetoothDevice, int key) {
+        if (bluetoothDevice == null) {
+            return null;
+        }
+        final byte[] data = bluetoothDevice.getMetadata(key);
+        if (data == null) {
+            return null;
+        }
+        return new String(data);
+    }
+
+    /**
+     * Get integer Bluetooth metadata
+     *
+     * @param bluetoothDevice the BluetoothDevice to get metadata
+     * @param key key value within the list of BluetoothDevice.METADATA_*
+     * @return the int metdata
+     */
+    public static int getIntMetaData(BluetoothDevice bluetoothDevice, int key) {
+        if (bluetoothDevice == null) {
+            return META_INT_ERROR;
+        }
+        final byte[] data = bluetoothDevice.getMetadata(key);
+        if (data == null) {
+            return META_INT_ERROR;
+        }
+        try {
+            return Integer.parseInt(new String(data));
+        } catch (NumberFormatException e) {
+            return META_INT_ERROR;
+        }
+    }
+
+    /**
+     * Get URI Bluetooth metadata
+     *
+     * @param bluetoothDevice the BluetoothDevice to get metadata
+     * @param key key value within the list of BluetoothDevice.METADATA_*
+     * @return the URI metdata
+     */
+    public static Uri getUriMetaData(BluetoothDevice bluetoothDevice, int key) {
+        String data = getStringMetaData(bluetoothDevice, key);
+        if (data == null) {
+            return null;
+        }
+        return Uri.parse(data);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 2405666..ff34578 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -881,16 +881,12 @@
         //when profile is connected, information would be available
         if (profileConnected) {
             // Update Meta data for connected device
-            if (Boolean.parseBoolean(
-                    mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))) {
-                try {
-                    leftBattery = Integer.parseInt(
-                            mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY));
-                    rightBattery = Integer.parseInt(mDevice.getMetadata(
-                                    BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY));
-                } catch (NumberFormatException e) {
-                    Log.d(TAG, "Parse error for unthethered battery level.");
-                }
+            if (BluetoothUtils.getBooleanMetaData(
+                    mDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
+                leftBattery = BluetoothUtils.getIntMetaData(mDevice,
+                        BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY);
+                rightBattery = BluetoothUtils.getIntMetaData(mDevice,
+                        BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY);
             }
 
             // Set default string with battery level in device connected situation.
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index 530c73a..fb5c16b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -19,6 +19,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.PowerManager;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -33,7 +34,25 @@
 public class BatterySaverUtils {
 
     private static final String TAG = "BatterySaverUtils";
-    public static final String EXTRA_CONFIRM_ONLY = "extra_confirm_only";
+    /**
+     * When set to "true" the notification will be a generic confirm message instead of asking the
+     * user if they want to turn on battery saver. If set to false the dialog will specifically
+     * talk about turning on battery saver and provide a button for taking the action.
+     */
+    public static final String EXTRA_CONFIRM_TEXT_ONLY = "extra_confirm_only";
+    /**
+     * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". Can be set to any of the values in
+     * {@link PowerManager.AutoPowerSaveModeTriggers}. If set the dialog will set the power
+     * save mode trigger to the specified value after the user acknowledges the trigger.
+     */
+    public static final String EXTRA_POWER_SAVE_MODE_TRIGGER = "extra_power_save_mode_trigger";
+    /**
+     * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". can be set to any value between
+     * 0-100 that will be used if {@link #EXTRA_POWER_SAVE_MODE_TRIGGER} is
+     * {@link PowerManager#POWER_SAVE_MODE_TRIGGER_PERCENTAGE}.
+     */
+    public static final String EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL =
+            "extra_power_save_mode_trigger_level";
 
     private BatterySaverUtils() {
     }
@@ -98,7 +117,10 @@
         }
         final ContentResolver cr = context.getContentResolver();
 
-        if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context, false)) {
+        final Bundle confirmationExtras = new Bundle(1);
+        confirmationExtras.putBoolean(EXTRA_CONFIRM_TEXT_ONLY, false);
+        if (enable && needFirstTimeWarning
+                && maybeShowBatterySaverConfirmation(context, confirmationExtras)) {
             return false;
         }
         if (enable && !needFirstTimeWarning) {
@@ -118,7 +140,7 @@
                         && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0
                         && Secure.getInt(cr,
                         Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {
-                    showAutoBatterySaverSuggestion(context, false);
+                    showAutoBatterySaverSuggestion(context, confirmationExtras);
                 }
             }
 
@@ -129,34 +151,36 @@
 
     /**
      * Shows the battery saver confirmation warning if it hasn't been acknowledged by the user in
-     * the past before. When confirmOnly is true, the dialog will have generic info about battery
-     * saver but will only update that the user has been shown the notification and take no
-     * further action. if confirmOnly is false it will show a more specific version of the dialog
-     * that toggles battery saver when acknowledged
+     * the past before. Various extras can be provided that will change the behavior of this
+     * notification as well as the ui for it.
      * @param context A valid context
-     * @param confirmOnly Whether to show the actionless generic dialog (true) or the specific one
-     * that toggles battery saver (false)
+     * @param extras Any extras to include in the intent to trigger this confirmation that will
+     * help the system disambiguate what to show/do
+     *
      * @return True if it showed the notification because it has not been previously acknowledged.
+     * @see #EXTRA_CONFIRM_TEXT_ONLY
+     * @see #EXTRA_POWER_SAVE_MODE_TRIGGER
+     * @see #EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL
      */
-    public static boolean maybeShowBatterySaverConfirmation(Context context, boolean confirmOnly) {
+    public static boolean maybeShowBatterySaverConfirmation(Context context, Bundle extras) {
         if (Secure.getInt(context.getContentResolver(),
                 Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) {
             return false; // Already shown.
         }
         context.sendBroadcast(
-                getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, confirmOnly));
+                getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, extras));
         return true;
     }
 
-    private static void showAutoBatterySaverSuggestion(Context context, boolean confirmOnly) {
-        context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, confirmOnly));
+    private static void showAutoBatterySaverSuggestion(Context context, Bundle extras) {
+        context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, extras));
     }
 
-    private static Intent getSystemUiBroadcast(String action, boolean confirmOnly) {
+    private static Intent getSystemUiBroadcast(String action, Bundle extras) {
         final Intent i = new Intent(action);
         i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         i.setPackage(SYSUI_PACKAGE);
-        i.putExtra(EXTRA_CONFIRM_ONLY, confirmOnly);
+        i.putExtras(extras);
         return i;
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index b228cf7..3a95852c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -25,6 +25,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.util.Pair;
 
 import com.android.settingslib.widget.AdaptiveIcon;
@@ -47,6 +48,9 @@
     private BluetoothDevice mBluetoothDevice;
 
     private Context mContext;
+    private static final String STRING_METADATA = "string_metadata";
+    private static final String BOOL_METADATA = "true";
+    private static final String INT_METADATA = "25";
 
     @Before
     public void setUp() {
@@ -78,7 +82,7 @@
     @Test
     public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
         when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false");
+                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("false".getBytes());
         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
         when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
 
@@ -86,4 +90,64 @@
                 RuntimeEnvironment.application,
                 mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
     }
-}
\ No newline at end of file
+
+    @Test
+    public void getStringMetaData_hasMetaData_getCorrectMetaData() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).thenReturn(
+                STRING_METADATA.getBytes());
+
+        assertThat(BluetoothUtils.getStringMetaData(mBluetoothDevice,
+                BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).isEqualTo(STRING_METADATA);
+    }
+
+    @Test
+    public void getIntMetaData_hasMetaData_getCorrectMetaData() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+                INT_METADATA.getBytes());
+
+        assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
+                BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY))
+                .isEqualTo(Integer.parseInt(INT_METADATA));
+    }
+
+    @Test
+    public void getIntMetaData_invalidMetaData_getErrorCode() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(null);
+
+        assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
+                BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON))
+                .isEqualTo(BluetoothUtils.META_INT_ERROR);
+    }
+
+    @Test
+    public void getBooleanMetaData_hasMetaData_getCorrectMetaData() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+                BOOL_METADATA.getBytes());
+
+        assertThat(BluetoothUtils.getBooleanMetaData(mBluetoothDevice,
+                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).isEqualTo(true);
+    }
+
+    @Test
+    public void getUriMetaData_hasMetaData_getCorrectMetaData() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(
+                STRING_METADATA.getBytes());
+
+        assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
+                BluetoothDevice.METADATA_MAIN_ICON)).isEqualTo(Uri.parse(STRING_METADATA));
+    }
+
+    @Test
+    public void getUriMetaData_nullMetaData_getNullUri() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(null);
+
+        assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
+                BluetoothDevice.METADATA_MAIN_ICON)).isNull();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 79b84b9..c0a1f11 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -455,12 +455,12 @@
         updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
-        when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn(
-                "true");
-        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
-                TWS_BATTERY_LEFT);
-        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn(
-                TWS_BATTERY_RIGHT);
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+                "true".getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+                TWS_BATTERY_LEFT.getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+                TWS_BATTERY_RIGHT.getBytes());
 
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "Active, L: 15% battery, R: 25% battery");
@@ -472,12 +472,12 @@
         updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn(
-                "true");
-        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
-                TWS_BATTERY_LEFT);
-        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn(
-                TWS_BATTERY_RIGHT);
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+                "true".getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+                TWS_BATTERY_LEFT.getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+                TWS_BATTERY_RIGHT.getBytes());
 
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "L: 15% battery, R: 25% battery");
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java
index cec97ab..79c691c 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java
@@ -87,13 +87,13 @@
 import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
 import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.utilities.Utilities;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.utilities.Utilities;
 import com.android.systemui.recents.views.RecentsView;
 import com.android.systemui.recents.views.SystemBarScrimViews;
+import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 import java.io.FileDescriptor;
@@ -370,8 +370,7 @@
         MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
 
         // Getting system scrim colors ignoring wallpaper visibility since it should never be grey.
-        ColorExtractor.GradientColors systemColors = mColorExtractor.getColors(
-                ColorExtractor.TYPE_DARK, WallpaperManager.FLAG_SYSTEM, true);
+        ColorExtractor.GradientColors systemColors = mColorExtractor.getNeutralColors();
         // We don't want to interpolate colors because we're defining the initial state.
         // Gradient should be set/ready when you open "Recents".
         mRecentsView.setScrimColors(systemColors, false);
@@ -397,9 +396,7 @@
         if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
             // Recents doesn't care about the wallpaper being visible or not, it always
             // wants to scrim with wallpaper colors
-            ColorExtractor.GradientColors colors = mColorExtractor.getColors(
-                    WallpaperManager.FLAG_SYSTEM,
-                    ColorExtractor.TYPE_DARK, true /* ignoreVis */);
+            ColorExtractor.GradientColors colors = mColorExtractor.getNeutralColors();
             boolean darkText = colors.supportsDarkText();
             if (darkText != mUsingDarkText) {
                 mUsingDarkText = darkText;
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
index 8723fb9..e60ffba 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,7 +34,6 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.os.IRemoteCallback;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -50,7 +49,7 @@
 import android.widget.TextView;
 
 import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.colorextraction.drawable.ScrimDrawable;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
@@ -87,9 +86,9 @@
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
 import com.android.systemui.shared.recents.view.RecentsTransition;
@@ -134,7 +133,7 @@
     private int mDividerSize;
 
     private float mBusynessFactor;
-    private GradientDrawable mBackgroundScrim;
+    private ScrimDrawable mBackgroundScrim;
     private ColorDrawable mMultiWindowBackgroundScrim;
     private ValueAnimator mBackgroundScrimAnimator;
     private Point mTmpDisplaySize = new Point();
@@ -172,7 +171,7 @@
         mDividerSize = ssp.getDockedDividerSize(context);
         mTouchHandler = new RecentsViewTouchHandler(this);
         mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
-        mBackgroundScrim = new GradientDrawable(context);
+        mBackgroundScrim = new ScrimDrawable();
         mMultiWindowBackgroundScrim = new ColorDrawable();
 
         LayoutInflater inflater = LayoutInflater.from(context);
@@ -395,7 +394,7 @@
      * @param animated Interpolate colors if true.
      */
     public void setScrimColors(ColorExtractor.GradientColors scrimColors, boolean animated) {
-        mBackgroundScrim.setColors(scrimColors, animated);
+        mBackgroundScrim.setColor(scrimColors.getMainColor(), animated);
         int alpha = mMultiWindowBackgroundScrim.getAlpha();
         mMultiWindowBackgroundScrim.setColor(scrimColors.getMainColor());
         mMultiWindowBackgroundScrim.setAlpha(alpha);
@@ -467,7 +466,6 @@
         // Needs to know the screen size since the gradient never scales up or down
         // even when bounds change.
         mContext.getDisplay().getRealSize(mTmpDisplaySize);
-        mBackgroundScrim.setScreenSize(mTmpDisplaySize.x, mTmpDisplaySize.y);
         mBackgroundScrim.setBounds(left, top, right, bottom);
         mMultiWindowBackgroundScrim.setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index fbd863d..bc6547f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -59,15 +59,17 @@
         public static final int TYPE_WAKE_DISPLAY = 2;
         public static final int TYPE_SWIPE = 3;
 
-        int mType;
-
-        public int getType() {
-            return mType;
-        }
+        private int mType;
 
         public Sensor(int type) {
             mType = type;
         }
+        public int getType() {
+            return mType;
+        }
+        public String toString() {
+            return "{PluginSensor type=\"" + mType + "\"}";
+        }
     }
 
     /**
diff --git a/packages/SystemUI/res-keyguard/layout/type_aod_clock.xml b/packages/SystemUI/res-keyguard/layout/type_aod_clock.xml
deleted file mode 100644
index 28ff5a2..0000000
--- a/packages/SystemUI/res-keyguard/layout/type_aod_clock.xml
+++ /dev/null
@@ -1,23 +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.
-  -->
-<com.android.keyguard.clock.ClockLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-  >
-  <include layout="@layout/typographic_clock" />
-</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/typographic_clock.xml b/packages/SystemUI/res-keyguard/layout/typographic_clock.xml
deleted file mode 100644
index 73bb4b9..0000000
--- a/packages/SystemUI/res-keyguard/layout/typographic_clock.xml
+++ /dev/null
@@ -1,26 +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.
-  -->
-<com.android.keyguard.clock.TypographicClock
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/type_clock"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingStart="50dp"
-    android:textAlignment="viewStart"
-    style="@style/widget_big"
-    android:textSize="40dp"
-    />
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 2f2f84a..4738887 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -405,106 +405,6 @@
 number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
     </plurals>
 
-    <!-- Time displayed on typographic clock face, which displays the time in words.
-             Example:
-
-                 It's
-                 Four
-                 Twenty
-                 Nine
-
-         This string requires two arguments: the first in the hours of the time and
-         the second is the minutes of the time. The hours string is obtained from
-         string-array type_clock_hours below and the minutes string is obtained
-         from string-array type_clock_minutes below.
-
-    [CHAR LIMIT=8] -->
-    <plurals name="type_clock_header">
-        <item quantity="one"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item>
-        <item quantity="few"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item>
-        <item quantity="other"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item>
-    </plurals>
-
-    <!-- Hour displayed in words on the typographic clock face. [CHAR LIMIT=12] -->
-    <string-array name="type_clock_hours">
-        <item>Twelve</item>
-        <item>One</item>
-        <item>Two</item>
-        <item>Three</item>
-        <item>Four</item>
-        <item>Five</item>
-        <item>Six</item>
-        <item>Seven</item>
-        <item>Eight</item>
-        <item>Nine</item>
-        <item>Ten</item>
-        <item>Eleven</item>
-    </string-array>
-
-    <!-- Minutes displayed in words on the typographic clock face. [CHAR LIMIT=20] -->
-    <string-array name="type_clock_minutes">
-        <item>O\u2019Clock</item>
-        <item>Oh One</item>
-        <item>Oh Two</item>
-        <item>Oh Three</item>
-        <item>Oh Four</item>
-        <item>Oh Five</item>
-        <item>Oh Six</item>
-        <item>Oh Seven</item>
-        <item>Oh Eight</item>
-        <item>Oh Nine</item>
-        <item>Ten</item>
-        <item>Eleven</item>
-        <item>Twelve</item>
-        <item>Thirteen</item>
-        <item>Fourteen</item>
-        <item>Fifteen</item>
-        <item>Sixteen</item>
-        <item>Seventeen</item>
-        <item>Eighteen</item>
-        <item>Nineteen</item>
-        <item>Twenty</item>
-        <item>Twenty\nOne</item>
-        <item>Twenty\nTwo</item>
-        <item>Twenty\nThree</item>
-        <item>Twenty\nFour</item>
-        <item>Twenty\nFive</item>
-        <item>Twenty\nSix</item>
-        <item>Twenty\nSeven</item>
-        <item>Twenty\nEight</item>
-        <item>Twenty\nNine</item>
-        <item>Thirty</item>
-        <item>Thirty\nOne</item>
-        <item>Thirty\nTwo</item>
-        <item>Thirty\nThree</item>
-        <item>Thirty\nFour</item>
-        <item>Thirty\nFive</item>
-        <item>Thirty\nSix</item>
-        <item>Thirty\nSeven</item>
-        <item>Thirty\nEight</item>
-        <item>Thirty\nNine</item>
-        <item>Forty</item>
-        <item>Forty\nOne</item>
-        <item>Forty\nTwo</item>
-        <item>Forty\nThree</item>
-        <item>Forty\nFour</item>
-        <item>Forty\nFive</item>
-        <item>Forty\nSix</item>
-        <item>Forty\nSeven</item>
-        <item>Forty\nEight</item>
-        <item>Forty\nNine</item>
-        <item>Fifty</item>
-        <item>Fifty\nOne</item>
-        <item>Fifty\nTwo</item>
-        <item>Fifty\nThree</item>
-        <item>Fifty\nFour</item>
-        <item>Fifty\nFive</item>
-        <item>Fifty\nSix</item>
-        <item>Fifty\nSeven</item>
-        <item>Fifty\nEight</item>
-        <item>Fifty\nNine</item>
-    </string-array>
-
     <!-- Title for default clock face that will appear in the picker app next to a preview image of
          the clock face. [CHAR LIMIT=8] -->
     <string name="clock_title_default" translatable="false">Default</string>
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
index 41e0786..ced26c9 100644
--- a/packages/SystemUI/res/values/attrs_car.xml
+++ b/packages/SystemUI/res/values/attrs_car.xml
@@ -15,16 +15,23 @@
 -->
 
 <resources>
+    <attr name="icon" format="reference"/>
+    <attr name="selectedIcon" format="reference"/>
+    <attr name="intent" format="string"/>
+    <attr name="longIntent" format="string"/>
+    <attr name="selectedAlpha" format="float" />
+    <attr name="unselectedAlpha" format="float" />
+
     <!-- Allow for custom attribs to be added to a facet button -->
     <declare-styleable name="CarFacetButton">
         <!-- icon to be rendered (drawable) -->
-        <attr name="icon" format="reference"/>
+        <attr name="icon"/>
         <!-- icon to be rendered when in selected state -->
-        <attr name="selectedIcon" format="reference"/>
+        <attr name="selectedIcon"/>
         <!-- intent to start when button is click -->
-        <attr name="intent" format="string"/>
+        <attr name="intent"/>
         <!-- intent to start when a long press has happened -->
-        <attr name="longIntent" format="string"/>
+        <attr name="longIntent"/>
         <!-- categories that will be added as extras to the fired intents -->
         <attr name="categories" format="string"/>
         <!-- package names that will be added as extras to the fired intents -->
@@ -32,9 +39,9 @@
         <!-- componentName names that will be used for detecting selected state -->
         <attr name="componentNames" format="string" />
         <!-- Alpha value to used when in selected state.  Defaults 1f  -->
-        <attr name="selectedAlpha" format="float" />
+        <attr name="selectedAlpha" />
         <!-- Alpha value to used when in un-selected state.  Defaults 0.7f  -->
-        <attr name="unselectedAlpha" format="float" />
+        <attr name="unselectedAlpha" />
         <!-- Render a "more" icon. Defaults true  -->
         <attr name="useMoreIcon" format="boolean" />
 
@@ -44,17 +51,17 @@
     <!-- Allow for custom attribs to be added to a nav button -->
     <declare-styleable name="CarNavigationButton">
         <!-- intent to start when button is click -->
-        <attr name="intent" format="string"/>
+        <attr name="intent" />
         <!-- intent to start when a long press has happened -->
-        <attr name="longIntent" format="string"/>
+        <attr name="longIntent" />
         <!-- start the intent as a broad cast instead of an activity if true-->
         <attr name="broadcast" format="boolean"/>
         <!-- Alpha value to used when in selected state.  Defaults 1f  -->
-        <attr name="selectedAlpha" format="float" />
+        <attr name="selectedAlpha" />
         <!-- Alpha value to used when in un-selected state.  Defaults 0.7f  -->
-        <attr name="unselectedAlpha" format="float" />
+        <attr name="unselectedAlpha" />
         <!-- icon to be rendered when in selected state -->
-        <attr name="selectedIcon" format="reference"/>
+        <attr name="selectedIcon" />
     </declare-styleable>
 
     <!-- Custom attributes to configure hvac values -->
@@ -89,6 +96,6 @@
         </attr>
 
         <!-- Icon resource ids to render on UI -->
-        <attr name="icon" format="reference"/>
+        <attr name="icon" />
     </declare-styleable>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
index 59ee267..7ffee5d 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
@@ -37,7 +37,6 @@
      */
     private View mDigitalClock;
     private View mAnalogClock;
-    private View mTypeClock;
 
     /**
      * Pixel shifting amplitidues used to prevent screen burn-in.
@@ -62,7 +61,6 @@
         super.onFinishInflate();
         mDigitalClock = findViewById(R.id.digital_clock);
         mAnalogClock = findViewById(R.id.analog_clock);
-        mTypeClock = findViewById(R.id.type_clock);
 
         // Get pixel shifting X, Y amplitudes from resources.
         Resources resources = getResources();
@@ -95,11 +93,5 @@
             mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight()))
                     + offsetY);
         }
-
-        // Put the typographic clock part way down the screen.
-        if (mTypeClock != null) {
-            mTypeClock.setX(offsetX);
-            mTypeClock.setY(0.2f * getHeight() + offsetY);
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index bc00b5c..e373ca1 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -144,7 +144,6 @@
         addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor));
         addBuiltinClock(() -> new StretchAnalogClockController(res, layoutInflater,
                 colorExtractor));
-        addBuiltinClock(() -> new TypeClockController(res, layoutInflater, colorExtractor));
 
         // Store the size of the display for generation of clock preview.
         DisplayMetrics dm = res.getDisplayMetrics();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
deleted file mode 100644
index 1c6b38b..0000000
--- a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * 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.
- */
-package com.android.keyguard.clock;
-
-import android.app.WallpaperManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.graphics.Paint.Style;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.keyguard.R;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.plugins.ClockPlugin;
-
-import java.util.TimeZone;
-
-/**
- * Plugin for a custom Typographic clock face that displays the time in words.
- */
-public class TypeClockController implements ClockPlugin {
-
-    /**
-     * Resources used to get title and thumbnail.
-     */
-    private final Resources mResources;
-
-    /**
-     * LayoutInflater used to inflate custom clock views.
-     */
-    private final LayoutInflater mLayoutInflater;
-
-    /**
-     * Extracts accent color from wallpaper.
-     */
-    private final SysuiColorExtractor mColorExtractor;
-
-    /**
-     * Renders preview from clock view.
-     */
-    private final ViewPreviewer mRenderer = new ViewPreviewer();
-
-    /**
-     * Custom clock shown on AOD screen and behind stack scroller on lock.
-     */
-    private View mView;
-    private TypographicClock mTypeClock;
-
-    /**
-     * Small clock shown on lock screen above stack scroller.
-     */
-    private TypographicClock mLockClock;
-
-    /**
-     * Controller for transition into dark state.
-     */
-    private CrossFadeDarkController mDarkController;
-
-    /**
-     * Create a TypeClockController instance.
-     *
-     * @param res Resources contains title and thumbnail.
-     * @param inflater Inflater used to inflate custom clock views.
-     * @param colorExtractor Extracts accent color from wallpaper.
-     */
-    TypeClockController(Resources res, LayoutInflater inflater,
-            SysuiColorExtractor colorExtractor) {
-        mResources = res;
-        mLayoutInflater = inflater;
-        mColorExtractor = colorExtractor;
-    }
-
-    private void createViews() {
-        mView = mLayoutInflater.inflate(R.layout.type_aod_clock, null);
-        mTypeClock = mView.findViewById(R.id.type_clock);
-
-        // For now, this view is used to hide the default digital clock.
-        // Need better transition to lock screen.
-        mLockClock = (TypographicClock) mLayoutInflater.inflate(R.layout.typographic_clock, null);
-        mLockClock.setVisibility(View.GONE);
-
-        mDarkController = new CrossFadeDarkController(mView, mLockClock);
-    }
-
-    @Override
-    public void onDestroyView() {
-        mView = null;
-        mTypeClock = null;
-        mLockClock = null;
-        mDarkController = null;
-    }
-
-    @Override
-    public String getName() {
-        return "type";
-    }
-
-    @Override
-    public String getTitle() {
-        return mResources.getString(R.string.clock_title_type);
-    }
-
-    @Override
-    public Bitmap getThumbnail() {
-        return BitmapFactory.decodeResource(mResources, R.drawable.type_thumbnail);
-    }
-
-    @Override
-    public Bitmap getPreview(int width, int height) {
-
-        // Use the big clock view for the preview
-        View view = getBigClockView();
-
-        // Initialize state of plugin before generating preview.
-        setDarkAmount(1f);
-        setTextColor(Color.WHITE);
-        ColorExtractor.GradientColors colors = mColorExtractor.getColors(
-                WallpaperManager.FLAG_LOCK, true);
-        setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
-        onTimeTick();
-
-        return mRenderer.createPreview(view, width, height);
-    }
-
-    @Override
-    public View getView() {
-        if (mLockClock == null) {
-            createViews();
-        }
-        return mLockClock;
-    }
-
-    @Override
-    public View getBigClockView() {
-        if (mView == null) {
-            createViews();
-        }
-        return mView;
-    }
-
-    @Override
-    public void setStyle(Style style) {}
-
-    @Override
-    public void setTextColor(int color) {
-        mTypeClock.setTextColor(color);
-        mLockClock.setTextColor(color);
-    }
-
-    @Override
-    public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {
-        if (colorPalette == null || colorPalette.length == 0) {
-            return;
-        }
-        final int color = colorPalette[Math.max(0, colorPalette.length - 5)];
-        mTypeClock.setClockColor(color);
-        mLockClock.setClockColor(color);
-    }
-
-    @Override
-    public void onTimeTick() {
-        mTypeClock.onTimeChanged();
-        mLockClock.onTimeChanged();
-    }
-
-    @Override
-    public void setDarkAmount(float darkAmount) {
-        if (mDarkController != null) {
-            mDarkController.setDarkAmount(darkAmount);
-        }
-    }
-
-    @Override
-    public void onTimeZoneChanged(TimeZone timeZone) {
-        mTypeClock.onTimeZoneChanged(timeZone);
-        mLockClock.onTimeZoneChanged(timeZone);
-    }
-
-    @Override
-    public boolean shouldShowStatusArea() {
-        return false;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
deleted file mode 100644
index 572ab30..0000000
--- a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * 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.
- */
-package com.android.keyguard.clock;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.Annotation;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.SpannedString;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.text.style.ForegroundColorSpan;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import com.android.keyguard.R;
-
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.TimeZone;
-
-/**
- * Clock that presents the time in words.
- */
-public class TypographicClock extends TextView {
-
-    private static final String ANNOTATION_COLOR = "color";
-
-    private final Resources mResources;
-    private final String[] mHours;
-    private final String[] mMinutes;
-    private int mAccentColor;
-    private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault());
-    private String mDescFormat;
-    private TimeZone mTimeZone;
-
-    public TypographicClock(Context context) {
-        this(context, null);
-    }
-
-    public TypographicClock(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TypographicClock(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
-        mResources = context.getResources();
-        mHours = mResources.getStringArray(R.array.type_clock_hours);
-        mMinutes = mResources.getStringArray(R.array.type_clock_minutes);
-        mAccentColor = mResources.getColor(R.color.typeClockAccentColor, null);
-    }
-
-    /**
-     * Call when the time changes to update the text of the time.
-     */
-    public void onTimeChanged() {
-        mTime.setTimeInMillis(System.currentTimeMillis());
-        setContentDescription(DateFormat.format(mDescFormat, mTime));
-        final int hour = mTime.get(Calendar.HOUR) % 12;
-        final int minute = mTime.get(Calendar.MINUTE) % 60;
-
-        // Get the quantity based on the hour for languages like Portuguese and Czech.
-        SpannedString typeTemplate = (SpannedString) mResources.getQuantityText(
-                R.plurals.type_clock_header, hour);
-
-        // Find the "color" annotation and set the foreground color to the accent color.
-        Annotation[] annotations = typeTemplate.getSpans(0, typeTemplate.length(),
-                Annotation.class);
-        SpannableString spanType = new SpannableString(typeTemplate);
-        for (int i = 0; i < annotations.length; i++) {
-            Annotation annotation = annotations[i];
-            String key = annotation.getValue();
-            if (ANNOTATION_COLOR.equals(key)) {
-                spanType.setSpan(new ForegroundColorSpan(mAccentColor),
-                        spanType.getSpanStart(annotation), spanType.getSpanEnd(annotation),
-                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-        }
-
-        setText(TextUtils.expandTemplate(spanType, mHours[hour], mMinutes[minute]));
-    }
-
-    /**
-     * Call when the time zone has changed to update clock time.
-     *
-     * @param timeZone The updated time zone that will be used.
-     */
-    public void onTimeZoneChanged(TimeZone timeZone) {
-        mTimeZone = timeZone;
-        mTime.setTimeZone(timeZone);
-    }
-
-    /**
-     * Sets the accent color used on the clock face.
-     */
-    public void setClockColor(int color) {
-        mAccentColor = color;
-        onTimeChanged();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
-        onTimeChanged();
-    }
-
-    /**
-     * Overriding hasOverlappingRendering as false to improve performance of crossfading.
-     */
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 6b21526..285d4aab 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -229,6 +229,9 @@
                 true /* singleTaskInstance */);
         addView(mActivityView);
 
+        // Make sure pointer is below activity view
+        bringChildToFront(mPointerView);
+
         setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
             // Keep track of IME displaying because we should not make any adjustments that might
             // cause a config change while the IME is displayed otherwise it'll loose focus.
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index a74c328..05665b5 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -34,6 +34,7 @@
 import com.android.internal.colorextraction.types.Tonal;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -46,7 +47,8 @@
  * ColorExtractor aware of wallpaper visibility
  */
 @Singleton
-public class SysuiColorExtractor extends ColorExtractor implements Dumpable {
+public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
+        ConfigurationController.ConfigurationListener {
     private static final String TAG = "SysuiColorExtractor";
     private final Tonal mTonal;
     private boolean mWallpaperVisible;
@@ -55,15 +57,17 @@
     private final GradientColors mWpHiddenColors;
 
     @Inject
-    public SysuiColorExtractor(Context context) {
-        this(context, new Tonal(context), true);
+    public SysuiColorExtractor(Context context, ConfigurationController configurationController) {
+        this(context, new Tonal(context), configurationController, true);
     }
 
     @VisibleForTesting
-    public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) {
+    public SysuiColorExtractor(Context context, ExtractionType type,
+            ConfigurationController configurationController, boolean registerVisibility) {
         super(context, type, false /* immediately */);
         mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
         mWpHiddenColors = new GradientColors();
+        configurationController.addCallback(this);
 
         WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
         updateDefaultGradients(systemColors);
@@ -113,8 +117,21 @@
         }
     }
 
-    @VisibleForTesting
-    GradientColors getFallbackColors() {
+    @Override
+    public void onUiModeChanged() {
+        WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+        updateDefaultGradients(systemColors);
+    }
+
+    /**
+     * Colors the should be using for scrims.
+     *
+     * They will be:
+     * - A light gray if the wallpaper is light
+     * - A dark gray if the wallpaper is very dark or we're in night mode.
+     * - Black otherwise
+     */
+    public GradientColors getNeutralColors() {
         return mWpHiddenColors;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 77180f8..831d074 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -228,9 +228,9 @@
     /** Dump current state */
     public void dump(PrintWriter pw) {
         for (TriggerSensor s : mSensors) {
-            pw.print("Sensor: "); pw.println(s.toString());
+            pw.print("  Sensor: "); pw.println(s.toString());
         }
-        pw.print("ProxSensor: "); pw.println(mProxSensor.toString());
+        pw.print("  ProxSensor: "); pw.println(mProxSensor.toString());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 7a3f3be..411536c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -35,7 +35,6 @@
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
-import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
@@ -73,7 +72,7 @@
 import com.android.internal.R;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.colorextraction.drawable.ScrimDrawable;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.TelephonyIntents;
@@ -1503,7 +1502,7 @@
         private final MyAdapter mAdapter;
         private MultiListLayout mGlobalActionsLayout;
         private Drawable mBackgroundDrawable;
-        private final ColorExtractor mColorExtractor;
+        private final SysuiColorExtractor mColorExtractor;
         private final GlobalActionsPanelPlugin.PanelViewController mPanelController;
         private boolean mKeyguardShowing;
         private boolean mShowing;
@@ -1582,7 +1581,7 @@
 
             if (!shouldUsePanel()) {
                 if (mBackgroundDrawable == null) {
-                    mBackgroundDrawable = new GradientDrawable(mContext);
+                    mBackgroundDrawable = new ScrimDrawable();
                 }
                 mScrimAlpha = ScrimController.GRADIENT_SCRIM_ALPHA;
             } else {
@@ -1610,16 +1609,9 @@
             super.onStart();
             mGlobalActionsLayout.updateList();
 
-            if (mBackgroundDrawable instanceof GradientDrawable) {
-                Point displaySize = new Point();
-                mContext.getDisplay().getRealSize(displaySize);
+            if (mBackgroundDrawable instanceof ScrimDrawable) {
                 mColorExtractor.addOnColorsChangedListener(this);
-                ((GradientDrawable) mBackgroundDrawable)
-                        .setScreenSize(displaySize.x, displaySize.y);
-                GradientColors colors = mColorExtractor.getColors(
-                        mKeyguardShowing
-                                ? WallpaperManager.FLAG_LOCK
-                                : WallpaperManager.FLAG_SYSTEM);
+                GradientColors colors = mColorExtractor.getNeutralColors();
                 updateColors(colors, false /* animate */);
             }
         }
@@ -1630,10 +1622,10 @@
          * @param animate Interpolates gradient if true, just sets otherwise.
          */
         private void updateColors(GradientColors colors, boolean animate) {
-            if (!(mBackgroundDrawable instanceof GradientDrawable)) {
+            if (!(mBackgroundDrawable instanceof ScrimDrawable)) {
                 return;
             }
-            ((GradientDrawable) mBackgroundDrawable).setColors(colors, animate);
+            ((ScrimDrawable) mBackgroundDrawable).setColor(colors.getMainColor(), animate);
             View decorView = getWindow().getDecorView();
             if (colors.supportsDarkText()) {
                 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 4cf58b7..4065d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -19,9 +19,7 @@
 
 import android.app.Dialog;
 import android.app.KeyguardManager;
-import android.app.WallpaperManager;
 import android.content.Context;
-import android.graphics.Point;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -31,7 +29,7 @@
 
 import com.android.internal.R;
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.colorextraction.drawable.ScrimDrawable;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.SysUiServiceProvider;
@@ -87,7 +85,7 @@
 
     @Override
     public void showShutdownUi(boolean isReboot, String reason) {
-        GradientDrawable background = new GradientDrawable(mContext);
+        ScrimDrawable background = new ScrimDrawable();
         background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
 
         Dialog d = new Dialog(mContext,
@@ -129,12 +127,8 @@
         message.setTextColor(color);
         if (isReboot) message.setText(R.string.reboot_to_reset_message);
 
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        GradientColors colors = Dependency.get(SysuiColorExtractor.class).getColors(
-                onKeyguard ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
-        background.setColors(colors, false);
-        background.setScreenSize(displaySize.x, displaySize.y);
+        GradientColors colors = Dependency.get(SysuiColorExtractor.class).getNeutralColors();
+        background.setColor(colors.getMainColor(), false);
 
         d.show();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 10f727b..e92aa51 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -22,15 +22,19 @@
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioAttributes;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.text.Annotation;
 import android.text.Layout;
@@ -547,9 +551,15 @@
         updateNotification();
     }
 
-    private void showStartSaverConfirmation(boolean confirmOnly) {
+    private void showStartSaverConfirmation(Bundle extras) {
         if (mSaverConfirmation != null) return;
         final SystemUIDialog d = new SystemUIDialog(mContext);
+        final boolean confirmOnly = extras.getBoolean(BatterySaverUtils.EXTRA_CONFIRM_TEXT_ONLY);
+        final int batterySaverTriggerMode =
+                extras.getInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER,
+                        PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
+        final int batterySaverTriggerLevel =
+                extras.getInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL, 0);
         d.setMessage(getBatterySaverDescription());
 
         // Sad hack for http://b/78261259 and http://b/78298335. Otherwise "Battery" may be split
@@ -563,14 +573,25 @@
         if (confirmOnly) {
             d.setTitle(R.string.battery_saver_confirmation_title_generic);
             d.setPositiveButton(com.android.internal.R.string.confirm_battery_saver,
-                    (dialog, which) -> Secure.putInt(
-                            mContext.getContentResolver(),
-                            Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
-                            1));
+                    (dialog, which) -> {
+                        final ContentResolver resolver = mContext.getContentResolver();
+                        Secure.putInt(
+                                resolver,
+                                Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
+                                1);
+                        Settings.Global.putInt(
+                                resolver,
+                                Global.AUTOMATIC_POWER_SAVE_MODE,
+                                batterySaverTriggerMode);
+                        Settings.Global.putInt(
+                                resolver,
+                                Global.LOW_POWER_MODE_TRIGGER_LEVEL,
+                                batterySaverTriggerLevel);
+                    });
         } else {
             d.setTitle(R.string.battery_saver_confirmation_title);
             d.setPositiveButton(R.string.battery_saver_confirmation_ok,
-                (dialog, which) -> setSaverMode(true, false));
+                    (dialog, which) -> setSaverMode(true, false));
             d.setNegativeButton(android.R.string.cancel, null);
         }
         d.setShowForAllUsers(true);
@@ -731,7 +752,7 @@
                 dismissLowBatteryNotification();
             } else if (action.equals(ACTION_SHOW_START_SAVER_CONFIRMATION)) {
                 dismissLowBatteryNotification();
-                showStartSaverConfirmation(intent.getBooleanExtra(EXTRA_CONFIRM_ONLY, false));
+                showStartSaverConfirmation(intent.getExtras());
             } else if (action.equals(ACTION_DISMISSED_WARNING)) {
                 dismissLowBatteryWarning();
             } else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index cf6e64c..04f1c32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -16,61 +16,33 @@
 
 package com.android.systemui.statusbar;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Display;
 import android.view.View;
-import android.view.WindowManager;
 
 import androidx.core.graphics.ColorUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.drawable.GradientDrawable;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.internal.colorextraction.drawable.ScrimDrawable;
 
 /**
  * A view which can draw a scrim
  */
-public class ScrimView extends View implements ConfigurationController.ConfigurationListener {
-    private static final String TAG = "ScrimView";
+public class ScrimView extends View {
     private final ColorExtractor.GradientColors mColors;
-    private int mDensity;
     private float mViewAlpha = 1.0f;
-    private ValueAnimator mAlphaAnimator;
     private Drawable mDrawable;
     private PorterDuffColorFilter mColorFilter;
     private int mTintColor;
-    private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = animation -> {
-        if (mDrawable == null) {
-            Log.w(TAG, "Trying to animate null drawable");
-            return;
-        }
-        mDrawable.setAlpha((int) (255 * (float) animation.getAnimatedValue()));
-    };
-    private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mAlphaAnimator = null;
-        }
-    };
     private Runnable mChangeRunnable;
-    private int mCornerRadius;
 
     public ScrimView(Context context) {
         this(context, null);
@@ -87,47 +59,10 @@
     public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        mDrawable = new GradientDrawable(context);
+        mDrawable = new ScrimDrawable();
         mDrawable.setCallback(this);
         mColors = new ColorExtractor.GradientColors();
-        updateScreenSize();
         updateColorWithTint(false);
-        initView();
-        final Configuration currentConfig = mContext.getResources().getConfiguration();
-        mDensity = currentConfig.densityDpi;
-    }
-
-    private void initView() {
-        mCornerRadius = getResources().getDimensionPixelSize(
-                Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        int densityDpi = newConfig.densityDpi;
-        if (mDensity != densityDpi) {
-            mDensity = densityDpi;
-            initView();
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        // We need to know about configuration changes to update the gradient size
-        // since it's independent from view bounds.
-        ConfigurationController config = Dependency.get(ConfigurationController.class);
-        config.addCallback(this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        ConfigurationController config = Dependency.get(ConfigurationController.class);
-        config.removeCallback(this);
     }
 
     @Override
@@ -142,7 +77,6 @@
         mDrawable.setCallback(this);
         mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
         mDrawable.setAlpha((int) (255 * mViewAlpha));
-        updateScreenSize();
         invalidate();
     }
 
@@ -200,15 +134,13 @@
     }
 
     private void updateColorWithTint(boolean animated) {
-        if (mDrawable instanceof GradientDrawable) {
+        if (mDrawable instanceof ScrimDrawable) {
             // Optimization to blend colors and avoid a color filter
-            GradientDrawable drawable = (GradientDrawable) mDrawable;
+            ScrimDrawable drawable = (ScrimDrawable) mDrawable;
             float tintAmount = Color.alpha(mTintColor) / 255f;
             int mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor,
                     tintAmount);
-            int secondaryTinted = ColorUtils.blendARGB(mColors.getSecondaryColor(), mTintColor,
-                    tintAmount);
-            drawable.setColors(mainTinted, secondaryTinted, animated);
+            drawable.setColor(mainTinted, animated);
         } else {
             boolean hasAlpha = Color.alpha(mTintColor) != 0;
             if (hasAlpha) {
@@ -250,10 +182,6 @@
         if (alpha != mViewAlpha) {
             mViewAlpha = alpha;
 
-            if (mAlphaAnimator != null) {
-                mAlphaAnimator.cancel();
-            }
-
             mDrawable.setAlpha((int) (255 * alpha));
             if (mChangeRunnable != null) {
                 mChangeRunnable.run();
@@ -270,27 +198,6 @@
     }
 
     @Override
-    public void onConfigChanged(Configuration newConfig) {
-        updateScreenSize();
-    }
-
-    private void updateScreenSize() {
-        if (mDrawable instanceof GradientDrawable) {
-            WindowManager wm = mContext.getSystemService(WindowManager.class);
-            if (wm == null) {
-                Log.w(TAG, "Can't resize gradient drawable to fit the screen");
-                return;
-            }
-            Display display = wm.getDefaultDisplay();
-            if (display != null) {
-                Point size = new Point();
-                display.getRealSize(size);
-                ((GradientDrawable) mDrawable).setScreenSize(size.x, size.y);
-            }
-        }
-    }
-
-    @Override
     protected boolean canReceivePointerEvents() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 2e85fea..ce8463e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -367,16 +367,11 @@
     @Override
     public void onFinishedGoingToSleep(int why) {
         Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
-        if (mPendingAuthenticatedUserId != -1) {
-
+        BiometricSourceType pendingType = mPendingAuthenticatedBioSourceType;
+        int pendingUserId = mPendingAuthenticatedUserId;
+        if (pendingUserId != -1 && pendingType != null) {
             // Post this to make sure it's executed after the device is fully locked.
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onBiometricAuthenticated(mPendingAuthenticatedUserId,
-                            mPendingAuthenticatedBioSourceType);
-                }
-            });
+            mHandler.post(() -> onBiometricAuthenticated(pendingUserId, pendingType));
         }
         mPendingAuthenticatedUserId = -1;
         mPendingAuthenticatedBioSourceType = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0d2fe13..ed79476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -20,7 +20,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.app.AlarmManager;
-import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
@@ -121,8 +120,7 @@
     private final Handler mHandler;
 
     private final SysuiColorExtractor mColorExtractor;
-    private GradientColors mLockColors;
-    private GradientColors mSystemColors;
+    private GradientColors mColors;
     private boolean mNeedsDrawableColorUpdate;
 
     protected float mScrimBehindAlpha;
@@ -190,10 +188,7 @@
 
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
         mColorExtractor.addOnColorsChangedListener(this);
-        mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
-                ColorExtractor.TYPE_DARK, true /* ignoreVisibility */);
-        mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM,
-                ColorExtractor.TYPE_DARK, true /* ignoreVisibility */);
+        mColors = mColorExtractor.getNeutralColors();
         mNeedsDrawableColorUpdate = true;
 
         final ScrimState[] states = ScrimState.values();
@@ -201,7 +196,6 @@
             states[i].init(mScrimInFront, mScrimBehind, mDozeParameters);
             states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
         }
-        mState = ScrimState.UNINITIALIZED;
 
         mScrimBehind.setDefaultFocusHighlightEnabled(false);
         mScrimInFront.setDefaultFocusHighlightEnabled(false);
@@ -488,17 +482,15 @@
         // Make sure we have the right gradients and their opacities will satisfy GAR.
         if (mNeedsDrawableColorUpdate) {
             mNeedsDrawableColorUpdate = false;
-            boolean isKeyguard = mKeyguardUpdateMonitor.isKeyguardVisible() && !mKeyguardOccluded;
-            GradientColors currentScrimColors = isKeyguard ? mLockColors : mSystemColors;
             // Only animate scrim color if the scrim view is actually visible
             boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
             boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
-            mScrimInFront.setColors(currentScrimColors, animateScrimInFront);
-            mScrimBehind.setColors(currentScrimColors, animateScrimBehind);
+            mScrimInFront.setColors(mColors, animateScrimInFront);
+            mScrimBehind.setColors(mColors, animateScrimBehind);
 
             // Calculate minimum scrim opacity for white or black text.
-            int textColor = currentScrimColors.supportsDarkText() ? Color.BLACK : Color.WHITE;
-            int mainColor = currentScrimColors.getMainColor();
+            int textColor = mColors.supportsDarkText() ? Color.BLACK : Color.WHITE;
+            int mainColor = mColors.getMainColor();
             float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor,
                     4.5f /* minimumContrast */) / 255f;
             mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity);
@@ -815,7 +807,7 @@
     }
 
     public int getBackgroundColor() {
-        int color = mLockColors.getMainColor();
+        int color = mColors.getMainColor();
         return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)),
                 Color.red(color), Color.green(color), Color.blue(color));
     }
@@ -830,18 +822,9 @@
 
     @Override
     public void onColorsChanged(ColorExtractor colorExtractor, int which) {
-        if ((which & WallpaperManager.FLAG_LOCK) != 0) {
-            mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
-                    ColorExtractor.TYPE_DARK, true /* ignoreVisibility */);
-            mNeedsDrawableColorUpdate = true;
-            scheduleUpdate();
-        }
-        if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
-            mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM,
-                    ColorExtractor.TYPE_DARK, mState != ScrimState.UNLOCKED);
-            mNeedsDrawableColorUpdate = true;
-            scheduleUpdate();
-        }
+        mColors = mColorExtractor.getNeutralColors();
+        mNeedsDrawableColorUpdate = true;
+        scheduleUpdate();
     }
 
     @VisibleForTesting
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 b34e24e..aaaf3ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1152,7 +1152,6 @@
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.onDensityOrFontScaleChanged();
         }
-        mStatusBarKeyguardViewManager.onDensityOrFontScaleChanged();
         // TODO: Bring these out of StatusBar.
         ((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
                 .onDensityOrFontScaleChanged();
@@ -3949,6 +3948,7 @@
                 }
 
                 private void setPulsing(boolean pulsing) {
+                    mStatusBarKeyguardViewManager.setPulsing(pulsing);
                     mKeyguardViewMediator.setPulsing(pulsing);
                     mNotificationPanel.setPulsing(pulsing);
                     mVisualStabilityManager.setPulsing(pulsing);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 92cd280..e3cc3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -40,14 +40,18 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.SystemUIFactory;
+import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 
@@ -61,7 +65,7 @@
  * {@link com.android.keyguard.KeyguardViewBase}.
  */
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
-        StatusBarStateController.StateListener {
+        StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener {
 
     // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
     private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -105,6 +109,18 @@
             mNotificationPanelView.updateLockIcon();
         }
     };
+    private final DockManager.DockEventListener mDockEventListener =
+            new DockManager.DockEventListener() {
+                @Override
+                public void onEvent(int event) {
+                    boolean isDocked = mDockManager.isDocked();
+            if (isDocked == mIsDocked) {
+                return;
+            }
+            mIsDocked = isDocked;
+            updateStates();
+        }
+    };
 
     protected LockPatternUtils mLockPatternUtils;
     protected ViewMediatorCallback mViewMediatorCallback;
@@ -119,6 +135,9 @@
     protected boolean mOccluded;
     protected boolean mRemoteInputActive;
     private boolean mDozing;
+    private boolean mPulsing;
+    private boolean mGesturalNav;
+    private boolean mIsDocked;
 
     protected boolean mFirstUpdate = true;
     protected boolean mLastShowing;
@@ -127,6 +146,9 @@
     private boolean mLastBouncerDismissible;
     protected boolean mLastRemoteInputActive;
     private boolean mLastDozing;
+    private boolean mLastGesturalNav;
+    private boolean mLastIsDocked;
+    private boolean mLastPulsing;
     private int mLastBiometricMode;
     private boolean mGoingToSleepVisibleNotOccluded;
 
@@ -139,6 +161,7 @@
             (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
     private final NotificationMediaManager mMediaManager =
             Dependency.get(NotificationMediaManager.class);
+    private final DockManager mDockManager;
 
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -159,8 +182,15 @@
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
         mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
+        mGesturalNav = QuickStepContract.isGesturalMode(context);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
         Dependency.get(StatusBarStateController.class).addCallback(this);
+        Dependency.get(ConfigurationController.class).addCallback(this);
+        mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
+        if (mDockManager != null) {
+            mDockManager.addListener(mDockEventListener);
+            mIsDocked = mDockManager.isDocked();
+        }
     }
 
     public void registerStatusBar(StatusBar statusBar,
@@ -241,6 +271,9 @@
     }
 
     private void hideBouncer(boolean destroyView) {
+        if (mBouncer == null) {
+            return;
+        }
         mBouncer.hide(destroyView);
         cancelPendingWakeupAction();
     }
@@ -354,6 +387,16 @@
         }
     }
 
+    /**
+     * If {@link StatusBar} is pulsing.
+     */
+    public void setPulsing(boolean pulsing) {
+        if (mPulsing != pulsing) {
+            mPulsing = pulsing;
+            updateStates();
+        }
+    }
+
     public void setNeedsInput(boolean needsInput) {
         mStatusBarWindowController.setKeyguardNeedsInput(needsInput);
     }
@@ -492,10 +535,20 @@
             StatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN);
     }
 
+    @Override
     public void onDensityOrFontScaleChanged() {
         hideBouncer(true /* destroyView */);
     }
 
+    @Override
+    public void onOverlayChanged() {
+        boolean gesturalNav = QuickStepContract.isGesturalMode(mContext);
+        if (gesturalNav != mGesturalNav) {
+            mGesturalNav = gesturalNav;
+            updateStates();
+        }
+    }
+
     public void onThemeChanged() {
         hideBouncer(true /* destroyView */);
         mBouncer.prepare();
@@ -643,7 +696,10 @@
         mLastBouncerDismissible = bouncerDismissible;
         mLastRemoteInputActive = remoteInputActive;
         mLastDozing = mDozing;
+        mLastPulsing = mPulsing;
         mLastBiometricMode = mBiometricUnlockController.getMode();
+        mLastGesturalNav = mGesturalNav;
+        mLastIsDocked = mIsDocked;
         mStatusBar.onKeyguardViewManagerStatesUpdated();
     }
 
@@ -671,8 +727,10 @@
         int biometricMode = mBiometricUnlockController.getMode();
         boolean keyguardShowing = mShowing && !mOccluded;
         boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked)
+                && mGesturalNav;
         return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
-                || mRemoteInputActive);
+                || mRemoteInputActive || keyguardWithGestureNav);
     }
 
     /**
@@ -681,8 +739,10 @@
     protected boolean getLastNavBarVisible() {
         boolean keyguardShowing = mLastShowing && !mLastOccluded;
         boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
+                || mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
         return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
-                || mLastRemoteInputActive);
+                || mLastRemoteInputActive || keyguardWithGestureNav);
     }
 
     public boolean shouldDismissOnMenuPressed() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 1649f98..67df60a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -18,6 +18,11 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
 
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
@@ -27,7 +32,9 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.colorextraction.types.Tonal;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -57,7 +64,7 @@
         simulateEvent(extractor);
         extractor.setWallpaperVisible(false);
 
-        ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
+        ColorExtractor.GradientColors fallbackColors = extractor.getNeutralColors();
 
         for (int type : sTypes) {
             assertEquals("Not using fallback!",
@@ -96,7 +103,7 @@
         extractor.setWallpaperVisible(true);
         extractor.setHasBackdrop(true);
 
-        ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
+        ColorExtractor.GradientColors fallbackColors = extractor.getNeutralColors();
 
         for (int type : sTypes) {
             assertEquals("Not using fallback!",
@@ -106,6 +113,19 @@
         }
     }
 
+    @Test
+    public void onUiModeChanged_reloadsColors() {
+        Tonal tonal = mock(Tonal.class);
+        ConfigurationController configurationController = mock(ConfigurationController.class);
+        SysuiColorExtractor sysuiColorExtractor = new SysuiColorExtractor(getContext(),
+                tonal, configurationController, false /* registerVisibility */);
+        verify(configurationController).addCallback(eq(sysuiColorExtractor));
+
+        reset(tonal);
+        sysuiColorExtractor.onUiModeChanged();
+        verify(tonal).applyFallback(any(), any());
+    }
+
     private SysuiColorExtractor getTestableExtractor(ColorExtractor.GradientColors colors) {
         return new SysuiColorExtractor(getContext(),
                 (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
@@ -113,7 +133,7 @@
                     outGradientColorsNormal.set(colors);
                     outGradientColorsDark.set(colors);
                     outGradientColorsExtraDark.set(colors);
-                }, false);
+                }, mock(ConfigurationController.class), false);
     }
 
     private void simulateEvent(SysuiColorExtractor extractor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
index 2020d4b..87a7757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
@@ -30,7 +30,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.colorextraction.drawable.ScrimDrawable;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
@@ -70,12 +70,10 @@
 
     @Test
     public void testCreation_initialColor() {
-        GradientDrawable drawable = (GradientDrawable) mView.getDrawable();
+        ScrimDrawable drawable = (ScrimDrawable) mView.getDrawable();
         ColorExtractor.GradientColors colors = mView.getColors();
         assertEquals("Main color should be set upon creation",
                 drawable.getMainColor(), colors.getMainColor());
-        assertEquals("Secondary color should be set upon creation",
-                drawable.getSecondaryColor(), colors.getSecondaryColor());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 057f752..d2d294b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -71,6 +72,8 @@
     private UnlockMethodCache mUnlockMethodCache;
     @Mock
     private TunerService mTunerService;
+    @Mock
+    private Handler mHandler;
     private BiometricUnlockController mBiometricUnlockController;
 
     @Before
@@ -172,12 +175,24 @@
         verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
     }
 
+    @Test
+    public void onFinishedGoingToSleep_authenticatesWhenPending() {
+        when(mUpdateMonitor.isGoingToSleep()).thenReturn(true);
+        mBiometricUnlockController.onFinishedGoingToSleep(-1);
+        verify(mHandler, never()).post(any());
+
+        mBiometricUnlockController.onBiometricAuthenticated(1 /* userId */,
+                BiometricSourceType.FACE);
+        mBiometricUnlockController.onFinishedGoingToSleep(-1);
+        verify(mHandler).post(any());
+    }
+
     private class TestableBiometricUnlockController extends BiometricUnlockController {
 
         TestableBiometricUnlockController(boolean faceDismissesKeyguard) {
             super(mContext, mDozeScrimController,
                     mKeyguardViewMediator, mScrimController, mStatusBar, mUnlockMethodCache,
-                    new Handler(), mUpdateMonitor, mTunerService, 0 /* wakeUpDelay */,
+                    mHandler, mUpdateMonitor, mTunerService, 0 /* wakeUpDelay */,
                     faceDismissesKeyguard);
             mFaceDismissesKeyguard = faceDismissesKeyguard;
         }
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index c57d4e9..b9b3a61 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -18,6 +18,10 @@
 LOCAL_MODULE := frameworks-base-overlays
 LOCAL_REQUIRED_MODULES := \
 	AccentColorBlackOverlay \
+	AccentColorCinnamonOverlay \
+	AccentColorOceanOverlay \
+	AccentColorOrchidOverlay \
+	AccentColorSpaceOverlay \
 	AccentColorGreenOverlay \
 	AccentColorPurpleOverlay \
 	DisplayCutoutEmulationCornerOverlay \
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
index 704ff2e..86fd47b 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
@@ -33,4 +33,8 @@
     <!-- Controls the size of the back gesture inset. -->
     <dimen name="config_backGestureInset">20dp</dimen>
 
+    <!-- Controls whether the navbar needs a scrim with
+     {@link Window#setEnsureNavigationBarContrastWhenTransparent}. -->
+    <bool name="config_navBarNeedsScrim">false</bool>
+
 </resources>
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index afb8a15..fdc01e0 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1245,18 +1245,55 @@
      * when necessary.
      */
     public void logContextCommitted() {
-        mHandler.sendMessage(obtainMessage(
-                Session::doLogContextCommitted, this));
+        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this));
     }
 
-    private void doLogContextCommitted() {
+    private void handleLogContextCommitted() {
+        final FillResponse lastResponse;
         synchronized (mLock) {
-            logContextCommittedLocked();
+            lastResponse = getLastResponseLocked("logContextCommited()");
+        }
+
+        if (lastResponse == null) {
+            Slog.w(TAG, "handleLogContextCommitted(): last response is null");
+            return;
+        }
+
+        // Merge UserData if necessary.
+        // Fields in packageUserData will override corresponding fields in genericUserData.
+        final UserData genericUserData = mService.getUserData();
+        final UserData packageUserData = lastResponse.getUserData();
+        final FieldClassificationUserData userData;
+        if (packageUserData == null && genericUserData == null) {
+            userData = null;
+        } else if (packageUserData != null && genericUserData != null) {
+            userData = new CompositeUserData(genericUserData, packageUserData);
+        } else if (packageUserData != null) {
+            userData = packageUserData;
+        } else {
+            userData = mService.getUserData();
+        }
+
+        final FieldClassificationStrategy fcStrategy = mService.getFieldClassificationStrategy();
+
+        // Sets field classification scores
+        if (userData != null && fcStrategy != null) {
+            logFieldClassificationScore(fcStrategy, userData);
+        } else {
+            logContextCommitted(null, null);
+        }
+    }
+
+    private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
+            @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
+        synchronized (mLock) {
+            logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications);
         }
     }
 
     @GuardedBy("mLock")
-    private void logContextCommittedLocked() {
+    private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
+            @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
         final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
         if (lastResponse == null) return;
 
@@ -1310,21 +1347,6 @@
             return;
         }
 
-        // Merge UserData if necessary.
-        // Fields in packageUserData will override corresponding fields in genericUserData.
-        final UserData genericUserData = mService.getUserData();
-        final UserData packageUserData = lastResponse.getUserData();
-        final FieldClassificationUserData userData;
-        if (packageUserData == null && genericUserData == null) {
-            userData = null;
-        } else if (packageUserData != null && genericUserData != null) {
-            userData = new CompositeUserData(genericUserData, packageUserData);
-        } else if (packageUserData != null) {
-            userData = packageUserData;
-        } else {
-            userData = mService.getUserData();
-        }
-
         for (int i = 0; i < mViewStates.size(); i++) {
             final ViewState viewState = mViewStates.valueAt(i);
             final int state = viewState.getState();
@@ -1449,33 +1471,18 @@
             }
         }
 
-        // Sets field classification scores
-        final FieldClassificationStrategy fcStrategy = mService.getFieldClassificationStrategy();
-        if (userData != null && fcStrategy != null) {
-            logFieldClassificationScoreLocked(fcStrategy, ignoredDatasets, changedFieldIds,
-                    changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                    userData, mViewStates.values());
-        } else {
-            mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
-                    ignoredDatasets, changedFieldIds, changedDatasetIds,
-                    manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                    mComponentName, mCompatMode);
-        }
+        mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
+                ignoredDatasets, changedFieldIds, changedDatasetIds,
+                manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds,
+                detectedFieldClassifications, mComponentName, mCompatMode);
     }
 
     /**
      * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for
      * {@code fieldId} based on its {@code currentValue} and {@code userData}.
      */
-    private void logFieldClassificationScoreLocked(
-            @NonNull FieldClassificationStrategy fcStrategy,
-            @NonNull ArraySet<String> ignoredDatasets,
-            @NonNull ArrayList<AutofillId> changedFieldIds,
-            @NonNull ArrayList<String> changedDatasetIds,
-            @NonNull ArrayList<AutofillId> manuallyFilledFieldIds,
-            @NonNull ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-            @NonNull FieldClassificationUserData userData,
-            @NonNull Collection<ViewState> viewStates) {
+    private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
+            @NonNull FieldClassificationUserData userData) {
 
         final String[] userValues = userData.getValues();
         final String[] categoryIds = userData.getCategoryIds();
@@ -1501,6 +1508,11 @@
         final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>(
                 maxFieldsSize);
 
+        final Collection<ViewState> viewStates;
+        synchronized (mLock) {
+            viewStates = mViewStates.values();
+        }
+
         final int viewsSize = viewStates.size();
 
         // First, we get all scores.
@@ -1516,10 +1528,7 @@
         final RemoteCallback callback = new RemoteCallback((result) -> {
             if (result == null) {
                 if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
-                mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
-                        ignoredDatasets, changedFieldIds, changedDatasetIds,
-                        manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                        mComponentName, mCompatMode);
+                logContextCommitted(null, null);
                 return;
             }
             final Scores scores = result.getParcelable(EXTRA_SCORES);
@@ -1546,7 +1555,7 @@
                             final Float currentScore = scoresByField.get(categoryId);
                             if (currentScore != null && currentScore > score) {
                                 if (sVerbose) {
-                                    Slog.v(TAG,  "skipping score " + score
+                                    Slog.v(TAG, "skipping score " + score
                                             + " because it's less than " + currentScore);
                                 }
                                 continue;
@@ -1556,8 +1565,7 @@
                                         + autofillId);
                             }
                             scoresByField.put(categoryId, score);
-                        }
-                        else if (sVerbose) {
+                        } else if (sVerbose) {
                             Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
                         }
                     }
@@ -1581,10 +1589,7 @@
                 return;
             }
 
-            mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
-                    ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
-                    manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
-                    mComponentName, mCompatMode);
+            logContextCommitted(detectedFieldIds, detectedFieldClassifications);
         });
 
         fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 47c85683..1bd367c 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1764,7 +1764,8 @@
                                 + ", callingPackage: " + callingPackage;
                 // STOPSHIP (b/128866264): Just to catch breakages. Remove before final release.
                 Slog.wtf(TAG, errorMsg);
-                throw new UnsupportedOperationException(errorMsg);
+                // TODO b/129995049: Resume throwing once issue is resolved.
+                // throw new UnsupportedOperationException(errorMsg);
             }
             setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
                     interval, operation, directReceiver, listenerTag, flags, true, workSource,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 57de67e..e4c39cc 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -108,7 +108,6 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
-import android.net.shared.NetworkMonitorUtils;
 import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
@@ -238,6 +237,16 @@
 
     private static final boolean LOGD_BLOCKED_NETWORKINFO = true;
 
+    /**
+     * Default URL to use for {@link #getCaptivePortalServerUrl()}. This should not be changed
+     * by OEMs for configuration purposes, as this value is overridden by
+     * Settings.Global.CAPTIVE_PORTAL_HTTP_URL.
+     * R.string.config_networkCaptivePortalServerUrl should be overridden instead for this purpose
+     * (preferably via runtime resource overlays).
+     */
+    private static final String DEFAULT_CAPTIVE_PORTAL_HTTP_URL =
+            "http://connectivitycheck.gstatic.com/generate_204";
+
     // TODO: create better separation between radio types and network types
 
     // how long to wait before switching back to a radio's default network
@@ -6543,7 +6552,7 @@
                         uid, newRules, metered, mRestrictBackground);
             }
             if (oldBlocked == newBlocked) {
-                return;
+                continue;
             }
             final int arg = encodeBool(newBlocked);
             for (int i = 0; i < nai.numNetworkRequests(); i++) {
@@ -6701,9 +6710,20 @@
     @Override
     public String getCaptivePortalServerUrl() {
         enforceConnectivityInternalPermission();
-        final String defaultUrl = mContext.getResources().getString(
-                R.string.config_networkDefaultCaptivePortalServerUrl);
-        return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(mContext, defaultUrl);
+        String settingUrl = mContext.getResources().getString(
+                R.string.config_networkCaptivePortalServerUrl);
+
+        if (!TextUtils.isEmpty(settingUrl)) {
+            return settingUrl;
+        }
+
+        settingUrl = Settings.Global.getString(mContext.getContentResolver(),
+                Settings.Global.CAPTIVE_PORTAL_HTTP_URL);
+        if (!TextUtils.isEmpty(settingUrl)) {
+            return settingUrl;
+        }
+
+        return DEFAULT_CAPTIVE_PORTAL_HTTP_URL;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 8847e32..01a3a6f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1020,11 +1020,7 @@
                         if (state.state == STATE_RUNNING_UNLOCKED) {
                             // We'll skip all later code, so we must tell listener it's already
                             // unlocked.
-                            try {
-                                unlockListener.onFinished(userId, null);
-                            } catch (RemoteException ignore) {
-                                // Ignore.
-                            }
+                            notifyFinished(userId, unlockListener);
                         }
                         return true;
                     }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d360a63..e88d62f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4589,25 +4589,42 @@
                 pw.decreaseIndent();
                 pw.decreaseIndent();
 
-                pw.println("enable <ID>");
+                pw.println("enable [--user <USER_ID>] <ID>");
                 pw.increaseIndent();
                 pw.println("allows the given input method ID to be used.");
+                pw.increaseIndent();
+                pw.print("--user <USER_ID>: Specify which user to enable.");
+                pw.println(" Assumes the current user if not specified.");
+                pw.decreaseIndent();
                 pw.decreaseIndent();
 
-                pw.println("disable <ID>");
+                pw.println("disable [--user <USER_ID>] <ID>");
                 pw.increaseIndent();
                 pw.println("disallows the given input method ID to be used.");
+                pw.increaseIndent();
+                pw.print("--user <USER_ID>: Specify which user to disable.");
+                pw.println(" Assumes the current user if not specified.");
+                pw.decreaseIndent();
                 pw.decreaseIndent();
 
-                pw.println("set <ID>");
+                pw.println("set [--user <USER_ID>] <ID>");
                 pw.increaseIndent();
                 pw.println("switches to the given input method ID.");
+                pw.increaseIndent();
+                pw.print("--user <USER_ID>: Specify which user to enable.");
+                pw.println(" Assumes the current user if not specified.");
+                pw.decreaseIndent();
                 pw.decreaseIndent();
 
-                pw.println("reset");
+                pw.println("reset [--user <USER_ID>]");
                 pw.increaseIndent();
                 pw.println("reset currently selected/enabled IMEs to the default ones as if "
                         + "the device is initially booted with the current locale.");
+                pw.increaseIndent();
+                pw.print("--user <USER_ID>: Specify which user to reset.");
+                pw.println(" Assumes the current user if not specified.");
+                pw.decreaseIndent();
+
                 pw.decreaseIndent();
 
                 pw.decreaseIndent();
@@ -4693,32 +4710,108 @@
     @ShellCommandResult
     private int handleShellCommandEnableDisableInputMethod(
             @NonNull ShellCommand shellCommand, boolean enabled) {
-        final String id = shellCommand.getNextArgRequired();
-        final boolean previouslyEnabled;
+        final String imeId = shellCommand.getNextArgRequired();
+        final PrintWriter out = shellCommand.getOutPrintWriter();
+        final PrintWriter error = shellCommand.getErrPrintWriter();
+        final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
         synchronized (mMethodMap) {
-            if (!userHasDebugPriv(mSettings.getCurrentUserId(), shellCommand)) {
-                return ShellCommandResult.SUCCESS;
+            final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
+                    mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
+            for (int userId : userIds) {
+                if (!userHasDebugPriv(userId, shellCommand)) {
+                    continue;
+                }
+                handleShellCommandEnableDisableInputMethodInternalLocked(userId, imeId, enabled,
+                        out, error);
             }
-            // Make sure this is a valid input method.
-            if (enabled && !mMethodMap.containsKey(id)) {
-                final PrintWriter error = shellCommand.getErrPrintWriter();
-                error.print("Unknown input method ");
-                error.print(id);
-                error.println(" cannot be enabled");
-                return ShellCommandResult.SUCCESS;
-            }
-            previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
         }
-        final PrintWriter pr = shellCommand.getOutPrintWriter();
-        pr.print("Input method ");
-        pr.print(id);
-        pr.print(": ");
-        pr.print((enabled == previouslyEnabled) ? "already " : "now ");
-        pr.println(enabled ? "enabled" : "disabled");
         return ShellCommandResult.SUCCESS;
     }
 
     /**
+     * A special helper method for commands that only have {@code -u} and {@code --user} options.
+     *
+     * <p>You cannot use this helper method if the command has other options.</p>
+     *
+     * @param shellCommand {@link ShellCommand} from which options should be obtained.
+     * @return User ID to be resolved. {@link UserHandle#CURRENT} if not specified.
+     */
+    @BinderThread
+    @UserIdInt
+    private static int handleOptionsForCommandsThatOnlyHaveUserOption(ShellCommand shellCommand) {
+        while (true) {
+            final String nextOption = shellCommand.getNextOption();
+            if (nextOption == null) {
+                break;
+            }
+            switch (nextOption) {
+                case "-u":
+                case "--user":
+                    return UserHandle.parseUserArg(shellCommand.getNextArgRequired());
+            }
+        }
+        return UserHandle.USER_CURRENT;
+    }
+
+    @BinderThread
+    private void handleShellCommandEnableDisableInputMethodInternalLocked(
+            @UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
+            PrintWriter error) {
+        boolean failedToEnableUnknownIme = false;
+        boolean previouslyEnabled = false;
+        if (userId == mSettings.getCurrentUserId()) {
+            if (enabled && !mMethodMap.containsKey(imeId)) {
+                failedToEnableUnknownIme = true;
+            } else {
+                previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled);
+            }
+        } else {
+            final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+            final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+            final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+                    new ArrayMap<>();
+            AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+            queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
+                    methodMap, methodList);
+            final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
+                    mContext.getContentResolver(), methodMap, userId, false);
+            if (enabled) {
+                if (!methodMap.containsKey(imeId)) {
+                    failedToEnableUnknownIme = true;
+                } else {
+                    for (InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
+                        if (TextUtils.equals(imi.getId(), imeId)) {
+                            previouslyEnabled = true;
+                            break;
+                        }
+                    }
+                    if (!previouslyEnabled) {
+                        settings.appendAndPutEnabledInputMethodLocked(imeId, false);
+                    }
+                }
+            } else {
+                previouslyEnabled =
+                        settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+                                new StringBuilder(),
+                                settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId);
+            }
+        }
+        if (failedToEnableUnknownIme) {
+            error.print("Unknown input method ");
+            error.print(imeId);
+            error.println(" cannot be enabled for user #" + userId);
+        } else {
+            out.print("Input method ");
+            out.print(imeId);
+            out.print(": ");
+            out.print((enabled == previouslyEnabled) ? "already " : "now ");
+            out.print(enabled ? "enabled" : "disabled");
+            out.print(" for user #");
+            out.println(userId);
+        }
+    }
+
+    /**
      * Handles {@code adb shell ime set}.
      * @param shellCommand {@link ShellCommand} object that is handling this command.
      * @return Exit code of the command.
@@ -4726,17 +4819,55 @@
     @BinderThread
     @ShellCommandResult
     private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) {
-        final String id = shellCommand.getNextArgRequired();
+        final String imeId = shellCommand.getNextArgRequired();
+        final PrintWriter out = shellCommand.getOutPrintWriter();
+        final PrintWriter error = shellCommand.getErrPrintWriter();
+        final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
         synchronized (mMethodMap) {
-            if (!userHasDebugPriv(mSettings.getCurrentUserId(), shellCommand)) {
-                return ShellCommandResult.SUCCESS;
+            final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
+                    mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
+            for (int userId : userIds) {
+                if (!userHasDebugPriv(userId, shellCommand)) {
+                    continue;
+                }
+                boolean failedToSelectUnknownIme = false;
+                if (userId == mSettings.getCurrentUserId()) {
+                    if (mMethodMap.containsKey(imeId)) {
+                        setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
+                    } else {
+                        failedToSelectUnknownIme = true;
+                    }
+                } else {
+                    final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+                    final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+                    final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+                            new ArrayMap<>();
+                    AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+                    queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
+                            methodMap, methodList);
+                    final InputMethodSettings settings = new InputMethodSettings(
+                            mContext.getResources(), mContext.getContentResolver(), methodMap,
+                            userId, false);
+                    if (methodMap.containsKey(imeId)) {
+                        settings.putSelectedInputMethod(imeId);
+                        settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+                    } else {
+                        failedToSelectUnknownIme = true;
+                    }
+                }
+                if (failedToSelectUnknownIme) {
+                    error.print("Unknown input method ");
+                    error.print(imeId);
+                    error.print(" cannot be selected for user #");
+                    error.println(userId);
+                } else {
+                    out.print("Input method ");
+                    out.print(imeId);
+                    out.print(" selected for user #");
+                    error.println(userId);
+                }
             }
-            setInputMethodLocked(id, NOT_A_SUBTYPE_ID);
         }
-        final PrintWriter pr = shellCommand.getOutPrintWriter();
-        pr.print("Input method ");
-        pr.print(id);
-        pr.println("  selected");
         return ShellCommandResult.SUCCESS;
     }
 
@@ -4748,45 +4879,67 @@
     @BinderThread
     @ShellCommandResult
     private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
+        final PrintWriter out = shellCommand.getOutPrintWriter();
+        final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
         synchronized (mMethodMap) {
-            if (!userHasDebugPriv(mSettings.getCurrentUserId(), shellCommand)) {
-                return ShellCommandResult.SUCCESS;
-            }
-            final String nextIme;
-            final List<InputMethodInfo> nextEnabledImes;
-            hideCurrentInputLocked(0, null);
-            unbindCurrentMethodLocked();
-            // Reset the current IME
-            resetSelectedInputMethodAndSubtypeLocked(null);
-            // Also reset the settings of the current IME
-            mSettings.putSelectedInputMethod(null);
-            // Disable all enabled IMEs.
-            mSettings.getEnabledInputMethodListLocked().forEach(
-                    imi -> setInputMethodEnabledLocked(imi.getId(), false));
-            // Re-enable with default enabled IMEs.
-            InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
-                    imi -> setInputMethodEnabledLocked(imi.getId(), true));
-            updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
-            InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
-                    mSettings.getEnabledInputMethodListLocked(),
-                    mSettings.getCurrentUserId(),
-                    mContext.getBasePackageName());
-            nextIme = mSettings.getSelectedInputMethod();
-            nextEnabledImes = mSettings.getEnabledInputMethodListLocked();
-            final PrintWriter pr = shellCommand.getOutPrintWriter();
-            pr.println("Reset current and enabled IMEs");
-            pr.println("Newly selected IME:");
-            pr.print("  "); pr.println(nextIme);
-            pr.println("Newly enabled IMEs:");
-            {
-                final int N = nextEnabledImes.size();
-                for (int i = 0; i < N; ++i) {
-                    pr.print("  ");
-                    pr.println(nextEnabledImes.get(i).getId());
+            final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
+                    mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
+            for (int userId : userIds) {
+                if (!userHasDebugPriv(userId, shellCommand)) {
+                    continue;
                 }
+                final String nextIme;
+                final List<InputMethodInfo> nextEnabledImes;
+                if (userId == mSettings.getCurrentUserId()) {
+                    hideCurrentInputLocked(0, null);
+                    unbindCurrentMethodLocked();
+                    // Reset the current IME
+                    resetSelectedInputMethodAndSubtypeLocked(null);
+                    // Also reset the settings of the current IME
+                    mSettings.putSelectedInputMethod(null);
+                    // Disable all enabled IMEs.
+                    mSettings.getEnabledInputMethodListLocked().forEach(
+                            imi -> setInputMethodEnabledLocked(imi.getId(), false));
+                    // Re-enable with default enabled IMEs.
+                    InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
+                            imi -> setInputMethodEnabledLocked(imi.getId(), true));
+                    updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
+                    InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
+                            mSettings.getEnabledInputMethodListLocked(),
+                            mSettings.getCurrentUserId(),
+                            mContext.getBasePackageName());
+                    nextIme = mSettings.getSelectedInputMethod();
+                    nextEnabledImes = mSettings.getEnabledInputMethodListLocked();
+                } else {
+                    final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+                    final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+                    final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+                            new ArrayMap<>();
+                    AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+                    queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
+                            methodMap, methodList);
+                    final InputMethodSettings settings = new InputMethodSettings(
+                            mContext.getResources(), mContext.getContentResolver(), methodMap,
+                            userId, false);
+
+                    nextEnabledImes = InputMethodUtils.getDefaultEnabledImes(mContext, methodList);
+                    nextIme = InputMethodUtils.getMostApplicableDefaultIME(nextEnabledImes).getId();
+
+                    // Reset enabled IMEs.
+                    settings.putEnabledInputMethodsStr("");
+                    nextEnabledImes.forEach(imi -> settings.appendAndPutEnabledInputMethodLocked(
+                            imi.getId(), false));
+
+                    // Reset selected IME.
+                    settings.putSelectedInputMethod(nextIme);
+                    settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+                }
+                out.println("Reset current and enabled IMEs for user #" + userId);
+                out.println("  Selected: " + nextIme);
+                nextEnabledImes.forEach(ime -> out.println("   Enabled: " + ime.getId()));
             }
-            return ShellCommandResult.SUCCESS;
         }
+        return ShellCommandResult.SUCCESS;
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 4349b4a..b5e19ae 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -1003,7 +1003,7 @@
             return res;
         }
 
-        private void putEnabledInputMethodsStr(@Nullable String str) {
+        void putEnabledInputMethodsStr(@Nullable String str) {
             if (DEBUG) {
                 Slog.d(TAG, "putEnabledInputMethodStr: " + str);
             }
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index 2948aaf..2e72fbd 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -457,15 +457,13 @@
         }
         mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
 
-        // The NetworkRequest.Builder class is not used to construct the network request because
-        // the ConnectivityService requires the network request to be constructed in this way
-        // to extend support for requestRouteToHostAddress() method for pre-gnss@2.0 devices.
-        NetworkCapabilities networkCapabilities = new NetworkCapabilities();
-        networkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        networkCapabilities.addCapability(getNetworkCapability(mAGpsType));
-        NetworkRequest networkRequest = new NetworkRequest(networkCapabilities,
-                getLegacyDataConnectionType(agpsType), ConnectivityManager.REQUEST_ID_UNSET,
-                NetworkRequest.Type.REQUEST);
+        // The transport type must be set to NetworkCapabilities.TRANSPORT_CELLULAR for the
+        // deprecated requestRouteToHostAddress() method in ConnectivityService to work for
+        // pre-gnss@2.0 devices.
+        NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
+        networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType));
+        networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        NetworkRequest networkRequest = networkRequestBuilder.build();
         mConnMgr.requestNetwork(
                 networkRequest,
                 mSuplConnectivityCallback,
@@ -487,19 +485,6 @@
         }
     }
 
-    private int getLegacyDataConnectionType(int agpsType) {
-        switch (agpsType) {
-            case AGPS_TYPE_C2K:
-            case AGPS_TYPE_SUPL:
-                return ConnectivityManager.TYPE_MOBILE_SUPL;
-            case AGPS_TYPE_EIMS:
-                return ConnectivityManager.TYPE_MOBILE_EMERGENCY;
-            case AGPS_TYPE_IMS:
-                return ConnectivityManager.TYPE_MOBILE_IMS;
-            default:
-                throw new IllegalArgumentException("agpsType: " + agpsType);
-        }
-    }
     private void handleReleaseSuplConnection(int agpsDataConnStatus) {
         if (DEBUG) {
             String message = String.format(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dec47a1..7f1b25ca 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1516,6 +1516,11 @@
     }
 
     @VisibleForTesting
+    void setZenHelper(ZenModeHelper zenHelper) {
+        mZenModeHelper = zenHelper;
+    }
+
+    @VisibleForTesting
     void setIsAutomotive(boolean isAutomotive) {
         mIsAutomotive = isAutomotive;
     }
@@ -2856,7 +2861,7 @@
         }
 
         @Override
-        public List<String> getAllowedAssistantCapabilities(String pkg) {
+        public List<String> getAllowedAssistantAdjustments(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
 
             if (!isCallerSystemOrPhone()
@@ -2864,11 +2869,11 @@
                     throw new SecurityException("Not currently an assistant");
             }
 
-            return mAssistants.getAllowedAssistantCapabilities();
+            return mAssistants.getAllowedAssistantAdjustments();
         }
 
         @Override
-        public void allowAssistantCapability(String adjustmentType) {
+        public void allowAssistantAdjustment(String adjustmentType) {
             checkCallerIsSystemOrSystemUiOrShell();
             mAssistants.allowAdjustmentType(adjustmentType);
 
@@ -2876,7 +2881,7 @@
         }
 
         @Override
-        public void disallowAssistantCapability(String adjustmentType) {
+        public void disallowAssistantAdjustment(String adjustmentType) {
             checkCallerIsSystemOrSystemUiOrShell();
             mAssistants.disallowAdjustmentType(adjustmentType);
 
@@ -3563,7 +3568,7 @@
                 return;
             }
             boolean accessAllowed = false;
-            String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
+            String[] packages = mPackageManagerClient.getPackagesForUid(uid);
             final int packageCount = packages.length;
             for (int i = 0; i < packageCount; i++) {
                 if (mConditionProviders.isPackageOrComponentAllowed(
@@ -7410,7 +7415,7 @@
             }
         }
 
-        protected List<String> getAllowedAssistantCapabilities() {
+        protected List<String> getAllowedAssistantAdjustments() {
             synchronized (mLock) {
                 List<String> types = new ArrayList<>();
                 types.addAll(mAllowedAdjustments);
@@ -7470,7 +7475,7 @@
         private void notifyCapabilitiesChanged(final ManagedServiceInfo info) {
             final INotificationListener assistant = (INotificationListener) info.service;
             try {
-                assistant.onCapabilitiesChanged();
+                assistant.onAllowedAdjustmentsChanged();
             } catch (RemoteException ex) {
                 Slog.e(TAG, "unable to notify assistant (capabilities): " + assistant, ex);
             }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ea7bf2d2..7e74cc2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -389,8 +389,8 @@
             if (mConfig == null) return;
 
             newConfig = mConfig.copy();
+            setAutomaticZenRuleStateLocked(newConfig, newConfig.automaticRules.get(id), condition);
         }
-        setAutomaticZenRuleState(newConfig, newConfig.automaticRules.get(id), condition);
     }
 
     public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition) {
@@ -398,14 +398,15 @@
         synchronized (mConfig) {
             if (mConfig == null) return;
             newConfig = mConfig.copy();
-        }
 
-        setAutomaticZenRuleState(newConfig,
-                findMatchingRule(newConfig, ruleDefinition, condition),
-                condition);
+            setAutomaticZenRuleStateLocked(newConfig,
+                    findMatchingRule(newConfig, ruleDefinition, condition),
+                    condition);
+        }
     }
 
-    private void setAutomaticZenRuleState(ZenModeConfig config, ZenRule rule, Condition condition) {
+    private void setAutomaticZenRuleStateLocked(ZenModeConfig config, ZenRule rule,
+            Condition condition) {
         if (rule == null) return;
 
         rule.condition = condition;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6c5abe4..928b8f6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -757,9 +757,124 @@
         @Override public final boolean hasFeature(String feature) {
             return PackageManagerService.this.hasSystemFeature(feature, 0);
         }
+
+        final List<PackageParser.Package> getStaticOverlayPackages(
+                Collection<PackageParser.Package> allPackages, String targetPackageName) {
+            if ("android".equals(targetPackageName)) {
+                // Static RROs targeting to "android", ie framework-res.apk, are already applied by
+                // native AssetManager.
+                return null;
+            }
+
+            List<PackageParser.Package> overlayPackages = null;
+            for (PackageParser.Package p : allPackages) {
+                if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
+                    if (overlayPackages == null) {
+                        overlayPackages = new ArrayList<>();
+                    }
+                    overlayPackages.add(p);
+                }
+            }
+            if (overlayPackages != null) {
+                Comparator<PackageParser.Package> cmp =
+                        Comparator.comparingInt(p -> p.mOverlayPriority);
+                overlayPackages.sort(cmp);
+            }
+            return overlayPackages;
+        }
+
+        final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
+                String targetPath) {
+            if (overlayPackages == null || overlayPackages.isEmpty()) {
+                return null;
+            }
+            List<String> overlayPathList = null;
+            for (PackageParser.Package overlayPackage : overlayPackages) {
+                if (targetPath == null) {
+                    if (overlayPathList == null) {
+                        overlayPathList = new ArrayList<>();
+                    }
+                    overlayPathList.add(overlayPackage.baseCodePath);
+                    continue;
+                }
+
+                try {
+                    // Creates idmaps for system to parse correctly the Android manifest of the
+                    // target package.
+                    //
+                    // OverlayManagerService will update each of them with a correct gid from its
+                    // target package app id.
+                    mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
+                            UserHandle.getSharedAppGid(
+                                    UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
+                    if (overlayPathList == null) {
+                        overlayPathList = new ArrayList<>();
+                    }
+                    overlayPathList.add(overlayPackage.baseCodePath);
+                } catch (InstallerException e) {
+                    Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " +
+                            overlayPackage.baseCodePath);
+                }
+            }
+            return overlayPathList == null ? null : overlayPathList.toArray(new String[0]);
+        }
+
+        String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
+            List<PackageParser.Package> overlayPackages;
+            synchronized (mInstallLock) {
+                synchronized (mPackages) {
+                    overlayPackages = getStaticOverlayPackages(
+                            mPackages.values(), targetPackageName);
+                }
+                // It is safe to keep overlayPackages without holding mPackages because static overlay
+                // packages can't be uninstalled or disabled.
+                return getStaticOverlayPaths(overlayPackages, targetPath);
+            }
+        }
+
+        @Override public final String[] getOverlayApks(String targetPackageName) {
+            return getStaticOverlayPaths(targetPackageName, null);
+        }
+
+        @Override public final String[] getOverlayPaths(String targetPackageName,
+                String targetPath) {
+            return getStaticOverlayPaths(targetPackageName, targetPath);
+        }
+    }
+
+    class ParallelPackageParserCallback extends PackageParserCallback {
+        List<PackageParser.Package> mOverlayPackages = null;
+
+        void findStaticOverlayPackages() {
+            synchronized (mPackages) {
+                for (PackageParser.Package p : mPackages.values()) {
+                    if (p.mOverlayIsStatic) {
+                        if (mOverlayPackages == null) {
+                            mOverlayPackages = new ArrayList<>();
+                        }
+                        mOverlayPackages.add(p);
+                    }
+                }
+            }
+        }
+
+        @Override
+        synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
+            // We can trust mOverlayPackages without holding mPackages because package uninstall
+            // can't happen while running parallel parsing.
+            // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
+            // because mInstallLock is held before running parallel parsing.
+            // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
+            return mOverlayPackages == null ? null :
+                    getStaticOverlayPaths(
+                            getStaticOverlayPackages(mOverlayPackages, targetPackageName),
+                            targetPath);
+        }
     }
 
     final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
+    final ParallelPackageParserCallback mParallelPackageParserCallback =
+            new ParallelPackageParserCallback();
 
     // Currently known shared libraries.
     final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
@@ -2443,6 +2558,8 @@
                     | SCAN_AS_ODM,
                     0);
 
+            mParallelPackageParserCallback.findStaticOverlayPackages();
+
             // Find base frameworks (resource packages without code).
             scanDirTracedLI(frameworkDir,
                     mDefParseFlags
@@ -2850,7 +2967,7 @@
 
                 // Uncompress and install any stubbed system applications.
                 // This must be done last to ensure all stubs are replaced or disabled.
-                decompressSystemApplications(stubSystemApps, scanFlags);
+                installSystemStubPackages(stubSystemApps, scanFlags);
 
                 final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()
                                 - cachedSystemApps;
@@ -3161,49 +3278,37 @@
      * <p>In order to forcefully attempt an installation of a full application, go to app
      * settings and enable the application.
      */
-    private void decompressSystemApplications(@NonNull List<String> stubSystemApps, int scanFlags) {
-        for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
-            final String pkgName = stubSystemApps.get(i);
+    private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
+            @ScanFlags int scanFlags) {
+        for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+            final String packageName = systemStubPackageNames.get(i);
             // skip if the system package is already disabled
-            if (mSettings.isDisabledSystemPackageLPr(pkgName)) {
-                stubSystemApps.remove(i);
+            if (mSettings.isDisabledSystemPackageLPr(packageName)) {
+                systemStubPackageNames.remove(i);
                 continue;
             }
             // skip if the package isn't installed (?!); this should never happen
-            final PackageParser.Package pkg = mPackages.get(pkgName);
+            final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
-                stubSystemApps.remove(i);
+                systemStubPackageNames.remove(i);
                 continue;
             }
             // skip if the package has been disabled by the user
-            final PackageSetting ps = mSettings.mPackages.get(pkgName);
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps != null) {
                 final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
                 if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
-                    stubSystemApps.remove(i);
+                    systemStubPackageNames.remove(i);
                     continue;
                 }
             }
 
-            if (DEBUG_COMPRESSION) {
-                Slog.i(TAG, "Uncompressing system stub; pkg: " + pkgName);
-            }
-
-            // uncompress the binary to its eventual destination on /data
-            final File scanFile = decompressPackage(pkg);
-            if (scanFile == null) {
-                continue;
-            }
-
             // install the package to replace the stub on /system
             try {
-                mSettings.disableSystemPackageLPw(pkgName, true /*replaced*/);
-                removePackageLI(pkg, true /*chatty*/);
-                scanPackageTracedLI(scanFile, 0 /*reparseFlags*/, scanFlags, 0, null);
+                installStubPackageLI(pkg, 0, scanFlags);
                 ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
                         UserHandle.USER_SYSTEM, "android");
-                stubSystemApps.remove(i);
-                continue;
+                systemStubPackageNames.remove(i);
             } catch (PackageManagerException e) {
                 Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
             }
@@ -3212,8 +3317,8 @@
         }
 
         // disable any stub still left; these failed to install the full application
-        for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
-            final String pkgName = stubSystemApps.get(i);
+        for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+            final String pkgName = systemStubPackageNames.get(i);
             final PackageSetting ps = mSettings.mPackages.get(pkgName);
             ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                     UserHandle.USER_SYSTEM, "android");
@@ -3222,20 +3327,107 @@
     }
 
     /**
+     * Extract, install and enable a stub package.
+     * <p>If the compressed file can not be extracted / installed for any reason, the stub
+     * APK will be installed and the package will be disabled. To recover from this situation,
+     * the user will need to go into system settings and re-enable the package.
+     */
+    private boolean enableCompressedPackage(PackageParser.Package stubPkg) {
+        final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+                | PackageParser.PARSE_ENFORCE_CODE;
+        synchronized (mInstallLock) {
+            final PackageParser.Package pkg;
+            try (PackageFreezer freezer =
+                    freezePackage(stubPkg.packageName, "setEnabledSetting")) {
+                pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
+                synchronized (mPackages) {
+                    prepareAppDataAfterInstallLIF(pkg);
+                    try {
+                        updateSharedLibrariesLocked(pkg, null, mPackages);
+                    } catch (PackageManagerException e) {
+                        Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
+                    }
+                    mPermissionManager.updatePermissions(
+                            pkg.packageName, pkg, true, mPackages.values(),
+                            mPermissionCallback);
+                    mSettings.writeLPr();
+                }
+            } catch (PackageManagerException e) {
+                // Whoops! Something went very wrong; roll back to the stub and disable the package
+                try (PackageFreezer freezer =
+                        freezePackage(stubPkg.packageName, "setEnabledSetting")) {
+                    synchronized (mPackages) {
+                        // NOTE: Ensure the system package is enabled; even for a compressed stub.
+                        // If we don't, installing the system package fails during scan
+                        enableSystemPackageLPw(stubPkg);
+                    }
+                    installPackageFromSystemLIF(stubPkg.codePath,
+                            null /*allUserHandles*/, null /*origUserHandles*/,
+                            null /*origPermissionsState*/, true /*writeSettings*/);
+                } catch (PackageManagerException pme) {
+                    // Serious WTF; we have to be able to install the stub
+                    Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme);
+                } finally {
+                    // Disable the package; the stub by itself is not runnable
+                    synchronized (mPackages) {
+                        final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
+                        if (stubPs != null) {
+                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
+                                    UserHandle.USER_SYSTEM, "android");
+                        }
+                        mSettings.writeLPr();
+                    }
+                }
+                return false;
+            }
+            clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
+                    | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+            mDexManager.notifyPackageUpdated(pkg.packageName,
+                    pkg.baseCodePath, pkg.splitCodePaths);
+        }
+        return true;
+    }
+
+    private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg,
+            @ParseFlags int parseFlags, @ScanFlags int scanFlags)
+                    throws PackageManagerException {
+        if (DEBUG_COMPRESSION) {
+            Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.packageName);
+        }
+        // uncompress the binary to its eventual destination on /data
+        final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath);
+        if (scanFile == null) {
+            throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath);
+        }
+        synchronized (mPackages) {
+            mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/);
+        }
+        removePackageLI(stubPkg, true /*chatty*/);
+        try {
+            return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+        } catch (PackageManagerException e) {
+            Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.packageName, e);
+            // Remove the failed install
+            removeCodePathLI(scanFile);
+            throw e;
+        }
+    }
+
+    /**
      * Decompresses the given package on the system image onto
      * the /data partition.
      * @return The directory the package was decompressed into. Otherwise, {@code null}.
      */
-    private File decompressPackage(PackageParser.Package pkg) {
-        final File[] compressedFiles = getCompressedFiles(pkg.codePath);
+    private File decompressPackage(String packageName, String codePath) {
+        final File[] compressedFiles = getCompressedFiles(codePath);
         if (compressedFiles == null || compressedFiles.length == 0) {
             if (DEBUG_COMPRESSION) {
-                Slog.i(TAG, "No files to decompress: " + pkg.baseCodePath);
+                Slog.i(TAG, "No files to decompress: " + codePath);
             }
             return null;
         }
         final File dstCodePath =
-                getNextCodePath(Environment.getDataAppDirectory(null), pkg.packageName);
+                getNextCodePath(Environment.getDataAppDirectory(null), packageName);
         int ret = PackageManager.INSTALL_SUCCEEDED;
         try {
             Os.mkdir(dstCodePath.getAbsolutePath(), 0755);
@@ -3248,14 +3440,14 @@
                 ret = decompressFile(srcFile, dstFile);
                 if (ret != PackageManager.INSTALL_SUCCEEDED) {
                     logCriticalInfo(Log.ERROR, "Failed to decompress"
-                            + "; pkg: " + pkg.packageName
+                            + "; pkg: " + packageName
                             + ", file: " + dstFileName);
                     break;
                 }
             }
         } catch (ErrnoException e) {
             logCriticalInfo(Log.ERROR, "Failed to decompress"
-                    + "; pkg: " + pkg.packageName
+                    + "; pkg: " + packageName
                     + ", err: " + e.errno);
         }
         if (ret == PackageManager.INSTALL_SUCCEEDED) {
@@ -3267,7 +3459,7 @@
                         null /*abiOverride*/);
             } catch (IOException e) {
                 logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
-                        + "; pkg: " + pkg.packageName);
+                        + "; pkg: " + packageName);
                 ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
             } finally {
                 IoUtils.closeQuietly(handle);
@@ -8665,7 +8857,7 @@
         }
         try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                 mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
-                mPackageParserCallback)) {
+                mParallelPackageParserCallback)) {
             // Submit files for parsing in parallel
             int fileCount = 0;
             for (File file : files) {
@@ -18099,12 +18291,15 @@
             return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
         }
 
-        PackageSetting uninstalledPs;
-        PackageParser.Package pkg;
+        final PackageSetting uninstalledPs;
+        final PackageSetting disabledSystemPs;
+        final PackageParser.Package pkg;
 
         // for the uninstall-updates case and restricted profiles, remember the per-
         // user handle installed state
         int[] allUsers;
+        /** enabled state of the uninstalled application */
+        final int origEnabledState;
         synchronized (mPackages) {
             uninstalledPs = mSettings.mPackages.get(packageName);
             if (uninstalledPs == null) {
@@ -18119,6 +18314,11 @@
                 return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
             }
 
+            disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
+            // Save this off 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);
@@ -18187,10 +18387,30 @@
         Runtime.getRuntime().gc();
         // Delete the resources here after sending the broadcast to let
         // other processes clean up before deleting resources.
-        if (info.args != null) {
-            synchronized (mInstallLock) {
+        synchronized (mInstallLock) {
+            if (info.args != null) {
                 info.args.doPostDeleteLI(true);
             }
+            final PackageParser.Package stubPkg =
+                    (disabledSystemPs == null) ? null : disabledSystemPs.pkg;
+            if (stubPkg != null && stubPkg.isStub) {
+                synchronized (mPackages) {
+                    // restore the enabled state of the stub; the state is overwritten when
+                    // the stub is uninstalled
+                    final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
+                    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.packageName);
+                    }
+                    enableCompressedPackage(stubPkg);
+                }
+            }
         }
 
         return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
@@ -18596,7 +18816,14 @@
             throw new SystemDeleteException(e);
         } finally {
             if (disabledPs.pkg.isStub) {
-                mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
+                // We've re-installed the stub; make sure it's disabled here. If package was
+                // originally enabled, we'll install the compressed version of the application
+                // and re-enable it afterward.
+                final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.packageName);
+                if (stubPs != null) {
+                    stubPs.setEnabled(
+                            COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
+                }
             }
         }
     }
@@ -20681,102 +20908,9 @@
             if (isSystemStub
                     && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
-                final File codePath = decompressPackage(deletedPkg);
-                if (codePath == null) {
-                    Slog.e(TAG, "couldn't decompress pkg: " + pkgSetting.name);
+                if (!enableCompressedPackage(deletedPkg)) {
                     return;
                 }
-                // TODO remove direct parsing of the package object during internal cleanup
-                // of scan package
-                // We need to call parse directly here for no other reason than we need
-                // the new package in order to disable the old one [we use the information
-                // for some internal optimization to optionally create a new package setting
-                // object on replace]. However, we can't get the package from the scan
-                // because the scan modifies live structures and we need to remove the
-                // old [system] package from the system before a scan can be attempted.
-                // Once scan is indempotent we can remove this parse and use the package
-                // object we scanned, prior to adding it to package settings.
-                final PackageParser pp = new PackageParser();
-                pp.setSeparateProcesses(mSeparateProcesses);
-                pp.setDisplayMetrics(mMetrics);
-                pp.setCallback(mPackageParserCallback);
-                final PackageParser.Package tmpPkg;
-                try {
-                    final @ParseFlags int parseFlags = mDefParseFlags
-                            | PackageParser.PARSE_MUST_BE_APK
-                            | PackageParser.PARSE_IS_SYSTEM_DIR;
-                    tmpPkg = pp.parsePackage(codePath, parseFlags);
-                } catch (PackageParserException e) {
-                    Slog.w(TAG, "Failed to parse compressed system package:" + pkgSetting.name, e);
-                    return;
-                }
-                synchronized (mInstallLock) {
-                    // Disable the stub and remove any package entries
-                    removePackageLI(deletedPkg, true);
-                    synchronized (mPackages) {
-                        disableSystemPackageLPw(deletedPkg, tmpPkg);
-                    }
-                    final PackageParser.Package pkg;
-                    try (PackageFreezer freezer =
-                            freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
-                        final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
-                                | PackageParser.PARSE_ENFORCE_CODE;
-                        pkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,
-                                0 /*currentTime*/, null /*user*/);
-                        prepareAppDataAfterInstallLIF(pkg);
-                        synchronized (mPackages) {
-                            try {
-                                updateSharedLibrariesLocked(pkg, null, mPackages);
-                            } catch (PackageManagerException e) {
-                                Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
-                            }
-                            mPermissionManager.updatePermissions(
-                                    pkg.packageName, pkg, true, mPackages.values(),
-                                    mPermissionCallback);
-                            mSettings.writeLPr();
-                        }
-                    } catch (PackageManagerException e) {
-                        // Whoops! Something went wrong; try to roll back to the stub
-                        Slog.w(TAG, "Failed to install compressed system package:"
-                                + pkgSetting.name, e);
-                        // Remove the failed install
-                        removeCodePathLI(codePath);
-
-                        // Install the system package
-                        try (PackageFreezer freezer =
-                                freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
-                            synchronized (mPackages) {
-                                // NOTE: The system package always needs to be enabled; even
-                                // if it's for a compressed stub. If we don't, installing the
-                                // system package fails during scan [scanning checks the disabled
-                                // packages]. We will reverse this later, after we've "installed"
-                                // the stub.
-                                // This leaves us in a fragile state; the stub should never be
-                                // enabled, so, cross your fingers and hope nothing goes wrong
-                                // until we can disable the package later.
-                                enableSystemPackageLPw(deletedPkg);
-                            }
-                            installPackageFromSystemLIF(deletedPkg.codePath,
-                                    /*isPrivileged*/ null /*allUserHandles*/,
-                                    null /*origUserHandles*/, null /*origPermissionsState*/,
-                                    true /*writeSettings*/);
-                        } catch (PackageManagerException pme) {
-                            Slog.w(TAG, "Failed to restore system package:"
-                                    + deletedPkg.packageName, pme);
-                        } finally {
-                            synchronized (mPackages) {
-                                mSettings.disableSystemPackageLPw(
-                                        deletedPkg.packageName, true /*replaced*/);
-                                mSettings.writeLPr();
-                            }
-                        }
-                        return;
-                    }
-                    clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
-                            | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-                    mDexManager.notifyPackageUpdated(pkg.packageName,
-                            pkg.baseCodePath, pkg.splitCodePaths);
-                }
             }
             if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                 || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f83b3ea..96924c04 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1458,8 +1458,18 @@
 
     private void pullNumBiometricsEnrolled(int modality, int tagId, long elapsedNanos,
             long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
-        FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class);
-        FaceManager faceManager = mContext.getSystemService(FaceManager.class);
+        final PackageManager pm = mContext.getPackageManager();
+        FingerprintManager fingerprintManager = null;
+        FaceManager faceManager = null;
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            fingerprintManager = mContext.getSystemService(
+                    FingerprintManager.class);
+        }
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            faceManager = mContext.getSystemService(FaceManager.class);
+        }
+
         if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e2253e7..6cf36d6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -88,6 +88,8 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Process.SYSTEM_UID;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 
 import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
@@ -195,6 +197,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
+import android.view.DisplayCutout;
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IApplicationToken;
 import android.view.RemoteAnimationDefinition;
@@ -382,6 +385,12 @@
     private int[] mHorizontalSizeConfigurations;
     private int[] mSmallestSizeConfigurations;
 
+    /**
+     * The precomputed display insets for resolving configuration. It will be non-null if
+     * {@link #shouldUseSizeCompatMode} returns {@code true}.
+     */
+    private CompatDisplayInsets mCompatDisplayInsets;
+
     boolean pendingVoiceInteractionStart;   // Waiting for activity-invoked voice session
     IVoiceInteractionSession voiceSession;  // Voice interaction session for this activity
 
@@ -2833,6 +2842,11 @@
                 // The smallest screen width is the short side of screen bounds. Because the bounds
                 // and density won't be changed, smallestScreenWidthDp is also fixed.
                 overrideConfig.smallestScreenWidthDp = parentConfig.smallestScreenWidthDp;
+
+                final ActivityDisplay display = getDisplay();
+                if (display != null && display.mDisplayContent != null) {
+                    mCompatDisplayInsets = new CompatDisplayInsets(display.mDisplayContent);
+                }
             }
         }
         onRequestedOverrideConfigurationChanged(overrideConfig);
@@ -2849,7 +2863,7 @@
             super.resolveOverrideConfiguration(newParentConfiguration);
             if (hasOverrideBounds) {
                 task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
-                        newParentConfiguration, true /* insideParentBounds */);
+                        newParentConfiguration);
             }
         }
 
@@ -2922,9 +2936,8 @@
             resolvedBounds.right -= resolvedAppBounds.left;
         }
 
-        // In size compatibility mode, activity is allowed to have larger bounds than its parent.
         task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
-                false /* insideParentBounds */);
+                mCompatDisplayInsets);
         // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
         // the parent bounds appropriately.
         if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
@@ -3450,6 +3463,7 @@
         // configuration.
         getRequestedOverrideConfiguration().setToDefaults();
         getResolvedOverrideConfiguration().setToDefaults();
+        mCompatDisplayInsets = null;
         if (visible) {
             // Configuration will be ensured when becoming visible, so if it is already visible,
             // then the manual update is needed.
@@ -3796,4 +3810,46 @@
         writeToProto(proto);
         proto.end(token);
     }
+
+    /**
+     * The precomputed insets of the display in each rotation. This is used to make the size
+     * compatibility mode activity compute the configuration without relying on its current display.
+     */
+    static class CompatDisplayInsets {
+        final int mDisplayWidth;
+        final int mDisplayHeight;
+
+        /** The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. */
+        final Rect[] mNonDecorInsets = new Rect[4];
+        /**
+         * The stableInsets for each rotation. Includes the status bar inset and the
+         * nonDecorInsets. It is used to compute {@link Configuration#screenWidthDp} and
+         * {@link Configuration#screenHeightDp}.
+         */
+        final Rect[] mStableInsets = new Rect[4];
+
+        CompatDisplayInsets(DisplayContent display) {
+            mDisplayWidth = display.mBaseDisplayWidth;
+            mDisplayHeight = display.mBaseDisplayHeight;
+            final DisplayPolicy policy = display.getDisplayPolicy();
+            final DisplayCutout cutout = display.getDisplayInfo().displayCutout;
+            for (int rotation = 0; rotation < 4; rotation++) {
+                mNonDecorInsets[rotation] = new Rect();
+                mStableInsets[rotation] = new Rect();
+                final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+                final int dw = rotated ? mDisplayHeight : mDisplayWidth;
+                final int dh = rotated ? mDisplayWidth : mDisplayHeight;
+                policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
+                mStableInsets[rotation].set(mNonDecorInsets[rotation]);
+                policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
+            }
+        }
+
+        void getDisplayBounds(Rect outBounds, int rotation) {
+            final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+            final int dw = rotated ? mDisplayHeight : mDisplayWidth;
+            final int dh = rotated ? mDisplayWidth : mDisplayHeight;
+            outBounds.set(0, 0, dw, dh);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 6ae7720..1934e25 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2775,6 +2775,16 @@
     }
 
     /**
+     * Calculates the stable insets if we already have the non-decor insets.
+     *
+     * @param inOutInsets The known non-decor insets. It will be modified to stable insets.
+     * @param rotation The current display rotation.
+     */
+    void convertNonDecorInsetsToStableInsets(Rect inOutInsets, int rotation) {
+        inOutInsets.top = Math.max(inOutInsets.top, mStatusBarHeightForRotation[rotation]);
+    }
+
+    /**
      * Calculates the stable insets without running a layout.
      *
      * @param displayRotation the current display rotation
@@ -2789,7 +2799,7 @@
 
         // Navigation bar and status bar.
         getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
-        outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]);
+        convertNonDecorInsetsToStableInsets(outInsets, displayRotation);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 714c227..15060e1 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -22,6 +22,7 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -1688,6 +1689,8 @@
             int colorBackground = 0;
             int statusBarColor = 0;
             int navigationBarColor = 0;
+            boolean statusBarContrastWhenTransparent = false;
+            boolean navigationBarContrastWhenTransparent = false;
             boolean topActivity = true;
             for (--activityNdx; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = mActivities.get(activityNdx);
@@ -1711,12 +1714,17 @@
                         colorBackground = r.taskDescription.getBackgroundColor();
                         statusBarColor = r.taskDescription.getStatusBarColor();
                         navigationBarColor = r.taskDescription.getNavigationBarColor();
+                        statusBarContrastWhenTransparent =
+                                r.taskDescription.getEnsureStatusBarContrastWhenTransparent();
+                        navigationBarContrastWhenTransparent =
+                                r.taskDescription.getEnsureNavigationBarContrastWhenTransparent();
                     }
                 }
                 topActivity = false;
             }
             lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
-                    colorPrimary, colorBackground, statusBarColor, navigationBarColor);
+                    colorPrimary, colorBackground, statusBarColor, navigationBarColor,
+                    statusBarContrastWhenTransparent, navigationBarContrastWhenTransparent);
             if (mTask != null) {
                 mTask.setTaskDescription(lastTaskDescription);
             }
@@ -2041,15 +2049,12 @@
         }
         mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
 
-        policy.getStableInsetsLw(displayInfo.rotation,
-                displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout,
-                mTmpInsets);
-        intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
-
-        policy.getNonDecorInsetsLw(displayInfo.rotation,
-                displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout,
-                mTmpInsets);
+        policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+                displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
         intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
+
+        policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
+        intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
     }
 
     /**
@@ -2066,7 +2071,7 @@
 
     void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
             @NonNull Configuration parentConfig) {
-        computeConfigResourceOverrides(inOutConfig, parentConfig, true /* insideParentBounds */);
+        computeConfigResourceOverrides(inOutConfig, parentConfig, null /* compatInsets */);
     }
 
     /**
@@ -2078,7 +2083,8 @@
      * just be inherited from the parent configuration.
      **/
     void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
-            @NonNull Configuration parentConfig, boolean insideParentBounds) {
+            @NonNull Configuration parentConfig,
+            @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
         int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
             windowingMode = parentConfig.windowConfiguration.getWindowingMode();
@@ -2096,6 +2102,9 @@
             inOutConfig.windowConfiguration.setAppBounds(bounds);
             outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
         }
+        // Non-null compatibility insets means the activity prefers to keep its original size, so
+        // the out bounds doesn't need to be restricted by the parent.
+        final boolean insideParentBounds = compatInsets == null;
         if (insideParentBounds && windowingMode != WINDOWING_MODE_FREEFORM) {
             final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
             if (parentAppBounds != null && !parentAppBounds.isEmpty()) {
@@ -2118,6 +2127,17 @@
                 // Set to app bounds because it excludes decor insets.
                 mTmpNonDecorBounds.set(outAppBounds);
                 mTmpStableBounds.set(outAppBounds);
+
+                // Apply the given non-decor and stable insets to calculate the corresponding bounds
+                // for screen size of configuration.
+                final int rotation = parentConfig.windowConfiguration.getRotation();
+                if (rotation != ROTATION_UNDEFINED && compatInsets != null) {
+                    compatInsets.getDisplayBounds(mTmpBounds, rotation);
+                    intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
+                            compatInsets.mNonDecorInsets[rotation]);
+                    intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
+                            compatInsets.mStableInsets[rotation]);
+                }
             }
 
             if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6fe8b43..f31416c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -362,11 +362,9 @@
         }
         final int color = ColorUtils.setAlphaComponent(
                 task.getTaskDescription().getBackgroundColor(), 255);
-        final int statusBarColor = task.getTaskDescription().getStatusBarColor();
-        final int navigationBarColor = task.getTaskDescription().getNavigationBarColor();
         final LayoutParams attrs = mainWindow.getAttrs();
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
-                attrs.privateFlags, attrs.systemUiVisibility, statusBarColor, navigationBarColor);
+                attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription());
         final int width = mainWindow.getFrameLw().width();
         final int height = mainWindow.getFrameLw().height();
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 39b5662..5d99db5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -18,6 +18,8 @@
 
 import static android.graphics.Color.WHITE;
 import static android.graphics.Color.alpha;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
@@ -148,9 +150,8 @@
         final Rect tmpStableInsets = new Rect();
         final InsetsState mTmpInsetsState = new InsetsState();
         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
-        int backgroundColor = WHITE;
-        int statusBarColor = 0;
-        int navigationBarColor = 0;
+        final TaskDescription taskDescription = new TaskDescription();
+        taskDescription.setBackgroundColor(WHITE);
         final int sysUiVis;
         final int windowFlags;
         final int windowPrivateFlags;
@@ -194,11 +195,9 @@
             layoutParams.systemUiVisibility = sysUiVis;
             layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
 
-            final TaskDescription taskDescription = task.getTaskDescription();
-            if (taskDescription != null) {
-                backgroundColor = taskDescription.getBackgroundColor();
-                statusBarColor = taskDescription.getStatusBarColor();
-                navigationBarColor = taskDescription.getNavigationBarColor();
+            final TaskDescription td = task.getTaskDescription();
+            if (td != null) {
+                taskDescription.copyFrom(td);
             }
             taskBounds = new Rect();
             task.getBounds(taskBounds);
@@ -216,8 +215,8 @@
             // Local call.
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
-                surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
-                navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
+                surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
+                windowFlags, windowPrivateFlags, taskBounds,
                 currentOrientation);
         window.setOuter(snapshotSurface);
         try {
@@ -234,9 +233,9 @@
 
     @VisibleForTesting
     TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
-            TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
-            int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
-            Rect taskBounds, int currentOrientation) {
+            TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
+            int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
+            int currentOrientation) {
         mService = service;
         mSurface = new Surface();
         mHandler = new Handler(mService.mH.getLooper());
@@ -245,11 +244,12 @@
         mSurfaceControl = surfaceControl;
         mSnapshot = snapshot;
         mTitle = title;
+        int backgroundColor = taskDescription.getBackgroundColor();
         mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
         mTaskBounds = taskBounds;
         mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
-                windowPrivateFlags, sysUiVis, statusBarColor, navigationBarColor);
-        mStatusBarColor = statusBarColor;
+                windowPrivateFlags, sysUiVis, taskDescription);
+        mStatusBarColor = taskDescription.getStatusBarColor();
         mOrientationOnCreation = currentOrientation;
     }
 
@@ -490,7 +490,7 @@
         private final int mSysUiVis;
 
         SystemBarBackgroundPainter( int windowFlags, int windowPrivateFlags, int sysUiVis,
-                int statusBarColor, int navigationBarColor) {
+                TaskDescription taskDescription) {
             mWindowFlags = windowFlags;
             mWindowPrivateFlags = windowPrivateFlags;
             mSysUiVis = sysUiVis;
@@ -498,11 +498,17 @@
             final int semiTransparent = context.getColor(
                     R.color.system_bar_background_semi_transparent);
             mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
-                    semiTransparent, statusBarColor);
+                    semiTransparent, taskDescription.getStatusBarColor(), sysUiVis,
+                    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+                    taskDescription.getEnsureStatusBarContrastWhenTransparent());
             mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
-                    FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, navigationBarColor);
+                    FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
+                    taskDescription.getNavigationBarColor(), sysUiVis,
+                    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                    taskDescription.getEnsureNavigationBarContrastWhenTransparent()
+                            && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
             mStatusBarPaint.setColor(mStatusBarColor);
-            mNavigationBarPaint.setColor(navigationBarColor);
+            mNavigationBarPaint.setColor(mNavigationBarColor);
         }
 
         void setInsets(Rect contentInsets, Rect stableInsets) {
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
new file mode 100644
index 0000000..967750d
--- /dev/null
+++ b/services/core/xsd/vts/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.
+//
+
+cc_test {
+    name: "vts_defaultPermissions_validate_test",
+    srcs: [
+        "ValidateDefaultPermissions.cpp"
+    ],
+    static_libs: [
+        "android.hardware.audio.common.test.utility",
+        "libxml2",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/services/core/xsd/vts/Android.mk b/services/core/xsd/vts/Android.mk
new file mode 100644
index 0000000..6dc2c43
--- /dev/null
+++ b/services/core/xsd/vts/Android.mk
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsValidateDefaultPermissions
+include test/vts/tools/build/Android.host_config.mk
diff --git a/services/core/xsd/vts/AndroidTest.xml b/services/core/xsd/vts/AndroidTest.xml
new file mode 100644
index 0000000..4f3b2ef
--- /dev/null
+++ b/services/core/xsd/vts/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<configuration description="Config for VTS VtsValidateDefaultPermissions.">
+    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+        <option name="push" value="DATA/etc/default-permissions.xsd->/data/local/tmp/default-permissions.xsd"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+        <option name="test-module-name" value="VtsValidateDefaultPermissions"/>
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="test-timeout" value="30s"/>
+    </test>
+</configuration>
diff --git a/services/core/xsd/vts/ValidateDefaultPermissions.cpp b/services/core/xsd/vts/ValidateDefaultPermissions.cpp
new file mode 100644
index 0000000..54c115b
--- /dev/null
+++ b/services/core/xsd/vts/ValidateDefaultPermissions.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#include "utility/ValidateXml.h"
+
+TEST(CheckConfig, mediaDefaultPermissions) {
+    RecordProperty("description",
+                   "Verify that the default-permissions file "
+                   "is valid according to the schema");
+
+    const char* location = "/vendor/etc/default-permissions";
+
+    EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS("default-permissions.xml", {location},
+                                            "/data/local/tmp/default-permissions.xsd");
+}
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 7befd087..6b5842f 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -32,6 +32,7 @@
 import android.net.ip.IIpClientCallbacks;
 import android.net.util.SharedLog;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -148,14 +149,18 @@
     private class NetworkStackConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            log("Network stack service connected");
+            logi("Network stack service connected");
             registerNetworkStackService(service);
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            // TODO: crash/reboot the system ?
-            logWtf("Lost network stack connector", null);
+            // The system has lost its network stack (probably due to a crash in the
+            // network stack process): better crash rather than stay in a bad state where all
+            // networking is broken.
+            // onServiceDisconnected is not being called on device shutdown, so this method being
+            // called always indicates a bad state for the system server.
+            maybeCrashWithTerribleFailure("Lost network stack");
         }
     };
 
@@ -211,8 +216,7 @@
         }
 
         if (intent == null) {
-            logWtf("Could not resolve the network stack", null);
-            // TODO: crash/reboot system server ?
+            maybeCrashWithTerribleFailure("Could not resolve the network stack");
             return;
         }
 
@@ -220,9 +224,9 @@
         // NetworkStackConnection.onServiceConnected().
         if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
                 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
-            logWtf("Could not bind to network stack with " + intent, null);
+            maybeCrashWithTerribleFailure(
+                    "Could not bind to network stack in-process, or in app with " + intent);
             return;
-            // TODO: crash/reboot system server if no network stack after a timeout ?
         }
 
         log("Network stack service start requested");
@@ -270,6 +274,16 @@
         }
     }
 
+    private void maybeCrashWithTerribleFailure(@NonNull String message) {
+        logWtf(message, null);
+        if (Build.IS_DEBUGGABLE) {
+            throw new IllegalStateException(message);
+        }
+    }
+
+    /**
+     * Log a message in the local log.
+     */
     private void log(@NonNull String message) {
         synchronized (mLog) {
             mLog.log(message);
@@ -290,6 +304,15 @@
     }
 
     /**
+     * Log a message in the local and system logs.
+     */
+    private void logi(@NonNull String message) {
+        synchronized (mLog) {
+            mLog.i(message);
+        }
+    }
+
+    /**
      * For non-system server clients, get the connector registered by the system server.
      */
     private INetworkStackConnector getRemoteConnector() {
diff --git a/services/net/java/android/net/shared/NetworkMonitorUtils.java b/services/net/java/android/net/shared/NetworkMonitorUtils.java
index a17cb464..bb4a603 100644
--- a/services/net/java/android/net/shared/NetworkMonitorUtils.java
+++ b/services/net/java/android/net/shared/NetworkMonitorUtils.java
@@ -21,9 +21,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 
-import android.content.Context;
 import android.net.NetworkCapabilities;
-import android.provider.Settings;
 
 /** @hide */
 public class NetworkMonitorUtils {
@@ -45,16 +43,6 @@
             "android.permission.ACCESS_NETWORK_CONDITIONS";
 
     /**
-     * Get the captive portal server HTTP URL that is configured on the device.
-     */
-    public static String getCaptivePortalServerHttpUrl(Context context, String defaultUrl) {
-        final String settingUrl = Settings.Global.getString(
-                context.getContentResolver(),
-                Settings.Global.CAPTIVE_PORTAL_HTTP_URL);
-        return settingUrl != null ? settingUrl : defaultUrl;
-    }
-
-    /**
      * Return whether validation is required for a network.
      * @param dfltNetCap Default requested network capabilities.
      * @param nc Network capabilities of the network to test.
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 bafcd5f..355ff63 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -348,6 +348,7 @@
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
         when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
         when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
+        when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
 
         // write to a test file; the system file isn't readable from tests
         mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -4948,22 +4949,26 @@
         assertEquals(1, mService.getNotificationRecordCount());
     }
 
-    public void testGetAllowedAssistantCapabilities() throws Exception {
-        List<String> capabilities = mBinderService.getAllowedAssistantCapabilities(null);
+    @Test
+    public void testGetAllowedAssistantAdjustments() throws Exception {
+        List<String> capabilities = mBinderService.getAllowedAssistantAdjustments(null);
         assertNotNull(capabilities);
 
         for (int i = capabilities.size() - 1; i >= 0; i--) {
             String capability = capabilities.get(i);
-            mBinderService.disallowAssistantCapability(capability);
-            assertEquals(i + 1, mBinderService.getAllowedAssistantCapabilities(null).size());
-            List<String> currentCapabilities = mBinderService.getAllowedAssistantCapabilities(null);
+            mBinderService.disallowAssistantAdjustment(capability);
+            assertEquals(i + 1, mBinderService.getAllowedAssistantAdjustments(null).size());
+            List<String> currentCapabilities = mBinderService.getAllowedAssistantAdjustments(null);
             assertNotNull(currentCapabilities);
             assertFalse(currentCapabilities.contains(capability));
         }
     }
 
+    @Test
     public void testAdjustRestrictedKey() throws Exception {
         NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
 
         when(mAssistants.isAdjustmentAllowed(KEY_IMPORTANCE)).thenReturn(true);
         when(mAssistants.isAdjustmentAllowed(KEY_USER_SENTIMENT)).thenReturn(false);
@@ -4981,8 +4986,12 @@
         assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment());
     }
 
+    @Test
     public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Exception {
-        ComponentName owner = mock(ComponentName.class);
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+        mService.setZenHelper(mock(ZenModeHelper.class));
+        ComponentName owner = new ComponentName(mContext, this.getClass());
         ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
         boolean isEnabled = true;
         AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
@@ -4990,7 +4999,7 @@
 
         try {
             mBinderService.addAutomaticZenRule(rule);
-            fail("Zen policy only aplies to priority only mode");
+            fail("Zen policy only applies to priority only mode");
         } catch (IllegalArgumentException e) {
             // yay
         }
@@ -5004,6 +5013,7 @@
         mBinderService.addAutomaticZenRule(rule);
     }
 
+    @Test
     public void testAreNotificationsEnabledForPackage_crossUser() throws Exception {
         try {
             mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
@@ -5020,6 +5030,7 @@
                 mUid + UserHandle.PER_USER_RANGE);
     }
 
+    @Test
     public void testAreBubblesAllowedForPackage_crossUser() throws Exception {
         try {
             mBinderService.areBubblesAllowedForPackage(mContext.getPackageName(),
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 32e96a5..44390b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -57,6 +57,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.MergedConfiguration;
 import android.util.MutableBoolean;
+import android.view.DisplayInfo;
 
 import androidx.test.filters.MediumTest;
 
@@ -87,6 +88,10 @@
 
         doReturn(false).when(mService).isBooting();
         doReturn(true).when(mService).isBooted();
+
+        final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
+        doReturn(mock(DisplayPolicy.class)).when(displayContent).getDisplayPolicy();
+        doReturn(mock(DisplayInfo.class)).when(displayContent).getDisplayInfo();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index dc307b5..d87eed2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -25,7 +25,10 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.Surface.ROTATION_0;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.hamcrest.Matchers.not;
@@ -35,6 +38,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 
 import android.app.ActivityManager;
@@ -357,6 +362,7 @@
         parentConfig.densityDpi = 400;
         parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
         parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
+        parentConfig.windowConfiguration.setRotation(ROTATION_0);
 
         // Portrait bounds.
         inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide);
@@ -370,12 +376,27 @@
         inOutConfig.setToDefaults();
         // Landscape bounds.
         inOutConfig.windowConfiguration.getBounds().set(0, 0, longSide, shortSide);
+
+        // Setup the display with a top stable inset. The later assertion will ensure the inset is
+        // excluded from screenHeightDp.
+        final int statusBarHeight = 100;
+        final DisplayContent displayContent = mock(DisplayContent.class);
+        final DisplayPolicy policy = mock(DisplayPolicy.class);
+        doAnswer(invocationOnMock -> {
+            final Rect insets = invocationOnMock.<Rect>getArgument(0);
+            insets.top = statusBarHeight;
+            return null;
+        }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0));
+        doReturn(policy).when(displayContent).getDisplayPolicy();
+        doReturn(mock(DisplayInfo.class)).when(displayContent).getDisplayInfo();
+
         // Without limiting to be inside the parent bounds, the out screen size should keep relative
         // to the input bounds.
-        task.computeConfigResourceOverrides(inOutConfig, parentConfig,
-                false /* insideParentBounds */);
+        final ActivityRecord.CompatDisplayInsets compatIntsets =
+                new ActivityRecord.CompatDisplayInsets(displayContent);
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
 
-        assertEquals(shortSide * DENSITY_DEFAULT / parentConfig.densityDpi,
+        assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
                 inOutConfig.screenHeightDp);
         assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi,
                 inOutConfig.screenWidthDp);
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 ca815ec..cb6dc6d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 
+import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.ComponentName;
 import android.graphics.Canvas;
@@ -38,7 +39,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.view.Surface;
 import android.view.SurfaceControl;
 
 import androidx.test.filters.SmallTest;
@@ -67,8 +67,17 @@
                 ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
                 WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
         mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
-                Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
-                ORIENTATION_PORTRAIT);
+                createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
+                taskBounds, ORIENTATION_PORTRAIT);
+    }
+
+    private static TaskDescription createTaskDescription(int background, int statusBar,
+            int navigationBar) {
+        final TaskDescription td = new TaskDescription();
+        td.setBackgroundColor(background);
+        td.setStatusBarColor(statusBar);
+        td.setNavigationBarColor(navigationBar);
+        return td;
     }
 
     private void setupSurface(int width, int height) {
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index ee28ca2..cf15b92 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -87,8 +87,8 @@
     private int mCarrierId;
 
     /**
-     * The source of the name, NAME_SOURCE_UNDEFINED, NAME_SOURCE_DEFAULT_SOURCE,
-     * NAME_SOURCE_SIM_SOURCE or NAME_SOURCE_USER_INPUT.
+     * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or
+     * NAME_SOURCE_USER_INPUT.
      */
     private int mNameSource;
 
@@ -103,7 +103,7 @@
     private String mNumber;
 
     /**
-     * Data roaming state, DATA_RAOMING_ENABLE, DATA_RAOMING_DISABLE
+     * Data roaming state, DATA_ROAMING_ENABLE, DATA_ROAMING_DISABLE
      */
     private int mDataRoaming;
 
@@ -306,8 +306,8 @@
     }
 
     /**
-     * @return the source of the name, eg NAME_SOURCE_UNDEFINED, NAME_SOURCE_DEFAULT_SOURCE,
-     * NAME_SOURCE_SIM_SOURCE or NAME_SOURCE_USER_INPUT.
+     * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or
+     * NAME_SOURCE_USER_INPUT.
      * @hide
      */
     @UnsupportedAppUsage
@@ -316,8 +316,8 @@
     }
 
     /**
-     * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a user
-     * interface.
+     * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a
+     * user interface.
      *
      * @param context A {@code Context} to get the {@code DisplayMetrics}s from.
      *
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 57c84a6..0c63411 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -364,12 +364,6 @@
     public static final String NAME_SOURCE = "name_source";
 
     /**
-     * The name_source is undefined
-     * @hide
-     */
-    public static final int NAME_SOURCE_UNDEFINDED = -1;
-
-    /**
      * The name_source is the default
      * @hide
      */
@@ -1598,27 +1592,16 @@
     }
 
     /**
-     * Set display name by simInfo index
-     * @param displayName the display name of SIM card
-     * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
-     * @hide
-     */
-    public int setDisplayName(String displayName, int subId) {
-        return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED);
-    }
-
-    /**
      * Set display name by simInfo index with name source
      * @param displayName the display name of SIM card
      * @param subId the unique SubscriptionInfo index in database
      * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
-     *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
+     *                   2: NAME_SOURCE_USER_INPUT
      * @return the number of records updated or < 0 if invalid subId
      * @hide
      */
     @UnsupportedAppUsage
-    public int setDisplayName(String displayName, int subId, long nameSource) {
+    public int setDisplayName(String displayName, int subId, int nameSource) {
         if (VDBG) {
             logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
                     + " nameSource:" + nameSource);
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 01fdae8..cfba052 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -145,21 +145,13 @@
     int setIconTint(int tint, int subId);
 
     /**
-     * Set display name by simInfo index
-     * @param displayName the display name of SIM card
-     * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
-     */
-    int setDisplayName(String displayName, int subId);
-
-    /**
      * Set display name by simInfo index with name source
      * @param displayName the display name of SIM card
      * @param subId the unique SubscriptionInfo index in database
      * @param nameSource, 0: DEFAULT_SOURCE, 1: SIM_SOURCE, 2: USER_INPUT
      * @return the number of records updated
      */
-    int setDisplayNameUsingSrc(String displayName, int subId, long nameSource);
+    int setDisplayNameUsingSrc(String displayName, int subId, int nameSource);
 
     /**
      * Set phone number by subId