Merge "Update a couple of blobstore params to be configurable." into rvc-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index 63ec7c3..de4b24a 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -71,6 +71,11 @@
     // stubs libraries.
     libs: ["framework-annotations-lib"],
 
+    // Framework modules are not generally shared libraries, i.e. they are not
+    // intended, and must not be allowed, to be used in a <uses-library> manifest
+    // entry.
+    shared_library: false,
+
     // Enable api lint. This will eventually become the default for java_sdk_library
     // but it cannot yet be turned on because some usages have not been cleaned up.
     // TODO(b/156126315) - Remove when no longer needed.
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 3d0eeb8..8587e145 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -160,7 +160,7 @@
     static const long kPullerCacheClearIntervalSec = 1;
 
     // Max time to do a pull.
-    static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
+    static const int64_t kPullMaxDelayNs = 30 * NS_PER_SEC;
 
     // Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
     static const int kMaxNonPlatformPushedAtoms = 100;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index e86fdf0..673f668 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -82,7 +82,9 @@
     DIMENSION_GUARDRAIL_REACHED = 6,
     MULTIPLE_BUCKETS_SKIPPED = 7,
     // Not an invalid bucket case, but the bucket is dropped.
-    BUCKET_TOO_SMALL = 8
+    BUCKET_TOO_SMALL = 8,
+    // Not an invalid bucket case, but the bucket is skipped.
+    NO_DATA = 9
 };
 
 struct Activation {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index f03ce45..dbec24b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -108,7 +108,7 @@
       mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
       mUseZeroDefaultBase(metric.use_zero_default_base()),
       mHasGlobalBase(false),
-      mCurrentBucketIsInvalid(false),
+      mCurrentBucketIsSkipped(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()),
@@ -383,15 +383,12 @@
 
 void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs,
                                                                   const BucketDropReason reason) {
-    if (!mCurrentBucketIsInvalid) {
+    if (!mCurrentBucketIsSkipped) {
         // Only report to StatsdStats once per invalid bucket.
         StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
     }
 
-    if (!maxDropEventsReached()) {
-        mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason));
-    }
-    mCurrentBucketIsInvalid = true;
+    skipCurrentBucket(dropTimeNs, reason);
 }
 
 void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs,
@@ -400,6 +397,14 @@
     resetBase();
 }
 
+void ValueMetricProducer::skipCurrentBucket(const int64_t dropTimeNs,
+                                            const BucketDropReason reason) {
+    if (!maxDropEventsReached()) {
+        mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason));
+    }
+    mCurrentBucketIsSkipped = true;
+}
+
 void ValueMetricProducer::resetBase() {
     for (auto& slice : mCurrentBaseInfo) {
         for (auto& baseInfo : slice.second) {
@@ -961,12 +966,10 @@
     int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
     bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
     if (!isBucketLargeEnough) {
-        if (!maxDropEventsReached()) {
-            mCurrentSkippedBucket.dropEvents.emplace_back(
-                    buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
-        }
+        skipCurrentBucket(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL);
     }
-    if (isBucketLargeEnough && !mCurrentBucketIsInvalid) {
+    bool bucketHasData = false;
+    if (!mCurrentBucketIsSkipped) {
         // The current bucket is large enough to keep.
         for (const auto& slice : mCurrentSlicedBucket) {
             ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
@@ -975,14 +978,33 @@
             if (bucket.valueIndex.size() > 0) {
                 auto& bucketList = mPastBuckets[slice.first];
                 bucketList.push_back(bucket);
+                bucketHasData = true;
             }
         }
-    } else {
+    }
+
+    if (!bucketHasData && !mCurrentBucketIsSkipped) {
+        skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA);
+    }
+
+    if (mCurrentBucketIsSkipped) {
         mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
-        mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
+        // Fill in the gap if we skipped multiple buckets.
+        mCurrentSkippedBucket.bucketEndTimeNs =
+                numBucketsForward > 1 ? nextBucketStartTimeNs : bucketEndTime;
         mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
     }
 
+    // This means that the current bucket was not flushed before a forced bucket split.
+    if (bucketEndTime < nextBucketStartTimeNs && numBucketsForward <= 1) {
+        SkippedBucket bucketInGap;
+        bucketInGap.bucketStartTimeNs = bucketEndTime;
+        bucketInGap.bucketEndTimeNs = nextBucketStartTimeNs;
+        bucketInGap.dropEvents.emplace_back(
+                buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
+        mSkippedBuckets.emplace_back(bucketInGap);
+    }
+
     appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
     initCurrentSlicedBucket(nextBucketStartTimeNs);
     // Update the condition timer again, in case we skipped buckets.
@@ -1036,13 +1058,13 @@
         // TODO: remove mCurrentBaseInfo entries when obsolete
     }
 
-    mCurrentBucketIsInvalid = false;
+    mCurrentBucketIsSkipped = false;
     mCurrentSkippedBucket.reset();
 
     // If we do not have a global base when the condition is true,
     // we will have incomplete bucket for the next bucket.
     if (mUseDiff && !mHasGlobalBase && mCondition) {
-        mCurrentBucketIsInvalid = false;
+        mCurrentBucketIsSkipped = false;
     }
     mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
     VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
@@ -1051,7 +1073,7 @@
 
 void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
     bool isFullBucketReached = eventTimeNs > fullBucketEndTimeNs;
-    if (mCurrentBucketIsInvalid) {
+    if (mCurrentBucketIsSkipped) {
         if (isFullBucketReached) {
             // If the bucket is invalid, we ignore the full bucket since it contains invalid data.
             mCurrentFullBucket.clear();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 751fef2..bb4a661 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -144,6 +144,10 @@
     void invalidateCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
     void invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs,
                                                  const BucketDropReason reason);
+    // Skips the current bucket without notifying StatsdStats of the skipped bucket.
+    // This should only be called from #flushCurrentBucketLocked. Otherwise, a future event that
+    // causes the bucket to be invalidated will not notify StatsdStats.
+    void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
 
     const int mWhatMatcherIndex;
 
@@ -250,11 +254,9 @@
     // diff against.
     bool mHasGlobalBase;
 
-    // Invalid bucket. There was a problem in collecting data in the current bucket so we cannot
-    // trust any of the data in this bucket.
-    //
-    // For instance, one pull failed.
-    bool mCurrentBucketIsInvalid;
+    // This is to track whether or not the bucket is skipped for any of the reasons listed in
+    // BucketDropReason, many of which make the bucket potentially invalid.
+    bool mCurrentBucketIsSkipped;
 
     const int64_t mMaxPullDelayNs;
 
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 1121392..6bfa267 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -212,6 +212,8 @@
       MULTIPLE_BUCKETS_SKIPPED = 7;
       // Not an invalid bucket case, but the bucket is dropped.
       BUCKET_TOO_SMALL = 8;
+      // Not an invalid bucket case, but the bucket is skipped.
+      NO_DATA = 9;
   };
 
   message DropEvent {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 2e6043d..72decf2 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -278,7 +278,7 @@
 
   optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
 
-  optional int32 max_pull_delay_sec = 13 [default = 10];
+  optional int32 max_pull_delay_sec = 13 [default = 30];
 
   optional bool split_bucket_for_app_upgrade = 14 [default = true];
 }
@@ -328,7 +328,7 @@
 
   optional bool skip_zero_diff_output = 14 [default = true];
 
-  optional int32 max_pull_delay_sec = 16 [default = 10];
+  optional int32 max_pull_delay_sec = 16 [default = 30];
 
   optional bool split_bucket_for_app_upgrade = 17 [default = true];
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 474aa22..14246cab 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1115,13 +1115,21 @@
     EXPECT_EQ(false, curInterval.hasValue);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
                                     {bucket2StartTimeNs}, {bucket3StartTimeNs});
+    // The 1st bucket is dropped because of no data
     // The 3rd bucket is dropped due to multiple buckets being skipped.
-    ASSERT_EQ(1, valueProducer->mSkippedBuckets.size());
-    EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
-    EXPECT_EQ(bucket4StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
+    ASSERT_EQ(2, valueProducer->mSkippedBuckets.size());
+
+    EXPECT_EQ(bucketStartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
+    EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
     ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size());
-    EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
-    EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs);
+    EXPECT_EQ(NO_DATA, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
+    EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs);
+
+    EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[1].bucketStartTimeNs);
+    EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].bucketEndTimeNs);
+    ASSERT_EQ(1, valueProducer->mSkippedBuckets[1].dropEvents.size());
+    EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[1].dropEvents[0].reason);
+    EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].dropEvents[0].dropTimeNs);
 }
 
 /*
@@ -2214,7 +2222,7 @@
     valueProducer->mCondition = ConditionState::kFalse;
 
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
-    EXPECT_EQ(true, valueProducer->mCurrentBucketIsInvalid);
+    EXPECT_EQ(true, valueProducer->mCurrentBucketIsSkipped);
     ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
     ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size());
 
@@ -2629,13 +2637,17 @@
 
     vector<shared_ptr<LogEvent>> allData;
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4));
+    // Pull fails and arrives late.
     valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
                                     {partialBucketSplitTimeNs - bucketStartTimeNs},
                                     {bucketStartTimeNs}, {partialBucketSplitTimeNs});
     ASSERT_EQ(1, valueProducer->mSkippedBuckets.size());
+    ASSERT_EQ(2, valueProducer->mSkippedBuckets[0].dropEvents.size());
+    EXPECT_EQ(PULL_FAILED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
+    EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[1].reason);
     EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
-    EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
+    EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
     ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
 }
 
@@ -3464,26 +3476,41 @@
     // Condition change event that skips forward by three buckets.
     valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10);
 
+    int64_t dumpTimeNs = bucket4StartTimeNs + 1000;
+
     // Check dump report.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */, true,
+    valueProducer->onDumpReport(dumpTimeNs, true /* include current buckets */, true,
                                 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
     EXPECT_TRUE(report.has_value_metrics());
     ASSERT_EQ(0, report.value_metrics().data_size());
-    ASSERT_EQ(1, report.value_metrics().skipped_size());
+    ASSERT_EQ(2, report.value_metrics().skipped_size());
 
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+    EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
     ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
     auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
     EXPECT_EQ(BucketDropReason::MULTIPLE_BUCKETS_SKIPPED, dropEvent.drop_reason());
     EXPECT_EQ(NanoToMillis(bucket4StartTimeNs + 10), dropEvent.drop_time_millis());
+
+    // This bucket is skipped because a dumpReport with include current buckets is called.
+    // This creates a new bucket from bucket4StartTimeNs to dumpTimeNs in which we have no data
+    // since the condition is false for the entire bucket interval.
+    EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
+              report.value_metrics().skipped(1).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(dumpTimeNs),
+              report.value_metrics().skipped(1).end_bucket_elapsed_millis());
+    ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
+
+    dropEvent = report.value_metrics().skipped(1).drop_event(0);
+    EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(dumpTimeNs), dropEvent.drop_time_millis());
 }
 
 /*
@@ -3544,6 +3571,89 @@
 }
 
 /*
+ * Test that NO_DATA dump reason is logged when a flushed bucket contains no data.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+    // Check dump report.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
+    valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
+                                NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(0, report.value_metrics().data_size());
+    ASSERT_EQ(1, report.value_metrics().skipped_size());
+
+    EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+              report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
+              report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+    ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+    auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+    EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket
+ * was not flushed in time.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBeforeBucketFlush) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+    // App update event.
+    int64_t appUpdateTimeNs = bucket2StartTimeNs + 1000;
+    valueProducer->notifyAppUpgrade(appUpdateTimeNs);
+
+    // Check dump report.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000; // 10 seconds
+    valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
+                                NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(0, report.value_metrics().data_size());
+    ASSERT_EQ(2, report.value_metrics().skipped_size());
+
+    EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+              report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+              report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+    ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+    auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+    EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
+
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+              report.value_metrics().skipped(1).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
+              report.value_metrics().skipped(1).end_bucket_elapsed_millis());
+    ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
+
+    dropEvent = report.value_metrics().skipped(1).drop_event(0);
+    EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
  * Test multiple bucket drop events in the same bucket.
  */
 TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d086459..0d8618f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5968,6 +5968,206 @@
             }
         }
 
+        /**
+         * Merges the signing lineage of this instance with the lineage in the provided {@code
+         * otherSigningDetails} when one has the same or an ancestor signer of the other.
+         *
+         * <p>Merging two signing lineages will result in a new {@code SigningDetails} instance
+         * containing the longest common lineage with the most restrictive capabilities. If the two
+         * lineages contain the same signers with the same capabilities then the instance on which
+         * this was invoked is returned without any changes. Similarly if neither instance has a
+         * lineage, or if neither has the same or an ancestor signer then this instance is returned.
+         *
+         * Following are some example results of this method for lineages with signers A, B, C, D:
+         * - lineage B merged with lineage A -> B returns lineage A -> B.
+         * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
+         * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
+         *  lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns
+         *  lineage A -> B with both capabilities revoked for A.
+         * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
+         *  A -> B -> C since the current signer of both instances is not the same or in the
+         *   lineage of the other.
+         */
+        public SigningDetails mergeLineageWith(SigningDetails otherSigningDetails) {
+            if (!hasPastSigningCertificates()) {
+                return otherSigningDetails.hasPastSigningCertificates()
+                        && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
+            }
+            if (!otherSigningDetails.hasPastSigningCertificates()) {
+                return this;
+            }
+            // Use the utility method to determine which SigningDetails instance is the descendant
+            // and to confirm that the signing lineage does not diverge.
+            SigningDetails descendantSigningDetails = getDescendantOrSelf(otherSigningDetails);
+            if (descendantSigningDetails == null) {
+                return this;
+            }
+            return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
+                    otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
+        }
+
+        /**
+         * Merges the signing lineage of this instance with the lineage of the ancestor (or same)
+         * signer in the provided {@code otherSigningDetails}.
+         */
+        private SigningDetails mergeLineageWithAncestorOrSelf(SigningDetails otherSigningDetails) {
+            // This method should only be called with instances that contain lineages.
+            int index = pastSigningCertificates.length - 1;
+            int otherIndex = otherSigningDetails.pastSigningCertificates.length - 1;
+            if (index < 0 || otherIndex < 0) {
+                return this;
+            }
+
+            List<Signature> mergedSignatures = new ArrayList<>();
+            boolean capabilitiesModified = false;
+            // If this is a descendant lineage then add all of the descendant signer(s) to the
+            // merged lineage until the ancestor signer is reached.
+            while (index >= 0 && !pastSigningCertificates[index].equals(
+                    otherSigningDetails.pastSigningCertificates[otherIndex])) {
+                mergedSignatures.add(new Signature(pastSigningCertificates[index--]));
+            }
+            // If the signing lineage was exhausted then the provided ancestor is not actually an
+            // ancestor of this lineage.
+            if (index < 0) {
+                return this;
+            }
+
+            do {
+                // Add the common signer to the merged lineage with the most restrictive
+                // capabilities of the two lineages.
+                Signature signature = pastSigningCertificates[index--];
+                Signature ancestorSignature =
+                        otherSigningDetails.pastSigningCertificates[otherIndex--];
+                Signature mergedSignature = new Signature(signature);
+                int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
+                if (signature.getFlags() != mergedCapabilities) {
+                    capabilitiesModified = true;
+                    mergedSignature.setFlags(mergedCapabilities);
+                }
+                mergedSignatures.add(mergedSignature);
+            } while (index >= 0 && otherIndex >= 0 && pastSigningCertificates[index].equals(
+                    otherSigningDetails.pastSigningCertificates[otherIndex]));
+
+            // If both lineages still have elements then their lineages have diverged; since this is
+            // not supported return the invoking instance.
+            if (index >= 0 && otherIndex >= 0) {
+                return this;
+            }
+
+            // Add any remaining elements from either lineage that is not yet exhausted to the
+            // the merged lineage.
+            while (otherIndex >= 0) {
+                mergedSignatures.add(new Signature(
+                        otherSigningDetails.pastSigningCertificates[otherIndex--]));
+            }
+            while (index >= 0) {
+                mergedSignatures.add(new Signature(pastSigningCertificates[index--]));
+            }
+
+            // if this lineage already contains all the elements in the ancestor and none of the
+            // capabilities were changed then just return this instance.
+            if (mergedSignatures.size() == pastSigningCertificates.length
+                    && !capabilitiesModified) {
+                return this;
+            }
+            // Since the signatures were added to the merged lineage from newest to oldest reverse
+            // the list to ensure the oldest signer is at index 0.
+            Collections.reverse(mergedSignatures);
+            try {
+                return new SigningDetails(new Signature[]{new Signature(signatures[0])},
+                        signatureSchemeVersion, mergedSignatures.toArray(new Signature[0]));
+            } catch (CertificateException e) {
+                Slog.e(TAG, "Caught an exception creating the merged lineage: ", e);
+                return this;
+            }
+        }
+
+        /**
+         * Returns whether this and the provided {@code otherSigningDetails} share a common
+         * ancestor.
+         *
+         * <p>The two SigningDetails have a common ancestor if any of the following conditions are
+         * met:
+         * - If neither has a lineage and their current signer(s) are equal.
+         * - If only one has a lineage and the signer of the other is the same or in the lineage.
+         * - If both have a lineage and their current signers are the same or one is in the lineage
+         * of the other, and their lineages do not diverge to different signers.
+         */
+        public boolean hasCommonAncestor(SigningDetails otherSigningDetails) {
+            if (!hasPastSigningCertificates()) {
+                // If this instance does not have a lineage then it must either be in the ancestry
+                // of or the same signer of the otherSigningDetails.
+                return otherSigningDetails.hasAncestorOrSelf(this);
+            }
+            if (!otherSigningDetails.hasPastSigningCertificates()) {
+                return hasAncestorOrSelf(otherSigningDetails);
+            }
+            // If both have a lineage then use getDescendantOrSelf to obtain the descendant signing
+            // details; a null return from that method indicates there is no common lineage between
+            // the two or that they diverge at a point in the lineage.
+            return getDescendantOrSelf(otherSigningDetails) != null;
+        }
+
+        /**
+         * Returns the SigningDetails with a descendant (or same) signer after verifying the
+         * descendant has the same, a superset, or a subset of the lineage of the ancestor.
+         *
+         * <p>If this instance and the provided {@code otherSigningDetails} do not share an
+         * ancestry, or if their lineages diverge then null is returned to indicate there is no
+         * valid descendant SigningDetails.
+         */
+        private SigningDetails getDescendantOrSelf(SigningDetails otherSigningDetails) {
+            SigningDetails descendantSigningDetails;
+            SigningDetails ancestorSigningDetails;
+            if (hasAncestorOrSelf(otherSigningDetails)) {
+                // If the otherSigningDetails has the same signer or a signer in the lineage of this
+                // instance then treat this instance as the descendant.
+                descendantSigningDetails = this;
+                ancestorSigningDetails = otherSigningDetails;
+            } else if (otherSigningDetails.hasAncestor(this)) {
+                // The above check confirmed that the two instances do not have the same signer and
+                // the signer of otherSigningDetails is not in this instance's lineage; if this
+                // signer is in the otherSigningDetails lineage then treat this as the ancestor.
+                descendantSigningDetails = otherSigningDetails;
+                ancestorSigningDetails = this;
+            } else {
+                // The signers are not the same and neither has the current signer of the other in
+                // its lineage; return null to indicate there is no descendant signer.
+                return null;
+            }
+            // Once the descent (or same) signer is identified iterate through the ancestry until
+            // the current signer of the ancestor is found.
+            int descendantIndex = descendantSigningDetails.pastSigningCertificates.length - 1;
+            int ancestorIndex = ancestorSigningDetails.pastSigningCertificates.length - 1;
+            while (descendantIndex >= 0
+                    && !descendantSigningDetails.pastSigningCertificates[descendantIndex].equals(
+                    ancestorSigningDetails.pastSigningCertificates[ancestorIndex])) {
+                descendantIndex--;
+            }
+            // Since the ancestry was verified above the descendant lineage should never be
+            // exhausted, but if for some reason the ancestor signer is not found then return null.
+            if (descendantIndex < 0) {
+                return null;
+            }
+            // Once the common ancestor (or same) signer is found iterate over the lineage of both
+            // to ensure that they are either the same or one is a subset of the other.
+            do {
+                descendantIndex--;
+                ancestorIndex--;
+            } while (descendantIndex >= 0 && ancestorIndex >= 0
+                    && descendantSigningDetails.pastSigningCertificates[descendantIndex].equals(
+                    ancestorSigningDetails.pastSigningCertificates[ancestorIndex]));
+
+            // If both lineages still have elements then they diverge and cannot be considered a
+            // valid common lineage.
+            if (descendantIndex >= 0 && ancestorIndex >= 0) {
+                return null;
+            }
+            // Since one or both of the lineages was exhausted they are either the same or one is a
+            // subset of the other; return the valid descendant.
+            return descendantSigningDetails;
+        }
+
         /** Returns true if the signing details have one or more signatures. */
         public boolean hasSignatures() {
             return signatures != null && signatures.length > 0;
@@ -6284,7 +6484,13 @@
             if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
                 return false;
             }
-
+            // The capabilities for the past signing certs must match as well.
+            for (int i = 0; i < pastSigningCertificates.length; i++) {
+                if (pastSigningCertificates[i].getFlags()
+                        != that.pastSigningCertificates[i].getFlags()) {
+                    return false;
+                }
+            }
             return true;
         }
 
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 3b84ae7..b2875e9 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -123,6 +123,21 @@
     }
 
     /**
+     * Copy constructor that creates a new instance from the provided {@code other} Signature.
+     *
+     * @hide
+     */
+    public Signature(Signature other) {
+        mSignature = other.mSignature.clone();
+        Certificate[] otherCertificateChain = other.mCertificateChain;
+        if (otherCertificateChain != null && otherCertificateChain.length > 1) {
+            mCertificateChain = Arrays.copyOfRange(otherCertificateChain, 1,
+                    otherCertificateChain.length);
+        }
+        mFlags = other.mFlags;
+    }
+
+    /**
      * Sets the flags representing the capabilities of the past signing certificate.
      * @hide
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ebf4cc5..bf105ce 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2216,6 +2216,20 @@
                 }
             };
 
+    // Uses IS_USER_UNLOCKED_PROPERTY for invalidation as the APIs have the same dependencies.
+    private final PropertyInvalidatedCache<Integer, Boolean> mIsUserUnlockingOrUnlockedCache =
+            new PropertyInvalidatedCache<Integer, Boolean>(
+                32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
+                @Override
+                protected Boolean recompute(Integer query) {
+                    try {
+                        return mService.isUserUnlockingOrUnlocked(query);
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+            };
+
     /** {@hide} */
     @UnsupportedAppUsage
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
@@ -2227,6 +2241,7 @@
     /** {@hide} */
     public void disableIsUserUnlockedCache() {
         mIsUserUnlockedCache.disableLocal();
+        mIsUserUnlockingOrUnlockedCache.disableLocal();
     }
 
     /** {@hide} */
@@ -2263,11 +2278,7 @@
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
-        try {
-            return mService.isUserUnlockingOrUnlocked(userId);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+        return mIsUserUnlockingOrUnlockedCache.query(userId);
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e0bc764..1b19e12 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14363,6 +14363,21 @@
          * @hide
          */
         public static final String ADVANCED_BATTERY_USAGE_AMOUNT = "advanced_battery_usage_amount";
+
+        /**
+         * For 5G NSA capable devices, determines whether NR tracking indications are on
+         * when the screen is off.
+         *
+         * Values are:
+         * 0: off - All 5G NSA tracking indications are off when the screen is off.
+         * 1: extended - All 5G NSA tracking indications are on when the screen is off as long as
+         *    the device is camped on 5G NSA (5G icon is showing in status bar).
+         *    If the device is not camped on 5G NSA, tracking indications are off.
+         * 2: always on - All 5G NSA tracking indications are on whether the screen is on or off.
+         * @hide
+         */
+        public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE =
+                "nr_nsa_tracking_screen_off_mode";
     }
 
     /**
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index d20ffb3..9b5b882 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -136,6 +136,7 @@
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
                 .setParent(mRootSurface)
                 .setFormat(attrs.format)
+                .setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
                 .setName(attrs.getTitle().toString());
         final SurfaceControl sc = b.build();
 
@@ -242,13 +243,8 @@
         WindowManager.LayoutParams attrs = state.mParams;
 
         if (viewFlags == View.VISIBLE) {
-            final Rect surfaceInsets = attrs.surfaceInsets;
-            int width = surfaceInsets != null
-                    ? attrs.width + surfaceInsets.left + surfaceInsets.right : attrs.width;
-            int height = surfaceInsets != null
-                    ? attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
-
-            t.setBufferSize(sc, width, height).setOpaque(sc, isOpaque(attrs)).show(sc).apply();
+            t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs))
+                    .setOpaque(sc, isOpaque(attrs)).show(sc).apply();
             outSurfaceControl.copyFrom(sc);
         } else {
             t.hide(sc).apply();
@@ -444,4 +440,15 @@
     public android.os.IBinder asBinder() {
         return null;
     }
+
+    private int getSurfaceWidth(WindowManager.LayoutParams attrs) {
+      final Rect surfaceInsets = attrs.surfaceInsets;
+      return surfaceInsets != null
+          ? attrs.width + surfaceInsets.left + surfaceInsets.right : attrs.width;
+    }
+    private int getSurfaceHeight(WindowManager.LayoutParams attrs) {
+      final Rect surfaceInsets = attrs.surfaceInsets;
+      return surfaceInsets != null
+          ? attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
+    }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 389e33ae..8b0dd8a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -101,6 +101,7 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.Button;
@@ -2147,8 +2148,7 @@
             extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
 
             ChooserTarget chooserTarget = new ChooserTarget(
-                    shortcutInfo.getLongLabel() != null ? shortcutInfo.getLongLabel()
-                            : shortcutInfo.getShortLabel(),
+                    shortcutInfo.getLabel(),
                     null, // Icon will be loaded later if this target is selected to be shown.
                     score, matchingShortcuts.get(i).getTargetComponent().clone(), extras);
 
@@ -2778,6 +2778,7 @@
     @Override
     public void onListRebuilt(ResolverListAdapter listAdapter) {
         setupScrollListener();
+        maybeSetupGlobalLayoutListener();
 
         ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter;
         if (chooserListAdapter.getUserHandle()
@@ -2859,6 +2860,28 @@
                 });
     }
 
+    private void maybeSetupGlobalLayoutListener() {
+        if (shouldShowTabs()) {
+            return;
+        }
+        final View recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView();
+        recyclerView.getViewTreeObserver()
+                .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        // Fixes an issue were the accessibility border disappears on list creation.
+                        recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                        final TextView titleView = findViewById(R.id.title);
+                        if (titleView != null) {
+                            titleView.setFocusable(true);
+                            titleView.setFocusableInTouchMode(true);
+                            titleView.requestFocus();
+                            titleView.requestAccessibilityFocus();
+                        }
+                    }
+                });
+    }
+
     @Override // ChooserListCommunicator
     public boolean isSendAction(Intent targetIntent) {
         if (targetIntent == null) {
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 762895b..023197b 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -695,6 +695,8 @@
     }
     optional Notification notification = 82;
 
+    optional SettingProto nr_nsa_tracking_screen_off_mode = 153 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     optional SettingProto nsd_on = 83 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message Ntp {
@@ -1060,5 +1062,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 153;
+    // Next tag = 154;
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a205fcf..2dc3996 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4451,4 +4451,11 @@
     <bool name="config_pdp_reject_enable_retry">false</bool>
     <!-- pdp data reject retry delay in ms -->
     <integer name="config_pdp_reject_retry_delay_ms">-1</integer>
+
+    <!-- Package name that is recognized as an actor for the packages listed in
+         @array/config_overlayableConfiguratorTargets. If an overlay targeting one of the listed
+         targets is signed with the same signature as the configurator, the overlay will be granted
+         the "actor" policy. -->
+    <string name="config_overlayableConfigurator" translatable="false" />
+    <string-array name="config_overlayableConfiguratorTargets" translatable="false" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 758a4f7..6ce25d4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4025,4 +4025,6 @@
   <java-symbol type="string" name="config_pdp_reject_service_not_subscribed" />
   <java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" />
 
+  <java-symbol type="string" name="config_overlayableConfigurator" />
+  <java-symbol type="array" name="config_overlayableConfiguratorTargets" />
 </resources>
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index 51af048..24f45a5 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -15,13 +15,19 @@
  */
 package android.content.pm;
 
+import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.AUTH;
+import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA;
+import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.PERMISSION;
+import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.ROLLBACK;
+import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID;
 import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.pm.PackageParser.SigningDetails;
-import android.util.ArraySet;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -29,14 +35,70 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.security.PublicKey;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SigningDetailsTest {
-    private static final String FIRST_SIGNATURE = "1234";
-    private static final String SECOND_SIGNATURE = "5678";
-    private static final String THIRD_SIGNATURE = "9abc";
+    private static final int DEFAULT_CAPABILITIES =
+            INSTALLED_DATA | SHARED_USER_ID | PERMISSION | AUTH;
+
+    // Some of the tests in this class require valid certificate encodings from which to pull the
+    // public key for the SigningDetails; the following are all DER encoded EC X.509 certificates.
+    private static final String FIRST_SIGNATURE =
+            "3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a86"
+                    + "48ce3d04030230123110300e06035504030c0765632d70323536301e170d"
+                    + "3136303333313134353830365a170d3433303831373134353830365a3012"
+                    + "3110300e06035504030c0765632d703235363059301306072a8648ce3d02"
+                    + "0106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2b"
+                    + "a0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991e"
+                    + "f0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e"
+                    + "04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d"
+                    + "23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c06"
+                    + "03551d13040530030101ff300a06082a8648ce3d04030203490030460221"
+                    + "00f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16"
+                    + "db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0f"
+                    + "dbb8042cb655aadd";
+    private static final String SECOND_SIGNATURE =
+            "3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a86"
+                    + "48ce3d04030230123110300e06035504030c0765632d70323536301e170d"
+                    + "3138303731333137343135315a170d3238303731303137343135315a3014"
+                    + "3112301006035504030c0965632d703235365f323059301306072a8648ce"
+                    + "3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da9"
+                    + "3d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc"
+                    + "271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d060355"
+                    + "1d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603"
+                    + "551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc830"
+                    + "0c0603551d13040530030101ff300a06082a8648ce3d0403020348003045"
+                    + "02202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb"
+                    + "99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda1"
+                    + "00a6fe1a2ab19ff09e";
+    private static final String THIRD_SIGNATURE =
+            "3082016e30820115a0030201020209008394f5cad16a89a7300a06082a86"
+                    + "48ce3d04030230143112301006035504030c0965632d703235365f32301e"
+                    + "170d3138303731343030303532365a170d3238303731313030303532365a"
+                    + "30143112301006035504030c0965632d703235365f333059301306072a86"
+                    + "48ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d9"
+                    + "75fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3"
+                    + "fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d06"
+                    + "03551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f"
+                    + "0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e"
+                    + "5f300c0603551d13040530030101ff300a06082a8648ce3d040302034700"
+                    + "30440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820"
+                    + "309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad348"
+                    + "3f3fa04d5677355a579e";
+    private static final String FOURTH_SIGNATURE =
+            "3082017b30820120a00302010202146c8cb8a818433c1e6431fb16fb3ae0"
+                    + "fb5ad60aa7300a06082a8648ce3d04030230143112301006035504030c09"
+                    + "65632d703235365f33301e170d3230303531333139313532385a170d3330"
+                    + "303531313139313532385a30143112301006035504030c0965632d703235"
+                    + "365f343059301306072a8648ce3d020106082a8648ce3d03010703420004"
+                    + "db4a60031e79ad49cb759007d6855d4469b91c8bab065434f2fba971ade7"
+                    + "e4d19599a0f67b5e708cfda7543e5630c3769d37e093640d7c768a15144c"
+                    + "d0e5dcf4a350304e301d0603551d0e041604146e78970332554336b6ee89"
+                    + "24eaa70230e393f678301f0603551d230418301680146f8d0828b13efaf5"
+                    + "77fc86b0e99fa3e54bcbcff0300c0603551d13040530030101ff300a0608"
+                    + "2a8648ce3d0403020349003046022100ce786e79ec7547446082e9caf910"
+                    + "614ff80758f9819fb0f148695067abe0fcd4022100a4881e332ddec2116a"
+                    + "d2b59cf891d0f331ff7e27e77b7c6206c7988d9b539330";
 
     @Test
     public void hasAncestor_multipleSignersInLineageWithAncestor_returnsTrue() throws Exception {
@@ -121,27 +183,456 @@
         assertFalse(result2);
     }
 
-    private SigningDetails createSigningDetailsWithLineage(String... signers) {
+    @Test
+    public void mergeLineageWith_neitherHasLineage_returnsOriginal() throws Exception {
+        // When attempting to merge two instances of SigningDetails that do not have a lineage the
+        // initial object should be returned to indicate no changes were made.
+        SigningDetails noLineageDetails = createSigningDetails(FIRST_SIGNATURE);
+        SigningDetails otherNoLineageDetails = createSigningDetails(FIRST_SIGNATURE);
+
+        SigningDetails result1 = noLineageDetails.mergeLineageWith(otherNoLineageDetails);
+        SigningDetails result2 = otherNoLineageDetails.mergeLineageWith(noLineageDetails);
+
+        assertTrue(result1 == noLineageDetails);
+        assertTrue(result2 == otherNoLineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_oneHasNoLineage_returnsOther() throws Exception {
+        // When attempting to merge a SigningDetails with no lineage with another that has a
+        // lineage and is a descendant the descendant SigningDetails with lineage should be returned
+        SigningDetails noLineageDetails = createSigningDetails(FIRST_SIGNATURE);
+        SigningDetails lineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+
+        SigningDetails result1 = noLineageDetails.mergeLineageWith(lineageDetails);
+        SigningDetails result2 = lineageDetails.mergeLineageWith(noLineageDetails);
+
+        assertTrue(result1 == lineageDetails);
+        assertTrue(result2 == lineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_bothHaveSameLineage_returnsOriginal() throws Exception {
+        // If twoSigningDetails instances have the exact same lineage with the same capabilities
+        // then the original instance should be returned without modification.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+
+        SigningDetails result1 = firstLineageDetails.mergeLineageWith(secondLineageDetails);
+        SigningDetails result2 = secondLineageDetails.mergeLineageWith(firstLineageDetails);
+
+        assertTrue(result1 == firstLineageDetails);
+        assertTrue(result2 == secondLineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_oneIsAncestorWithoutLineage_returnsDescendant() throws Exception {
+        // If one instance without a lineage is an ancestor of the other then the descendant should
+        // be returned.
+        SigningDetails ancestorDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE);
+        SigningDetails descendantDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+
+        SigningDetails result1 = ancestorDetails.mergeLineageWith(descendantDetails);
+        SigningDetails result2 = descendantDetails.mergeLineageWith(ancestorDetails);
+
+        assertEquals(descendantDetails, result1);
+        assertTrue(result2 == descendantDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_oneIsAncestorWithLineage_returnsDescendant() throws Exception {
+        // Similar to the above test if one instance with a lineage is an ancestor of the other then
+        // the descendant should be returned.
+        SigningDetails ancestorDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails descendantDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        SigningDetails result1 = ancestorDetails.mergeLineageWith(descendantDetails);
+        SigningDetails result2 = descendantDetails.mergeLineageWith(ancestorDetails);
+
+        assertEquals(descendantDetails, result1);
+        assertTrue(result2 == descendantDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_singleSignerInMiddleOfLineage_returnsFullLineage()
+            throws Exception {
+        // If one instance without a lineage is an ancestor in the middle of the lineage for the
+        // descendant the descendant should be returned.
+        SigningDetails singleSignerDetails = createSigningDetails(SECOND_SIGNATURE);
+        SigningDetails fullLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        SigningDetails result1 = singleSignerDetails.mergeLineageWith(fullLineageDetails);
+        SigningDetails result2 = fullLineageDetails.mergeLineageWith(singleSignerDetails);
+
+        assertTrue(result1 == fullLineageDetails);
+        assertTrue(result2 == fullLineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_noCommonLineage_returnsOriginal() throws Exception {
+        // While a call should never be made to merge two lineages without a common ancestor if it
+        // is attempted the original lineage should be returned.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(THIRD_SIGNATURE,
+                FOURTH_SIGNATURE);
+
+        SigningDetails result1 = firstLineageDetails.mergeLineageWith(secondLineageDetails);
+        SigningDetails result2 = secondLineageDetails.mergeLineageWith(firstLineageDetails);
+
+        assertTrue(result1 == firstLineageDetails);
+        assertTrue(result2 == secondLineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_bothPartialLineages_returnsFullLineage() throws Exception {
+        // This test verifies the following scenario:
+        // - One package is signed with a rotated key B and linage A -> B
+        // - The other package is signed with a rotated key C and lineage B -> C
+        // Merging the lineage of these two should return the full lineage A -> B -> C
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(SECOND_SIGNATURE,
+                THIRD_SIGNATURE);
+        SigningDetails expectedDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        SigningDetails result1 = firstLineageDetails.mergeLineageWith(secondLineageDetails);
+        SigningDetails result2 = secondLineageDetails.mergeLineageWith(firstLineageDetails);
+
+        assertEquals(expectedDetails, result1);
+        assertEquals(expectedDetails, result2);
+    }
+
+    @Test
+    public void mergeLineageWith_oneSubsetLineage_returnsFullLineage() throws Exception {
+        // This test verifies when one lineage is a subset of the other the full lineage is
+        // returned.
+        SigningDetails subsetLineageDetails = createSigningDetailsWithLineage(SECOND_SIGNATURE,
+                THIRD_SIGNATURE);
+        SigningDetails fullLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE);
+
+        SigningDetails result1 = subsetLineageDetails.mergeLineageWith(fullLineageDetails);
+        SigningDetails result2 = fullLineageDetails.mergeLineageWith(subsetLineageDetails);
+
+        assertEquals(fullLineageDetails, result1);
+        assertTrue(result2 == fullLineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_differentRootsOfTrust_returnsOriginal() throws Exception {
+        // If two SigningDetails share a common lineage but diverge at one of the ancestors then the
+        // merge should return the invoking instance since this is not supported.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineage("1234",
+                FIRST_SIGNATURE, SECOND_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage("5678",
+                FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        SigningDetails result1 = firstLineageDetails.mergeLineageWith(secondLineageDetails);
+        SigningDetails result2 = secondLineageDetails.mergeLineageWith(firstLineageDetails);
+
+        assertTrue(result1 == firstLineageDetails);
+        assertTrue(result2 == secondLineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_divergedSignerInLineage_returnsOriginal() throws Exception {
+        // Similar to the test above if two lineages diverge at any point then the merge should
+        // return the original since the signers in a sharedUserId must always be either the same,
+        // a subset, or a superset of the existing lineage.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                "1234", SECOND_SIGNATURE, THIRD_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                "5678", SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        SigningDetails result1 = firstLineageDetails.mergeLineageWith(secondLineageDetails);
+        SigningDetails result2 = secondLineageDetails.mergeLineageWith(firstLineageDetails);
+
+        assertTrue(result1 == firstLineageDetails);
+        assertTrue(result2 == secondLineageDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithModifiedCaps()
+            throws Exception {
+        // This test verifies when two lineages consist of the same signers but have different
+        // capabilities the more restrictive capabilities are returned.
+        SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+        SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+                new int[]{INSTALLED_DATA, INSTALLED_DATA, INSTALLED_DATA});
+
+        SigningDetails result1 = defaultCapabilitiesDetails.mergeLineageWith(
+                modifiedCapabilitiesDetails);
+        SigningDetails result2 = modifiedCapabilitiesDetails.mergeLineageWith(
+                defaultCapabilitiesDetails);
+
+        assertEquals(modifiedCapabilitiesDetails, result1);
+        assertTrue(result2 == modifiedCapabilitiesDetails);
+    }
+
+    @Test
+    public void mergeLineageWith_overlappingLineageDiffCaps_returnsFullLineageWithModifiedCaps()
+            throws Exception {
+        // This test verifies the following scenario:
+        // - First lineage has signers A -> B with modified capabilities for A and B
+        // - Second lineage has signers B -> C with modified capabilities for B and C
+        // The merged lineage should be A -> B -> C with the most restrictive capabilities for B
+        // since it is in both lineages.
+        int[] firstCapabilities =
+                new int[]{INSTALLED_DATA | AUTH, INSTALLED_DATA | SHARED_USER_ID | PERMISSION};
+        int[] secondCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | AUTH,
+                INSTALLED_DATA | SHARED_USER_ID | AUTH};
+        int[] expectedCapabilities =
+                new int[]{firstCapabilities[0], firstCapabilities[1] & secondCapabilities[0],
+                        secondCapabilities[1]};
+        SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
+        SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, secondCapabilities);
+        SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+                expectedCapabilities);
+
+        SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails);
+        SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails);
+
+        assertEquals(expectedDetails, result1);
+        assertEquals(expectedDetails, result2);
+    }
+
+    @Test
+    public void mergeLineageWith_subLineageModifiedCaps_returnsFullLineageWithModifiedCaps()
+            throws Exception {
+        // This test verifies the following scenario:
+        // - First lineage has signers B -> C with modified capabilities
+        // - Second lineage has signers A -> B -> C -> D with modified capabilities
+        // The merged lineage should be A -> B -> C -> D with the most restrictive capabilities for
+        // B and C since they are in both lineages.
+        int[] subCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | PERMISSION,
+                DEFAULT_CAPABILITIES | ROLLBACK};
+        int[] fullCapabilities =
+                new int[]{0, SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES};
+        int[] expectedCapabilities =
+                new int[]{fullCapabilities[0], subCapabilities[0] & fullCapabilities[1],
+                        subCapabilities[1] & fullCapabilities[2], fullCapabilities[3]};
+        SigningDetails subLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, subCapabilities);
+        SigningDetails fullLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
+                fullCapabilities);
+        SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
+                expectedCapabilities);
+
+        SigningDetails result1 = subLineageDetails.mergeLineageWith(fullLineageDetails);
+        SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails);
+
+        assertEquals(expectedDetails, result1);
+        assertEquals(expectedDetails, result2);
+    }
+
+    @Test
+    public void mergeLineageWith_commonLineageDivergedSigners_returnsOriginal() throws Exception {
+        // When mergeWithLineage is invoked with SigningDetails instances that have a common lineage
+        // but diverged signers the calling instance should be returned since the current signer
+        // is not in the ancestry of the other's lineage.
+        SigningDetails firstLineageDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE,
+                THIRD_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetails(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+        SigningDetails result1 = firstLineageDetails.mergeLineageWith(secondLineageDetails);
+        SigningDetails result2 = secondLineageDetails.mergeLineageWith(firstLineageDetails);
+
+        assertTrue(result1 == firstLineageDetails);
+        assertTrue(result2 == secondLineageDetails);
+    }
+
+    @Test
+    public void hasCommonAncestor_noLineageSameSingleSigner_returnsTrue() throws Exception {
+        // If neither SigningDetails have a lineage but they have the same single signer then
+        // hasCommonAncestor should return true.
+        SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE);
+
+        assertTrue(firstDetails.hasCommonAncestor(secondDetails));
+        assertTrue(secondDetails.hasCommonAncestor(firstDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_noLineageSameMultipleSigners_returnsTrue() throws Exception {
+        // Similar to above if neither SigningDetails have a lineage but they have the same multiple
+        // signers then hasCommonAncestor should return true.
+        SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE, FIRST_SIGNATURE);
+
+        assertTrue(firstDetails.hasCommonAncestor(secondDetails));
+        assertTrue(secondDetails.hasCommonAncestor(firstDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_noLineageDifferentSigners_returnsFalse() throws Exception {
+        // If neither SigningDetails have a lineage and they have different signers then
+        // hasCommonAncestor should return false.
+        SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE);
+        SigningDetails thirdDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+        SigningDetails fourthDetails = createSigningDetails(SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        assertFalse(firstDetails.hasCommonAncestor(secondDetails));
+        assertFalse(firstDetails.hasCommonAncestor(thirdDetails));
+        assertFalse(firstDetails.hasCommonAncestor(fourthDetails));
+        assertFalse(secondDetails.hasCommonAncestor(firstDetails));
+        assertFalse(secondDetails.hasCommonAncestor(thirdDetails));
+        assertFalse(secondDetails.hasCommonAncestor(fourthDetails));
+        assertFalse(thirdDetails.hasCommonAncestor(firstDetails));
+        assertFalse(thirdDetails.hasCommonAncestor(secondDetails));
+        assertFalse(thirdDetails.hasCommonAncestor(fourthDetails));
+        assertFalse(fourthDetails.hasCommonAncestor(firstDetails));
+        assertFalse(fourthDetails.hasCommonAncestor(secondDetails));
+        assertFalse(fourthDetails.hasCommonAncestor(thirdDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_oneWithOthersSignerInLineage_returnsTrue() throws Exception {
+        // If only one of the SigningDetails has a lineage and the current signer of the other is in
+        // the lineage then hasCommonAncestor should return true.
+        SigningDetails noLineageDetails = createSigningDetails(FIRST_SIGNATURE);
+        SigningDetails lineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+
+        assertTrue(noLineageDetails.hasCommonAncestor(lineageDetails));
+        assertTrue(lineageDetails.hasCommonAncestor(noLineageDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_oneWithSameSignerWithoutLineage_returnsTrue() throws Exception {
+        // If only one of the SigningDetails has a lineage and both have the same current signer
+        // then hasCommonAncestor should return true.
+        SigningDetails noLineageDetails = createSigningDetails(SECOND_SIGNATURE);
+        SigningDetails lineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+
+        assertTrue(noLineageDetails.hasCommonAncestor(lineageDetails));
+        assertTrue(lineageDetails.hasCommonAncestor(noLineageDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_bothHaveSameLineage_returnsTrue() throws Exception {
+        // If both SigningDetails have the exact same lineage then hasCommonAncestor should return
+        // true.
+        SigningDetails firstDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+
+        assertTrue(firstDetails.hasCommonAncestor(secondDetails));
+        assertTrue(secondDetails.hasCommonAncestor(firstDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_oneLineageIsAncestor_returnsTrue() throws Exception {
+        // If one SigningDetails has a lineage that is an ancestor of the other then
+        // hasCommonAncestor should return true.
+        SigningDetails ancestorDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails descendantDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        assertTrue(ancestorDetails.hasCommonAncestor(descendantDetails));
+        assertTrue(descendantDetails.hasCommonAncestor(ancestorDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_oneLineageIsSubset_returnsTrue() throws Exception {
+        // If one SigningDetails has a lineage that is a subset of the other then hasCommonAncestor
+        // should return true.
+        SigningDetails subsetDetails = createSigningDetailsWithLineage(SECOND_SIGNATURE,
+                THIRD_SIGNATURE);
+        SigningDetails fullDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE);
+
+        assertTrue(subsetDetails.hasCommonAncestor(fullDetails));
+        assertTrue(fullDetails.hasCommonAncestor(subsetDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_differentRootOfTrustInLineage_returnsFalse() throws Exception {
+        // if the two SigningDetails have a different root of trust then hasCommonAncestor should
+        // return false.
+        SigningDetails firstDetails = createSigningDetailsWithLineage(THIRD_SIGNATURE,
+                FIRST_SIGNATURE, SECOND_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetailsWithLineage(FOURTH_SIGNATURE,
+                FIRST_SIGNATURE, SECOND_SIGNATURE);
+
+        assertFalse(firstDetails.hasCommonAncestor(secondDetails));
+        assertFalse(secondDetails.hasCommonAncestor(firstDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_differentSignerInMiddleOfLineage_returnsFalse() throws Exception {
+        // if the two SigningDetails have a different signer in the middle of a common lineage then
+        // hasCommonAncestor should return false.
+        SigningDetails firstDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, "1234",
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, "5678",
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        assertFalse(firstDetails.hasCommonAncestor(secondDetails));
+        assertFalse(secondDetails.hasCommonAncestor(firstDetails));
+    }
+
+    @Test
+    public void hasCommonAncestor_overlappingLineages_returnsTrue() throws Exception {
+        // if the two SigningDetails have overlapping lineages then hasCommonAncestor should return
+        // true.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(SECOND_SIGNATURE,
+                THIRD_SIGNATURE);
+
+        assertTrue(firstLineageDetails.hasCommonAncestor(secondLineageDetails));
+        assertTrue(secondLineageDetails.hasCommonAncestor(firstLineageDetails));
+    }
+
+    private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception {
+        int[] capabilities = new int[signers.length];
+        for (int i = 0; i < capabilities.length; i++) {
+            capabilities[i] = DEFAULT_CAPABILITIES;
+        }
+        return createSigningDetailsWithLineageAndCapabilities(signers, capabilities);
+    }
+
+    private SigningDetails createSigningDetailsWithLineageAndCapabilities(String[] signers,
+            int[] capabilities) throws Exception {
+        if (capabilities.length != signers.length) {
+            fail("The capabilities array must contain the same number of elements as the signers "
+                    + "array");
+        }
         Signature[] signingHistory = new Signature[signers.length];
         for (int i = 0; i < signers.length; i++) {
             signingHistory[i] = new Signature(signers[i]);
+            signingHistory[i].setFlags(capabilities[i]);
         }
         Signature[] currentSignature = new Signature[]{signingHistory[signers.length - 1]};
-        // TODO: Since the PublicKey ArraySet is not used by any of the tests a generic empty Set
-        // works for now, but if this is needed in the future consider creating mock PublicKeys that
-        // can respond as required for the method under test.
-        ArraySet<PublicKey> publicKeys = new ArraySet<>();
-        return new SigningDetails(currentSignature, SIGNING_BLOCK_V3, publicKeys, signingHistory);
+        return new SigningDetails(currentSignature, SIGNING_BLOCK_V3, signingHistory);
     }
 
-    private SigningDetails createSigningDetails(String... signers) {
+    private SigningDetails createSigningDetails(String... signers) throws Exception {
         Signature[] currentSignatures = new Signature[signers.length];
         for (int i = 0; i < signers.length; i++) {
             currentSignatures[i] = new Signature(signers[i]);
         }
-        // TODO: Similar to above when tests are added that require this it should be updated to use
-        // mocked PublicKeys.
-        ArraySet<PublicKey> publicKeys = new ArraySet<>();
-        return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, publicKeys, null);
+        return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null);
     }
 }
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 4aff3e5..b6c6cd0 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -113,7 +113,7 @@
     get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint);
 }
 
-static bool restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+static jboolean restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
     Canvas* canvas = get_canvas(canvasHandle);
     if (canvas->getSaveCount() <= 1) {
         return false; // cannot restore anymore
diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
index 8a26296..9cffceb 100644
--- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -102,7 +102,7 @@
 /**
  * Draw
  */
-static int draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr,
+static jint draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr,
         jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) {
     VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp
index df5e9cd..72995ef 100644
--- a/libs/hwui/jni/android_util_PathParser.cpp
+++ b/libs/hwui/jni/android_util_PathParser.cpp
@@ -39,18 +39,18 @@
     }
 }
 
-static long createEmptyPathData(JNIEnv*, jobject) {
+static jlong createEmptyPathData(JNIEnv*, jobject) {
     PathData* pathData = new PathData();
     return reinterpret_cast<jlong>(pathData);
 }
 
-static long createPathData(JNIEnv*, jobject, jlong pathDataPtr) {
+static jlong createPathData(JNIEnv*, jobject, jlong pathDataPtr) {
     PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
     PathData* newPathData = new PathData(*pathData);
     return reinterpret_cast<jlong>(newPathData);
 }
 
-static long createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) {
+static jlong createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) {
     const char* pathString = env->GetStringUTFChars(inputStr, NULL);
     PathData* pathData = new PathData();
     PathParser::ParseResult result;
@@ -65,7 +65,7 @@
     }
 }
 
-static bool interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr,
+static jboolean interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr,
         jlong toPathDataPtr, jfloat fraction) {
     PathData* outPathData = reinterpret_cast<PathData*>(outPathDataPtr);
     PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
@@ -79,7 +79,7 @@
     delete pathData;
 }
 
-static bool canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) {
+static jboolean canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) {
     PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
     PathData* toPathData = reinterpret_cast<PathData*>(toPathDataPtr);
     return VectorDrawableUtils::canMorph(*fromPathData, *toPathData);
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
index 1dd0291..c0482e1 100644
--- a/packages/CarSystemUI/AndroidManifest.xml
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -25,6 +25,11 @@
     <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/>
     <!-- This permission is required to get bluetooth broadcast. -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
+    <!-- These permissions are required to implement icons based on role holders. -->
+    <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"/>
+    <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS"/>
+    <!-- This permission is required to access app information from other users. -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
     <!-- This permission is required to check the foreground user id. -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 </manifest>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 1418bf8..2a715d0 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -125,6 +125,7 @@
             android:id="@+id/assist"
             style="@style/NavigationBarButton"
             systemui:icon="@drawable/ic_mic_white"
+            systemui:useDefaultAppIconForRole="true"
         />
 
     </LinearLayout>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index 837252b..ca4e76e 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -29,12 +29,14 @@
 
         <com.android.keyguard.AlphaOptimizedImageButton
             android:id="@+id/car_nav_button_icon_image"
-            android:layout_height="wrap_content"
+            android:layout_height="@dimen/car_navigation_button_icon_height"
             android:layout_width="match_parent"
             android:layout_gravity="center"
             android:animateLayoutChanges="true"
             android:background="@android:color/transparent"
             android:scaleType="fitCenter"
+            android:tintMode="src_in"
+            android:tint="@color/car_nav_icon_fill_color"
             android:clickable="false"
         />
 
@@ -48,6 +50,7 @@
             android:background="@android:color/transparent"
             android:scaleType="fitCenter"
             android:clickable="false"
+            android:visibility="gone"
         />
 
         <ImageView
diff --git a/packages/CarSystemUI/res/values/attrs.xml b/packages/CarSystemUI/res/values/attrs.xml
index a586763..7883764 100644
--- a/packages/CarSystemUI/res/values/attrs.xml
+++ b/packages/CarSystemUI/res/values/attrs.xml
@@ -65,6 +65,10 @@
         <attr name="showMoreWhenSelected" format="boolean" />
         <!-- whether to highlight the button when selected. Defaults false -->
         <attr name="highlightWhenSelected" format="boolean" />
+        <!-- whether to show the icon of the app currently associated this button's role. Only
+             relevant for buttons associated to specific roles (e.g.: AssistantButton).
+             Defaults false -->
+        <attr name="useDefaultAppIconForRole" format="boolean"/>
     </declare-styleable>
 
     <!-- Custom attributes to configure hvac values -->
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 0e84d51..d20ab49 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -21,7 +21,7 @@
     <color name="car_user_switcher_name_text_color">@*android:color/car_body1_light</color>
     <color name="car_user_switcher_add_user_background_color">#131313</color>
     <color name="car_user_switcher_add_user_add_sign_color">@*android:color/car_body1_light</color>
-    <color name="car_nav_icon_fill_color">#8Fffffff</color>
+    <color name="car_nav_icon_fill_color">#8F8F8F</color>
     <color name="car_nav_icon_fill_color_selected">#ffffff</color>
     <!-- colors for seekbar -->
     <color name="car_seekbar_track_background">#131315</color>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index ed0b485..cb321cd 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -175,6 +175,7 @@
     <dimen name="car_user_switcher_margin_top">@*android:dimen/car_padding_4</dimen>
 
     <dimen name="car_navigation_button_width">64dp</dimen>
+    <dimen name="car_navigation_button_icon_height">44dp</dimen>
     <dimen name="car_navigation_bar_width">760dp</dimen>
     <dimen name="car_left_navigation_bar_width">96dp</dimen>
     <dimen name="car_right_navigation_bar_width">96dp</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/AssitantButton.java
index 69ec78e..ede4696 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/AssitantButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/AssitantButton.java
@@ -18,6 +18,7 @@
 
 import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
 
+import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Bundle;
@@ -31,7 +32,6 @@
  * AssitantButton is a ui component that will trigger the Voice Interaction Service.
  */
 public class AssitantButton extends CarNavigationButton {
-
     private static final String TAG = "AssistantButton";
     private final AssistUtils mAssistUtils;
     private IVoiceInteractionSessionShowCallback mShowCallback =
@@ -50,9 +50,7 @@
     public AssitantButton(Context context, AttributeSet attrs) {
         super(context, attrs);
         mAssistUtils = new AssistUtils(context);
-        setOnClickListener(v -> {
-            showAssistant();
-        });
+        setOnClickListener(v -> showAssistant());
     }
 
     private void showAssistant() {
@@ -65,4 +63,9 @@
     protected void setUpIntents(TypedArray typedArray) {
         // left blank because for the assistant button Intent will not be passed from the layout.
     }
+
+    @Override
+    protected String getRoleName() {
+        return RoleManager.ROLE_ASSISTANT;
+    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonRoleHolderController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonRoleHolderController.java
new file mode 100644
index 0000000..5c83c02
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonRoleHolderController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.navigationbar;
+
+import android.annotation.Nullable;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.car.CarDeviceProvisionedController;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Some CarNavigationButtons can be associated to a {@link RoleManager} role. When they are, it is
+ * possible to have them display the icon of the default application (role holder) for the given
+ * role.
+ *
+ * This class monitors the current role holders for each role type and updates the button icon for
+ * this buttons with have this feature enabled.
+ */
+@Singleton
+public class ButtonRoleHolderController {
+    private static final String TAG = "ButtonRoleHolderController";
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final RoleManager mRoleManager;
+    private final CarDeviceProvisionedController mDeviceController;
+    private final Map<String, CarNavigationButton> mButtonMap = new HashMap<>();
+    private final OnRoleHoldersChangedListener mListener = this::onRoleChanged;
+    private boolean mRegistered;
+
+    @Inject
+    public ButtonRoleHolderController(Context context, PackageManager packageManager,
+            RoleManager roleManager, CarDeviceProvisionedController deviceController) {
+        mContext = context;
+        mPackageManager = packageManager;
+        mRoleManager = roleManager;
+        mDeviceController = deviceController;
+    }
+
+    /**
+     * Iterate through a view looking for CarNavigationButton and add it to this controller if it
+     * opted to be associated with a {@link RoleManager} role type.
+     *
+     * @param v the View that may contain CarFacetButtons
+     */
+    void addAllButtonsWithRoleName(View v) {
+        if (v instanceof CarNavigationButton) {
+            CarNavigationButton button = (CarNavigationButton) v;
+            String roleName = button.getRoleName();
+            if (roleName != null && button.isDefaultAppIconForRoleEnabled()) {
+                addButtonWithRoleName(button, roleName);
+            }
+        } else if (v instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) v;
+            for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                addAllButtonsWithRoleName(viewGroup.getChildAt(i));
+            }
+        }
+    }
+
+    private void addButtonWithRoleName(CarNavigationButton button, String roleName) {
+        mButtonMap.put(roleName, button);
+        updateIcon(roleName);
+        if (!mRegistered) {
+            mRoleManager.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
+                    mListener, UserHandle.ALL);
+            mRegistered = true;
+        }
+    }
+
+    void removeAll() {
+        mButtonMap.clear();
+        if (mRegistered) {
+            mRoleManager.removeOnRoleHoldersChangedListenerAsUser(mListener, UserHandle.ALL);
+            mRegistered = false;
+        }
+    }
+
+    @VisibleForTesting
+    void onRoleChanged(String roleName, UserHandle user) {
+        if (RoleManager.ROLE_ASSISTANT.equals(roleName)
+                && user.getIdentifier() == mDeviceController.getCurrentUser()) {
+            updateIcon(roleName);
+        }
+    }
+
+    private void updateIcon(String roleName) {
+        CarNavigationButton button = mButtonMap.get(roleName);
+        if (button == null) {
+            return;
+        }
+        List<String> holders = mRoleManager.getRoleHoldersAsUser(button.getRoleName(),
+                UserHandle.of(mDeviceController.getCurrentUser()));
+        if (holders == null || holders.isEmpty()) {
+            button.setAppIcon(null);
+        } else {
+            button.setAppIcon(loadIcon(holders.get(0)));
+        }
+    }
+
+    @Nullable
+    private Drawable loadIcon(String packageName) {
+        try {
+            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName,
+                    PackageManager.MATCH_ANY_USER);
+            return appInfo.loadIcon(mPackageManager);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(ButtonRoleHolderController.TAG, "Package not found: " + packageName, e);
+            return null;
+        }
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 5c6472e..4c720ab 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -87,7 +87,6 @@
     private final Executor mUiBgExecutor;
     private final IStatusBarService mBarService;
     private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
-    private final ButtonSelectionStateController mButtonSelectionStateController;
     private final Lazy<PhoneStatusBarPolicy> mIconPolicyLazy;
     private final Lazy<StatusBarIconController> mIconControllerLazy;
 
@@ -139,7 +138,6 @@
             @UiBackground Executor uiBgExecutor,
             IStatusBarService barService,
             Lazy<KeyguardStateController> keyguardStateControllerLazy,
-            ButtonSelectionStateController buttonSelectionStateController,
             Lazy<PhoneStatusBarPolicy> iconPolicyLazy,
             Lazy<StatusBarIconController> iconControllerLazy
     ) {
@@ -156,7 +154,6 @@
         mUiBgExecutor = uiBgExecutor;
         mBarService = barService;
         mKeyguardStateControllerLazy = keyguardStateControllerLazy;
-        mButtonSelectionStateController = buttonSelectionStateController;
         mIconPolicyLazy = iconPolicyLazy;
         mIconControllerLazy = iconControllerLazy;
 
@@ -280,10 +277,9 @@
      * before and after the device is provisioned. . Also for change of density and font size.
      */
     private void restartNavBars() {
-        // remove and reattach all hvac components such that we don't keep a reference to unused
-        // ui elements
-        mCarNavigationBarController.removeAllFromHvac();
-        mButtonSelectionStateController.removeAll();
+        // remove and reattach all components such that we don't keep a reference to unused ui
+        // elements
+        mCarNavigationBarController.removeAll();
 
         if (mTopNavigationBarWindow != null) {
             mTopNavigationBarWindow.removeAllViews();
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
index 288e5cf..ca780ae 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
@@ -37,6 +37,7 @@
     private final Context mContext;
     private final NavigationBarViewFactory mNavigationBarViewFactory;
     private final ButtonSelectionStateController mButtonSelectionStateController;
+    private final ButtonRoleHolderController mButtonRoleHolderController;
     private final Lazy<HvacController> mHvacControllerLazy;
 
     private boolean mShowTop;
@@ -59,11 +60,13 @@
     public CarNavigationBarController(Context context,
             NavigationBarViewFactory navigationBarViewFactory,
             ButtonSelectionStateController buttonSelectionStateController,
-            Lazy<HvacController> hvacControllerLazy) {
+            Lazy<HvacController> hvacControllerLazy,
+            ButtonRoleHolderController buttonRoleHolderController) {
         mContext = context;
         mNavigationBarViewFactory = navigationBarViewFactory;
         mButtonSelectionStateController = buttonSelectionStateController;
         mHvacControllerLazy = hvacControllerLazy;
+        mButtonRoleHolderController = buttonRoleHolderController;
 
         // Read configuration.
         mShowTop = mContext.getResources().getBoolean(R.bool.config_enableTopNavigationBar);
@@ -101,9 +104,11 @@
         mHvacControllerLazy.get().connectToCarService();
     }
 
-    /** Clean up hvac. */
-    public void removeAllFromHvac() {
+    /** Clean up */
+    public void removeAll() {
         mHvacControllerLazy.get().removeAllComponents();
+        mButtonSelectionStateController.removeAll();
+        mButtonRoleHolderController.removeAll();
     }
 
     /** Gets the top window if configured to do so. */
@@ -211,6 +216,7 @@
         view.setStatusBarWindowTouchListener(statusBarTouchListener);
         view.setNotificationsPanelController(notifShadeController);
         mButtonSelectionStateController.addAllButtonsWithSelectionState(view);
+        mButtonRoleHolderController.addAllButtonsWithRoleName(view);
         mHvacControllerLazy.get().addTemperatureViewToController(view);
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
index 5f4ac2d..5e113d6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
@@ -17,9 +17,11 @@
 package com.android.systemui.car.navigationbar;
 
 import android.app.ActivityOptions;
+import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.UserHandle;
 import android.util.AttributeSet;
@@ -29,6 +31,7 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
 
@@ -62,6 +65,8 @@
     private float mUnselectedAlpha;
     private int mSelectedIconResourceId;
     private int mIconResourceId;
+    private Drawable mAppIcon;
+    private boolean mIsDefaultAppIconForRoleEnabled;
     private String[] mComponentNames;
     /** App categories that are to be used with this widget */
     private String[] mButtonCategories;
@@ -92,7 +97,9 @@
         super.setSelected(selected);
         mSelected = selected;
         if (mHighlightWhenSelected) {
-            setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
+            // Always apply selected alpha if the button does not toggle alpha based on selection
+            // state.
+            setAlpha(!mHighlightWhenSelected || mSelected ? mSelectedAlpha : mUnselectedAlpha);
         }
         if (mShowMoreWhenSelected && mMoreIcon != null) {
             mMoreIcon.setVisibility(selected ? VISIBLE : GONE);
@@ -108,6 +115,20 @@
         updateImage();
     }
 
+    /**
+     * Sets the current icon of the default application associated with this button.
+     */
+    public void setAppIcon(Drawable appIcon) {
+        mAppIcon = appIcon;
+        updateImage();
+    }
+
+    /** Gets the icon of the app currently associated to the role of this button. */
+    @VisibleForTesting
+    protected Drawable getAppIcon() {
+        return mAppIcon;
+    }
+
     /** Gets whether the icon is in an unseen state. */
     public boolean getUnseen() {
         return mHasUnseen;
@@ -144,6 +165,22 @@
     }
 
     /**
+     * Subclasses should override this method to return the {@link RoleManager} role associated
+     * with this button.
+     */
+    protected String getRoleName() {
+        return null;
+    }
+
+    /**
+     * @return true if this button should show the icon of the default application for the
+     * role returned by {@link #getRoleName()}.
+     */
+    protected boolean isDefaultAppIconForRoleEnabled() {
+        return mIsDefaultAppIconForRoleEnabled;
+    }
+
+    /**
      * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
      * a display.
      */
@@ -240,7 +277,6 @@
         };
     }
 
-
     /**
      * Initializes view-related aspects of the button.
      */
@@ -256,28 +292,27 @@
                 R.styleable.CarNavigationButton_showMoreWhenSelected,
                 mShowMoreWhenSelected);
 
-        mSelectedIconResourceId = typedArray.getResourceId(
-                R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
         mIconResourceId = typedArray.getResourceId(
                 R.styleable.CarNavigationButton_icon, 0);
-
+        mSelectedIconResourceId = typedArray.getResourceId(
+                R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
+        mIsDefaultAppIconForRoleEnabled = typedArray.getBoolean(
+                R.styleable.CarNavigationButton_useDefaultAppIconForRole, false);
         mIcon = findViewById(R.id.car_nav_button_icon_image);
-        mIcon.setScaleType(ImageView.ScaleType.CENTER);
         // Always apply selected alpha if the button does not toggle alpha based on selection state.
         mIcon.setAlpha(mHighlightWhenSelected ? mUnselectedAlpha : mSelectedAlpha);
-        mIcon.setImageResource(mIconResourceId);
-
         mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
         mMoreIcon.setAlpha(mSelectedAlpha);
-        mMoreIcon.setVisibility(GONE);
-
         mUnseenIcon = findViewById(R.id.car_nav_button_unseen_icon);
-
-        mUnseenIcon.setVisibility(mHasUnseen ? VISIBLE : GONE);
+        updateImage();
     }
 
     private void updateImage() {
-        mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+        if (mIsDefaultAppIconForRoleEnabled && mAppIcon != null) {
+            mIcon.setImageDrawable(mAppIcon);
+        } else {
+            mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+        }
         mUnseenIcon.setVisibility(mHasUnseen ? VISIBLE : GONE);
     }
 
diff --git a/packages/CarSystemUI/tests/res/layout/button_role_holder_controller_test.xml b/packages/CarSystemUI/tests/res/layout/button_role_holder_controller_test.xml
new file mode 100644
index 0000000..25ec2c1
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/button_role_holder_controller_test.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@id/nav_buttons"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:paddingStart="20dp"
+    android:paddingEnd="20dp"
+    android:gravity="center">
+
+    <com.android.systemui.car.navigationbar.AssitantButton
+        android:id="@+id/assistant_role_button"
+        style="@style/NavigationBarButton"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:useDefaultAppIconForRole="true"
+    />
+
+    <com.android.systemui.car.navigationbar.AssitantButton
+        android:id="@+id/assistant_role_disabled_button"
+        style="@style/NavigationBarButton"
+        systemui:icon="@drawable/car_ic_overview"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml b/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml
index f0e0216..a8e83d6 100644
--- a/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml
+++ b/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml
@@ -25,7 +25,7 @@
     android:paddingEnd="20dp"
     android:gravity="center">
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/detectable_by_component_name"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -34,7 +34,7 @@
         systemui:highlightWhenSelected="true"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/detectable_by_category"
         style="@style/NavigationBarButton"
         systemui:categories="android.intent.category.APP_MAPS"
@@ -43,7 +43,7 @@
         systemui:highlightWhenSelected="true"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/detectable_by_package"
         style="@style/NavigationBarButton"
         systemui:icon="@drawable/car_ic_phone"
diff --git a/packages/CarSystemUI/tests/res/layout/car_navigation_bar_view_test.xml b/packages/CarSystemUI/tests/res/layout/car_navigation_bar_view_test.xml
index b0ca8dc..94edc4b 100644
--- a/packages/CarSystemUI/tests/res/layout/car_navigation_bar_view_test.xml
+++ b/packages/CarSystemUI/tests/res/layout/car_navigation_bar_view_test.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License.
   -->
 
-<com.android.systemui.navigationbar.car.CarNavigationBarView
+<com.android.systemui.car.navigationbar.CarNavigationBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -32,7 +32,7 @@
         android:paddingEnd="20dp"
         android:gravity="center">
 
-        <com.android.systemui.navigationbar.car.CarNavigationButton
+        <com.android.systemui.car.navigationbar.CarNavigationButton
             android:id="@+id/home"
             style="@style/NavigationBarButton"
             systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -55,4 +55,4 @@
         android:visibility="gone"
     />
 
-</com.android.systemui.navigationbar.car.CarNavigationBarView>
+</com.android.systemui.car.navigationbar.CarNavigationBarView>
diff --git a/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml b/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml
index 576928c..44f8340 100644
--- a/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml
+++ b/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml
@@ -25,7 +25,7 @@
     android:paddingEnd="20dp"
     android:gravity="center">
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/default_no_selection_state"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -34,7 +34,7 @@
         systemui:selectedIcon="@drawable/car_ic_overview_selected"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/app_grid_activity"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
@@ -44,7 +44,7 @@
         systemui:highlightWhenSelected="true"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/long_click_app_grid_activity"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
@@ -54,7 +54,7 @@
         systemui:highlightWhenSelected="true"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/broadcast"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -63,7 +63,7 @@
         systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/selected_icon_undefined"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -71,7 +71,7 @@
         systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/highlightable_no_more_button"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -81,7 +81,7 @@
         systemui:highlightWhenSelected="true"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/not_highlightable_more_button"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -91,7 +91,7 @@
         systemui:showMoreWhenSelected="true"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/highlightable_more_button"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -102,7 +102,7 @@
         systemui:showMoreWhenSelected="true"
     />
 
-    <com.android.systemui.navigationbar.car.CarNavigationButton
+    <com.android.systemui.car.navigationbar.CarNavigationButton
         android:id="@+id/broadcast"
         style="@style/NavigationBarButton"
         systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
@@ -112,4 +112,13 @@
         systemui:broadcast="true"
     />
 
+    <com.android.systemui.car.navigationbar.AssitantButton
+        android:id="@+id/role_based_button"
+        style="@style/NavigationBarButton"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:selectedIcon="@drawable/car_ic_overview_selected"
+        systemui:useDefaultAppIconForRole="true"
+        systemui:highlightWhenSelected="true"
+    />
+
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
new file mode 100644
index 0000000..a57736b
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.navigationbar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+
+import android.app.role.RoleManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.tests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class ButtonRoleHolderControllerTest extends SysuiTestCase {
+    private static final String TEST_VALID_PACKAGE_NAME = "foo";
+    private static final String TEST_INVALID_PACKAGE_NAME = "bar";
+    private static final UserHandle TEST_CURRENT_USER = UserHandle.of(100);
+    private static final UserHandle TEST_NON_CURRENT_USER = UserHandle.of(101);
+
+    private LinearLayout mTestView;
+    private CarNavigationButton mNavButtonDefaultAppIconForRoleWithEnabled;
+    private CarNavigationButton mNavButtonDefaultAppIconForRoleWithDisabled;
+    private ButtonRoleHolderController mControllerUnderTest;
+    private Drawable mAppIcon;
+
+    @Mock
+    private RoleManager mRoleManager;
+    @Mock
+    private CarDeviceProvisionedController mDeviceProvisionedController;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+
+        mTestView = (LinearLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.button_role_holder_controller_test, /* root= */ null);
+        mNavButtonDefaultAppIconForRoleWithEnabled = mTestView
+                .findViewById(R.id.assistant_role_button);
+        mNavButtonDefaultAppIconForRoleWithDisabled = mTestView
+                .findViewById(R.id.assistant_role_disabled_button);
+        mAppIcon = mContext.getDrawable(R.drawable.car_ic_apps);
+        when(mApplicationInfo.loadIcon(any())).thenReturn(mAppIcon);
+        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
+                .getApplicationInfo(any(), anyInt());
+        doReturn(mApplicationInfo).when(mPackageManager)
+                .getApplicationInfo(eq(TEST_VALID_PACKAGE_NAME), anyInt());
+        when(mDeviceProvisionedController
+                .getCurrentUser())
+                .thenReturn(TEST_CURRENT_USER.getIdentifier());
+        mControllerUnderTest = new ButtonRoleHolderController(mContext,
+                mPackageManager, mRoleManager, mDeviceProvisionedController);
+    }
+
+    @Test
+    public void addAllButtonsWithRoleName_roleAssigned_appIconEnabled_useAssignedAppIcon() {
+        when(mRoleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(List.of(TEST_VALID_PACKAGE_NAME));
+
+        mControllerUnderTest.addAllButtonsWithRoleName(mTestView);
+
+        assertThat(mNavButtonDefaultAppIconForRoleWithEnabled.getAppIcon()).isEqualTo(mAppIcon);
+    }
+
+    @Test
+    public void addAllButtonsWithRoleName_roleUnassigned_appIconEnabled_useDefaultIcon() {
+        when(mRoleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(null);
+
+        mControllerUnderTest.addAllButtonsWithRoleName(mTestView);
+
+        assertThat(mNavButtonDefaultAppIconForRoleWithEnabled.getAppIcon()).isNull();
+    }
+
+    @Test
+    public void onRoleChanged_currentUser_appIconEnabled_useAssignedAppIcon() {
+        when(mRoleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(null);
+        mControllerUnderTest.addAllButtonsWithRoleName(mTestView);
+        when(mRoleManager
+                .getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(List.of(TEST_VALID_PACKAGE_NAME));
+
+        mControllerUnderTest.onRoleChanged(RoleManager.ROLE_ASSISTANT, TEST_CURRENT_USER);
+
+        assertThat(mNavButtonDefaultAppIconForRoleWithEnabled.getAppIcon()).isEqualTo(mAppIcon);
+    }
+
+    @Test
+    public void onRoleChanged_nonCurrentUser_appIconEnabled_iconIsNotUpdated() {
+        when(mRoleManager
+                .getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(null);
+        mControllerUnderTest.addAllButtonsWithRoleName(mTestView);
+        Drawable beforeIcon = mNavButtonDefaultAppIconForRoleWithEnabled.getAppIcon();
+        when(mRoleManager
+                .getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(List.of(TEST_VALID_PACKAGE_NAME));
+
+        mControllerUnderTest.onRoleChanged(RoleManager.ROLE_ASSISTANT, TEST_NON_CURRENT_USER);
+
+        Drawable afterIcon = mNavButtonDefaultAppIconForRoleWithEnabled.getAppIcon();
+        assertThat(afterIcon).isEqualTo(beforeIcon);
+    }
+
+    @Test
+    public void onRoleChanged_invalidPackage_useDefaultIcon() {
+        when(mRoleManager
+                .getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(List.of(TEST_INVALID_PACKAGE_NAME));
+
+        mControllerUnderTest.addAllButtonsWithRoleName(mTestView);
+
+        assertThat(mNavButtonDefaultAppIconForRoleWithEnabled.getAppIcon()).isNull();
+    }
+
+    @Test
+    public void addAllButtonsWithRoleName_appIconDisabled_useDefaultIcon() {
+        when(mRoleManager
+                .getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(List.of(TEST_VALID_PACKAGE_NAME));
+
+        mControllerUnderTest.addAllButtonsWithRoleName(mTestView);
+
+        assertThat(mNavButtonDefaultAppIconForRoleWithDisabled.getAppIcon()).isNull();
+    }
+
+    @Test
+    public void onRoleChanged_roleAssigned_appIconDisabled_useDefaultIcon() {
+        when(mRoleManager
+                .getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(null);
+        mControllerUnderTest.addAllButtonsWithRoleName(mTestView);
+        assertThat(mNavButtonDefaultAppIconForRoleWithDisabled.getAppIcon()).isNull();
+        when(mRoleManager
+                .getRoleHoldersAsUser(eq(RoleManager.ROLE_ASSISTANT), any()))
+                .thenReturn(List.of(TEST_VALID_PACKAGE_NAME));
+
+        mControllerUnderTest.onRoleChanged(RoleManager.ROLE_ASSISTANT, TEST_CURRENT_USER);
+
+        assertThat(mNavButtonDefaultAppIconForRoleWithDisabled.getAppIcon()).isNull();
+    }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
index 911f624..e84e42c 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
@@ -53,6 +53,8 @@
     @Mock
     private ButtonSelectionStateController mButtonSelectionStateController;
     @Mock
+    private ButtonRoleHolderController mButtonRoleHolderController;
+    @Mock
     private HvacController mHvacController;
 
     @Before
@@ -66,10 +68,15 @@
         mDependency.injectMockDependency(StatusBarIconController.class);
     }
 
+    private CarNavigationBarController createNavigationBarController() {
+        return new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+                mButtonSelectionStateController, () -> mHvacController,
+                mButtonRoleHolderController);
+    }
+
     @Test
     public void testConnectToHvac_callsConnect() {
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         mCarNavigationBar.connectToHvac();
 
@@ -77,20 +84,37 @@
     }
 
     @Test
-    public void testRemoveAllFromHvac_callsRemoveAll() {
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+    public void testRemoveAll_callsHvacControllerRemoveAllComponents() {
+        mCarNavigationBar = createNavigationBarController();
 
-        mCarNavigationBar.removeAllFromHvac();
+        mCarNavigationBar.removeAll();
 
         verify(mHvacController).removeAllComponents();
     }
 
+
+    @Test
+    public void testRemoveAll_callsButtonRoleHolderControllerRemoveAll() {
+        mCarNavigationBar = createNavigationBarController();
+
+        mCarNavigationBar.removeAll();
+
+        verify(mButtonRoleHolderController).removeAll();
+    }
+
+    @Test
+    public void testRemoveAll_callsButtonSelectionStateControllerRemoveAll() {
+        mCarNavigationBar = createNavigationBarController();
+
+        mCarNavigationBar.removeAll();
+
+        verify(mButtonSelectionStateController).removeAll();
+    }
+
     @Test
     public void testGetTopWindow_topDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, false);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getTopWindow();
 
@@ -100,8 +124,7 @@
     @Test
     public void testGetTopWindow_topEnabled_returnsWindow() {
         mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getTopWindow();
 
@@ -111,8 +134,7 @@
     @Test
     public void testGetTopWindow_topEnabled_calledTwice_returnsSameWindow() {
         mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window1 = mCarNavigationBar.getTopWindow();
         ViewGroup window2 = mCarNavigationBar.getTopWindow();
@@ -123,8 +145,7 @@
     @Test
     public void testGetBottomWindow_bottomDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
 
@@ -134,8 +155,7 @@
     @Test
     public void testGetBottomWindow_bottomEnabled_returnsWindow() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
 
@@ -145,8 +165,7 @@
     @Test
     public void testGetBottomWindow_bottomEnabled_calledTwice_returnsSameWindow() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window1 = mCarNavigationBar.getBottomWindow();
         ViewGroup window2 = mCarNavigationBar.getBottomWindow();
@@ -157,8 +176,7 @@
     @Test
     public void testGetLeftWindow_leftDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, false);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
         ViewGroup window = mCarNavigationBar.getLeftWindow();
         assertThat(window).isNull();
     }
@@ -166,8 +184,7 @@
     @Test
     public void testGetLeftWindow_leftEnabled_returnsWindow() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getLeftWindow();
 
@@ -177,8 +194,7 @@
     @Test
     public void testGetLeftWindow_leftEnabled_calledTwice_returnsSameWindow() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window1 = mCarNavigationBar.getLeftWindow();
         ViewGroup window2 = mCarNavigationBar.getLeftWindow();
@@ -189,8 +205,7 @@
     @Test
     public void testGetRightWindow_rightDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, false);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
 
@@ -200,8 +215,7 @@
     @Test
     public void testGetRightWindow_rightEnabled_returnsWindow() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
 
@@ -211,8 +225,7 @@
     @Test
     public void testGetRightWindow_rightEnabled_calledTwice_returnsSameWindow() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window1 = mCarNavigationBar.getRightWindow();
         ViewGroup window2 = mCarNavigationBar.getRightWindow();
@@ -223,8 +236,7 @@
     @Test
     public void testSetBottomWindowVisibility_setTrue_isVisible() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
         mCarNavigationBar.setBottomWindowVisibility(View.VISIBLE);
@@ -235,8 +247,7 @@
     @Test
     public void testSetBottomWindowVisibility_setFalse_isGone() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
         mCarNavigationBar.setBottomWindowVisibility(View.GONE);
@@ -247,8 +258,7 @@
     @Test
     public void testSetLeftWindowVisibility_setTrue_isVisible() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getLeftWindow();
         mCarNavigationBar.setLeftWindowVisibility(View.VISIBLE);
@@ -259,8 +269,7 @@
     @Test
     public void testSetLeftWindowVisibility_setFalse_isGone() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getLeftWindow();
         mCarNavigationBar.setLeftWindowVisibility(View.GONE);
@@ -271,8 +280,7 @@
     @Test
     public void testSetRightWindowVisibility_setTrue_isVisible() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
         mCarNavigationBar.setRightWindowVisibility(View.VISIBLE);
@@ -283,8 +291,7 @@
     @Test
     public void testSetRightWindowVisibility_setFalse_isGone() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
         mCarNavigationBar.setRightWindowVisibility(View.GONE);
@@ -295,8 +302,7 @@
     @Test
     public void testRegisterBottomBarTouchListener_createViewFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener();
@@ -310,8 +316,7 @@
     @Test
     public void testRegisterBottomBarTouchListener_registerFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class));
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
@@ -323,8 +328,7 @@
     @Test
     public void testRegisterNotificationController_createViewFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         CarNavigationBarController.NotificationsShadeController controller =
@@ -340,8 +344,7 @@
     @Test
     public void testRegisterNotificationController_registerFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
 
         mCarNavigationBar.registerNotificationController(
                 mock(CarNavigationBarController.NotificationsShadeController.class));
@@ -355,8 +358,7 @@
     @Test
     public void testShowAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsVisible() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
 
@@ -368,8 +370,7 @@
     @Test
     public void testShowAllKeyguardButtons_bottomEnabled_bottomNavButtonsGone() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
 
@@ -381,8 +382,7 @@
     @Test
     public void testHideAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsGone() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
 
@@ -396,8 +396,7 @@
     @Test
     public void testHideAllKeyguardButtons_bottomEnabled_bottomNavButtonsVisible() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
 
@@ -411,8 +410,7 @@
     @Test
     public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_hasUnseen_setCorrectly() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
 
@@ -426,8 +424,7 @@
     @Test
     public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_noUnseen_setCorrectly() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
-        mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                mButtonSelectionStateController, () -> mHvacController);
+        mCarNavigationBar = createNavigationBarController();
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
 
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
index 04e0a73..0caa86f 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
@@ -89,6 +89,8 @@
     @Mock
     private ButtonSelectionStateListener mButtonSelectionStateListener;
     @Mock
+    private ButtonRoleHolderController mButtonRoleHolderController;
+    @Mock
     private IStatusBarService mBarService;
     @Mock
     private KeyguardStateController mKeyguardStateController;
@@ -137,8 +139,8 @@
                 mCarNavigationBarController, mLightBarController, mStatusBarIconController,
                 mWindowManager, mDeviceProvisionedController, new CommandQueue(mContext),
                 mAutoHideController, mButtonSelectionStateListener, mHandler, mUiBgExecutor,
-                mBarService, () -> mKeyguardStateController, mButtonSelectionStateController,
-                () -> mIconPolicy, () -> mIconController);
+                mBarService, () -> mKeyguardStateController, () -> mIconPolicy,
+                () -> mIconController);
     }
 
     @Test
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
index 11f2fa4..54282d3 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
@@ -180,6 +180,56 @@
     }
 
     @Test
+    public void onUnselected_withAppIcon_showsAppIcon() {
+        CarNavigationButton roleBasedButton = mTestView.findViewById(R.id.role_based_button);
+        Drawable appIcon = getContext().getDrawable(R.drawable.ic_android);
+
+        roleBasedButton.setSelected(false);
+        roleBasedButton.setAppIcon(appIcon);
+
+        Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+                R.id.car_nav_button_icon_image)).getDrawable();
+
+        assertThat(currentDrawable).isEqualTo(appIcon);
+    }
+
+    @Test
+    public void onUnselected_withAppIcon_applyUnselectedAlpha() {
+        CarNavigationButton roleBasedButton = mTestView.findViewById(R.id.role_based_button);
+
+        roleBasedButton.setSelected(false);
+        roleBasedButton.setAppIcon(getContext().getDrawable(R.drawable.ic_android));
+
+        assertThat(roleBasedButton.getAlpha()).isEqualTo(
+                CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
+    }
+
+    @Test
+    public void onSelected_withAppIcon_showsAppIconWithSelectedAlpha() {
+        CarNavigationButton roleBasedButton = mTestView.findViewById(R.id.role_based_button);
+        Drawable appIcon = getContext().getDrawable(R.drawable.ic_android);
+
+        roleBasedButton.setSelected(true);
+        roleBasedButton.setAppIcon(appIcon);
+
+        Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+                R.id.car_nav_button_icon_image)).getDrawable();
+
+        assertThat(currentDrawable).isEqualTo(appIcon);
+    }
+
+    @Test
+    public void onSelected_withAppIcon_applySelectedAlpha() {
+        CarNavigationButton roleBasedButton = mTestView.findViewById(R.id.role_based_button);
+
+        roleBasedButton.setSelected(true);
+        roleBasedButton.setAppIcon(getContext().getDrawable(R.drawable.ic_android));
+
+        assertThat(roleBasedButton.getAlpha()).isEqualTo(
+                CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+    }
+
+    @Test
     public void onClick_launchesIntentActivity() {
         mDefaultButton.performClick();
 
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 66787c2..35e2295 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Jou toestel moet herselflaai om hierdie verandering toe te pas. Herselflaai nou of kanselleer."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Werk-<xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Bedraade oorfoon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index ead0ebf..25cea4d 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"የእርስዎን መሣሪያ ይህ ለው ለማመልከት እንደገና መነሣት አለበት። አሁን እንደገና ያስነሡ ወይም ይተዉት።"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"የስራ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ባለገመድ ጆሮ ማዳመጫ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index c1f4fd9..bd94c40 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -558,6 +558,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"يجب إعادة تشغيل جهازك ليتم تطبيق هذا التغيير. يمكنك إعادة التشغيل الآن أو إلغاء التغيير."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> المخصّص للعمل"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"سمّاعة رأس سلكية"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index b5d642a..5572994 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই সলনিটো কার্যকৰী হ’বলৈ আপোনাৰ ডিভাইচটো ৰিবুট কৰিবই লাগিব। এতিয়াই ৰিবুট কৰক অথবা বাতিল কৰক।"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"কৰ্মস্থান <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"তাঁৰযুক্ত হেডফ\'ন"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index aecaf70..39ab078 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"İş <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Simli qulaqlıq"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 092bd42..78fd0bf 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -555,6 +555,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate da restartujete uređaj da bi se ova promena primenila. Restartujte ga odmah ili otkažite."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> za posao"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žičane slušalice"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index d610973..30a9e0e 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Перазагрузіце прыладу, каб прымяніць гэта змяненне. Перазагрузіце ці скасуйце."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (праца)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Правадныя навушнікі"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 5c3fe75..c8dd82f 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да бъде приложена тази промяна, устройството ви трябва да бъде рестартирано. Рестартирайте сега или анулирайте."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> за работа"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Слушалки с кабел"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 27d3707..02ca679 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই পরিবর্তনটি প্রয়োগ করার জন্য আপনার ডিভাইসটি অবশ্যই রিবুট করতে হবে। এখন রিবুট করুন বা বাতিল করুন।"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"অফিস <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"তার যুক্ত হেডফোন"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index a9d9e70..785460f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -555,6 +555,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate ponovo pokrenuti uređaj da se ova promjena primijeni. Ponovo pokrenite odmah ili otkažite."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Poslovna aplikacija <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žičane slušalice"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5ed0f1b..50c0374 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Has de reiniciar el teu dispositiu perquè s\'apliquin els canvis. Reinicia\'l ara o cancel·la."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de la feina"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculars amb cable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index d4d3735..9fb63ea 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aby se tato změna projevila, je třeba zařízení restartovat. Restartujte zařízení nebo zrušte akci."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Pracovní <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kabelová sluchátka"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 89dbf25..dc4f873 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at denne enhed bliver anvendt. Genstart nu, eller annuller."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> – arbejde"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Høretelefoner med ledning"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index c2030b1..bee2d79 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Damit diese Änderung übernommen wird, musst du dein Gerät neu starten. Du kannst es jetzt neu starten oder den Vorgang abbrechen."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (geschäftlich)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kabelgebundene Kopfhörer"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index bfc183e..b1d07ac 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Για να εφαρμοστεί αυτή η αλλαγή, θα πρέπει να επανεκκινήσετε τη συσκευή σας. Επανεκκίνηση τώρα ή ακύρωση."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Εργασία <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Ενσύρματα ακουστικά"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 9d869ba..e51456a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 9d869ba..e51456a 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 9d869ba..e51456a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 9d869ba..e51456a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 7765824..34ff568 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎Enabled‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎Your device must be rebooted for this change to apply. Reboot now or cancel.‎‏‎‎‏‎"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎Work ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎Wired headphone‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 6a76beb..fb5e9d7 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -153,7 +153,7 @@
     <string name="unknown" msgid="3544487229740637809">"Desconocido"</string>
     <string name="running_process_item_user_label" msgid="3988506293099805796">"Usuario: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="3631650616557252926">"Configuraciones predeterminadas establecidas"</string>
-    <string name="launch_defaults_none" msgid="8049374306261262709">"No se establecieron configuraciones predeterminadas"</string>
+    <string name="launch_defaults_none" msgid="8049374306261262709">"Sin configuraciones predeterminadas"</string>
     <string name="tts_settings" msgid="8130616705989351312">"Configuración de texto a voz"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"Salida de texto a voz"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"Velocidad de voz"</string>
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Debes reiniciar el dispositivo para que se aplique el cambio. Reinícialo ahora o cancela la acción."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de trabajo"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 010b85b..a6a89dd 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reiniciar ahora o cancelar."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de trabajo"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index ad2a561..65f1043 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Selle muudatuse rakendamiseks tuleb seade taaskäivitada. Taaskäivitage kohe või tühistage."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Töö: <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Juhtmega kõrvaklapid"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 1ceb738..6864db1 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aldaketa aplikatzeko, berrabiarazi egin behar da gailua. Berrabiaraz ezazu orain, edo utzi bertan behera."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Laneko <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Entzungailu kableduna"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index d3f1c56..5e13c1b 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"برای اعمال این تغییر، دستگاهتان باید راه‌اندازی مجدد شود. اکنون راه‌اندازی مجدد کنید یا لغو کنید."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> محل کار"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"هدفون سیمی"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 5ab01c4b..1ec71c7 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Laitteesi on käynnistettävä uudelleen, jotta muutos tulee voimaan. Käynnistä uudelleen nyt tai peruuta."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (työ)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Langalliset kuulokkeet"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 26e5042..0a32ca5 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Votre appareil doit être redémarré pour que ce changement prenne effet. Redémarrez-le maintenant ou annulez la modification."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (travail)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Écouteurs filaires"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index b8fc50d..bb069b5 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Vous devez redémarrer l\'appareil pour que cette modification soit appliquée. Redémarrez maintenant ou annulez l\'opération."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (travail)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Casque filaire"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8dc58ef..a27f2c4 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necesario reiniciar o teu dispositivo para aplicar este cambio. Reiníciao agora ou cancela o cambio."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Aplicación <xliff:g id="APP_NAME">%s</xliff:g> do traballo"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 9f69a2c..cf1e5ee 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"આ ફેરફારને લાગુ કરવા માટે તમારા ડિવાઇસને રીબૂટ કરવાની જરૂર છે. હમણાં જ રીબૂટ કરો કે રદ કરો."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ઑફિસ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"વાયરવાળો હૅડફોન"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index dc8e28b..89b7f67 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"बदली गई सेटिंग को लागू करने के लिए, अपने डिवाइस को फिर से चालू करें. डिवाइस को फिर से चालू करें या रद्द करें."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ऑफ़िस वाला <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"वायर वाला हेडफ़ोन"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f887839..f57a302 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -555,6 +555,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Uređaj se mora ponovno pokrenuti da bi se ta promjena primijenila. Ponovo pokrenite uređaj odmah ili odustanite."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> za posao"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žičane slušalice"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d7d2694..e8699dc 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Az eszközt újra kell indítani, hogy a módosítás megtörténjen. Indítsa újra most, vagy vesse el a módosítást."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Munkahelyi <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vezetékes fejhallgató"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index a5ef6b5..3f2b414 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Սարքն անհրաժեշտ է վերագործարկել, որպեսզի փոփոխությունը կիրառվի։ Վերագործարկեք հիմա կամ չեղարկեք փոփոխությունը։"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Աշխատանքային <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Լարով ականջակալ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 04ca2f6..dd1f999 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Perangkat Anda harus di-reboot agar perubahan ini diterapkan. Reboot sekarang atau batalkan."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> kerja"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Headphone berkabel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index ce60eb5..5dad63d 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Endurræsa þarf tækið til að þessi breyting taki gildi. Endurræstu núna eða hættu við."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> í vinnu"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Heyrnartól með snúru"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index bef644b..02b8533 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Devi riavviare il dispositivo per applicare questa modifica. Riavvia ora o annulla."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"App <xliff:g id="APP_NAME">%s</xliff:g> di lavoro"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Cuffie con cavo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index ed79632..af6a870 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"צריך להפעיל מחדש את המכשיר כדי להחיל את השינוי. יש להפעיל מחדש עכשיו או לבטל."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> של עבודה"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"אוזניות עם חוט"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 3f08a6a..080c1a9 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"この変更を適用するには、デバイスの再起動が必要です。今すぐ再起動してください。キャンセルすることもできます。"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"仕事の<xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有線ヘッドフォン"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 2ebc3ee..23de5b2 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ამ ცვლილების ასამოქმედებლად თქვენი მოწყობილობა უნდა გადაიტვირთოს. გადატვირთეთ ახლავე ან გააუქმეთ."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"სამსახურის <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"სადენიანი ყურსასმენი"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 91e5488..821c566 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бұл өзгеріс күшіне енуі үшін, құрылғыны қайта жүктеу керек. Қазір қайта жүктеңіз не бас тартыңыз."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (жұмыс)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Сымды құлақаспап"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 143f5f9..f93ab25 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ត្រូវតែ​ចាប់ផ្ដើម​ឧបករណ៍​របស់អ្នក​ឡើងវិញ ទើប​ការផ្លាស់ប្ដូរ​នេះ​ត្រូវបានអនុវត្ត​។ ចាប់ផ្ដើមឡើងវិញ​ឥឡូវនេះ ឬ​បោះបង់​។"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> សម្រាប់ការងារ"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"កាស​មានខ្សែ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 1df141c..e418779 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ಈ ಬದಲಾವಣೆ ಅನ್ವಯವಾಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ರೀಬೂಟ್ ಮಾಡಬೇಕು. ಇದೀಗ ರೀಬೂಟ್ ಮಾಡಿ ಅಥವಾ ರದ್ದುಗೊಳಿಸಿ."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ಉದ್ಯೋಗ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ವೈಯರ್ ಹೊಂದಿರುವ ಹೆಡ್‌ಫೋನ್"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 86e1650..6b273a3 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"변경사항을 적용하려면 기기를 재부팅해야 합니다. 지금 재부팅하거나 취소하세요."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"직장용 <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"유선 헤드폰"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 925f4a7..d035362 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бул өзгөртүүнү колдонуу үчүн түзмөктү өчүрүп күйгүзүңүз. Азыр өчүрүп күйгүзүңүз же жокко чыгарыңыз."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Жумуш <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Зымдуу гарнитура"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 3bf1996..e188f3d 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ທ່ານຕ້ອງປິດເປີດອຸປະກອນຄືນໃໝ່ເພື່ອນຳໃຊ້ການປ່ຽນແປງນີ້. ປິດເປີດໃໝ່ດຽວນີ້ ຫຼື ຍົກເລີກ."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"​ບ່ອນ​ເຮັດ​ວຽກ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ຫູຟັງແບບມີສາຍ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 153c995..075032c 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kad pakeitimas būtų pritaikytas, įrenginį reikia paleisti iš naujo. Dabar paleiskite iš naujo arba atšaukite."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Darbo „<xliff:g id="APP_NAME">%s</xliff:g>“"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Laidinės ausinės"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index f6e7f35..c37811f 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -555,6 +555,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Lai šīs izmaiņas tiktu piemērotas, nepieciešama ierīces atkārtota palaišana. Atkārtoti palaidiet to tūlīt vai atceliet izmaiņas."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Darbā: <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vadu austiņas"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index d55f1af..1f5908c 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да се примени променава, уредот мора да се рестартира. Рестартирајте сега или откажете."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Работна <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Жичени слушалки"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index c5267d6..6ee9777 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ഈ മാറ്റം ബാധകമാകുന്നതിന് നിങ്ങളുടെ ഉപകരണം റീബൂട്ട് ചെയ്യേണ്ടതുണ്ട്. ഇപ്പോൾ റീബൂട്ട് ചെയ്യുകയോ റദ്ദാക്കുകയോ ചെയ്യുക."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ഔദ്യോഗികം <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഹെഡ്ഫോൺ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 0a01f84..29647df 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл болино уу."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Ажлын <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Утастай чихэвч"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 075c748..99bf1e8 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"हा बदल लागू करण्यासाठी तुमचे डिव्हाइस रीबूट करणे आवश्यक आहे. आता रीबूट करा किंवा रद्द करा."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"कार्य <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"वायर असलेला हेडफोन"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index cdf32a7..bdecf64 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Peranti anda mesti dibut semula supaya perubahan ini berlaku. But semula sekarang atau batalkan."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Kerja <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Fon kepala berwayar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 2bd3b45..1519380 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ဤအပြောင်းအလဲ ထည့်သွင်းရန် သင့်စက်ကို ပြန်လည်စတင်ရမည်။ ယခု ပြန်လည်စတင်ပါ သို့မဟုတ် ပယ်ဖျက်ပါ။"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"အလုပ် <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ကြိုးတပ်နားကြပ်"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index de5bed6..a2f862e 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten din må startes på nytt for at denne endringen skal tre i kraft. Start på nytt nå eller avbryt."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Jobb-<xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Hodetelefoner med kabel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index bfe295a..159050e 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"यो परिवर्तन लागू गर्न तपाईंको यन्त्र अनिवार्य रूपमा रिबुट गर्नु पर्छ। अहिले रिबुट गर्नुहोस् वा रद्द गर्नुहोस्।"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"कार्यालयको प्रोफाइल <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"तारसहितको हेडफोन"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index a822719..7e3005b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ingeschakeld"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Je apparaat moet opnieuw worden opgestart om deze wijziging toe te passen. Start nu opnieuw op of annuleer de wijziging."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> voor werk"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Bedrade hoofdtelefoon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index ab9fbc3..19e250b 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ଏହି ପରିବର୍ତ୍ତନ ଲାଗୁ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ନିଶ୍ଚିତ ରୂପେ ରିବୁଟ୍ କରାଯିବା ଆବଶ୍ୟକ। ବର୍ତ୍ତମାନ ରିବୁଟ୍ କରନ୍ତୁ କିମ୍ବା ବାତିଲ୍ କରନ୍ତୁ।"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ୱାର୍କ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ତାରଯୁକ୍ତ ହେଡଫୋନ୍"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 35d8cba..184f4cd 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ। ਹੁਣੇ ਰੀਬੂਟ ਕਰੋ ਜਾਂ ਰੱਦ ਕਰੋ।"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ਕੰਮ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ਤਾਰ ਵਾਲੇ ਹੈੱਡਫ਼ੋਨ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 54ed131..eebea5f 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Wprowadzenie zmiany wymaga ponownego uruchomienia urządzenia. Uruchom ponownie teraz lub anuluj."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (do pracy)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Słuchawki przewodowe"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index c6dc1d3..142ef47 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"App <xliff:g id="APP_NAME">%s</xliff:g> de trabalho"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Fones de ouvido com fio"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 999e684..0c6f488 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reiniciar o dispositivo para aplicar esta alteração. Reinicie agora ou cancele."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de trabalho"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auscultadores com fios"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index c6dc1d3..142ef47 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"App <xliff:g id="APP_NAME">%s</xliff:g> de trabalho"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Fones de ouvido com fio"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index ba2f36f..036cf83 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -555,6 +555,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să reporniți dispozitivul. Reporniți-l acum sau anulați."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de serviciu"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Căști cu fir"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 8ec3875..6b39754 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Чтобы изменение вступило в силу, необходимо перезапустить устройство. Вы можете сделать это сейчас или позже."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Рабочее приложение \"<xliff:g id="APP_NAME">%s</xliff:g>\""</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Проводные наушники"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 01c3634..9074c63 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"මෙම වෙනස යෙදීමට ඔබේ උපාංගය නැවත පණ ගැන්විය යුතුය. දැන් නැවත පණ ගන්වන්න හෝ අවලංගු කරන්න."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"කාර්යාල <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"රැහැන්ගත කළ හෙඩ්ෆෝන්"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index b5cbf43..cd3109f 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Táto zmena sa uplatní až po reštartovaní zariadenia. Zariadenie reštartujte alebo zmenu zrušte."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Pracovná aplikácia <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Slúchadlá s káblom"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 86cccb8..0e492c7 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Napravo je treba znova zagnati, da bo ta sprememba uveljavljena. Znova zaženite zdaj ali prekličite."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> za delo"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žične slušalke"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index bd3353c..4a80bcd 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pajisja jote duhet të riniset që ky ndryshim të zbatohet. Rinise tani ose anuloje."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> për punën"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kufje me tela"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 29f23b4..321645e 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -555,6 +555,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Морате да рестартујете уређај да би се ова промена применила. Рестартујте га одмах или откажите."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> за посао"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Жичане слушалице"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index a40100e..cd28c86b 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten måste startas om för att ändringen ska börja gälla. Starta om nu eller avbryt."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> för arbetet"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Hörlurar med sladd"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index fedf1c8..2bc038e 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Ya kazini <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vipokea sauti vyenye waya vinavyobanwa kichwani"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 345f0a7..a28fdce 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"இந்த மாற்றங்கள் செயல்படுத்தப்பட உங்கள் சாதனத்தை மறுபடி தொடங்க வேண்டும். இப்போதே மறுபடி தொடங்கவும் அல்லது ரத்துசெய்யவும்."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"பணியிடம் <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"வயருள்ள ஹெட்ஃபோன்"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c4fda01..b7bb6e8 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ఈ మార్పును వర్తింపజేయాలంటే మీరు మీ పరికరాన్ని తప్పనిసరిగా రీబూట్ చేయాలి. ఇప్పుడే రీబూట్ చేయండి లేదా రద్దు చేయండి."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ఆఫీసు <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"వైర్ ఉన్న హెడ్‌ఫోన్"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index a9032eb..2f9ac46 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"คุณต้องรีบูตอุปกรณ์เพื่อให้การเปลี่ยนแปลงนี้มีผล รีบูตเลยหรือยกเลิก"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> ในโปรไฟล์งาน"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"หูฟังแบบมีสาย"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 712c06b..5064c1e 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Dapat i-reboot ang iyong device para mailapat ang pagbabagong ito. Mag-reboot ngayon o kanselahin."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> sa Trabaho"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired na headphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 8818498..454ca3b 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazının yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (İş)"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kablolu kulaklık"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index f6568e3..7e969f7 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -556,6 +556,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Щоб застосувати ці зміни, перезапустіть пристрій. Перезапустіть пристрій або скасуйте зміни."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Робочий додаток <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Дротові навушники"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index f13f8fb..e3b28c7 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"اس تبدیلی کو لاگو کرنے کے ليے آپ کے آلہ کو ریبوٹ کرنا ضروری ہے۔ ابھی ریبوٹ کریں یا منسوخ کریں۔"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"دفتر <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"وائرڈ ہیڈ فون"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 7b8f627..04707c5 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar qurilma oʻchib yonganda bajariladi. Hoziroq oʻchib yoqish yoki bekor qilish."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Ish <xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Simli quloqlik"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 35cecc1..e7fbf46 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bạn phải khởi động lại thiết bị để áp dụng sự thay đổi này. Hãy khởi động lại ngay hoặc hủy."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> dành cho công việc"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Tai nghe có dây"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index c69ea2d..bda7d8b 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"设备必须重新启动才能应用此更改。您可以立即重新启动或取消。"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"工作资料中的<xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有线耳机"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index b0324d9..841de63 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"您的裝置必須重新開機,才能套用此變更。請立即重新開機或取消。"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"工作設定檔入面嘅「<xliff:g id="APP_NAME">%s</xliff:g>」"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有線耳機"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 0574c7e..a6c1647 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"裝置必須重新啟動才能套用這項變更。請立即重新啟動或取消變更。"</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"工作資料夾中的<xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有線耳機"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index b60553a..c437580 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -554,6 +554,5 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kufanele idivayisi yakho iqaliswe ukuze lolu shintsho lusebenze. Qalisa manje noma khansela."</string>
     <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Umsebenzi we-<xliff:g id="APP_NAME">%s</xliff:g>"</string>
-    <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) -->
-    <skip />
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Ama-headphone anentambo"</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index d3f9cd4..9d1b3cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -7,6 +7,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.MediaStore;
@@ -132,6 +133,44 @@
      */
     public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
             CachedBluetoothDevice cachedDevice) {
+        final Resources resources = context.getResources();
+        final Pair<Drawable, String> pair = BluetoothUtils.getBtDrawableWithDescription(context,
+                cachedDevice);
+
+        if (pair.first instanceof BitmapDrawable) {
+            return new Pair<>(new AdaptiveOutlineDrawable(
+                    resources, ((BitmapDrawable) pair.first).getBitmap()), pair.second);
+        }
+
+        return new Pair<>(buildBtRainbowDrawable(context,
+                pair.first, cachedDevice.getAddress().hashCode()), pair.second);
+    }
+
+    /**
+     * Build Bluetooth device icon with rainbow
+     */
+    public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
+            int hashCode) {
+        final Resources resources = context.getResources();
+
+        // Deal with normal headset
+        final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
+        final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
+
+        // get color index based on mac address
+        final int index = Math.abs(hashCode % iconBgColors.length);
+        drawable.setTint(iconFgColors[index]);
+        final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
+        ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
+
+        return adaptiveIcon;
+    }
+
+    /**
+     * Get bluetooth icon with description
+     */
+    public static Pair<Drawable, String> getBtDrawableWithDescription(Context context,
+            CachedBluetoothDevice cachedDevice) {
         final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
                 context, cachedDevice);
         final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
@@ -159,9 +198,8 @@
                         final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
                                 iconSize, false);
                         bitmap.recycle();
-                        final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(
-                                resources, resizedBitmap);
-                        return new Pair<>(drawable, pair.second);
+                        return new Pair<>(new BitmapDrawable(resources,
+                                resizedBitmap), pair.second);
                     }
                 } catch (IOException e) {
                     Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
@@ -171,28 +209,7 @@
             }
         }
 
-        return new Pair<>(buildBtRainbowDrawable(context,
-                pair.first, cachedDevice.getAddress().hashCode()), pair.second);
-    }
-
-    /**
-     * Build Bluetooth device icon with rainbow
-     */
-    public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
-            int hashCode) {
-        final Resources resources = context.getResources();
-
-        // Deal with normal headset
-        final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
-        final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
-
-        // get color index based on mac address
-        final int index = Math.abs(hashCode % iconBgColors.length);
-        drawable.setTint(iconFgColors[index]);
-        final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
-        ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
-
-        return adaptiveIcon;
+        return new Pair<>(pair.first, pair.second);
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 74a5939..a2f77e2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -63,6 +63,11 @@
     }
 
     @Override
+    public Drawable getIconWithoutBackground() {
+        return BluetoothUtils.getBtDrawableWithDescription(mContext, mCachedDevice).first;
+    }
+
+    @Override
     public String getId() {
         return MediaDeviceUtils.getId(mCachedDevice);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 83a9671..22dc906 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -60,6 +60,11 @@
                 mContext.getDrawable(getDrawableResId()), getId().hashCode());
     }
 
+    @Override
+    public Drawable getIconWithoutBackground() {
+        return mContext.getDrawable(getDrawableResId());
+    }
+
     @VisibleForTesting
     int getDrawableResId() {
         int resId;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 008a433..959c42fe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -56,7 +56,7 @@
 public class InfoMediaManager extends MediaManager {
 
     private static final String TAG = "InfoMediaManager";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);;
     @VisibleForTesting
     final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
     @VisibleForTesting
@@ -364,8 +364,8 @@
     private void buildAvailableRoutes() {
         for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
             if (DEBUG) {
-                Log.d(TAG, "buildAvailableRoutes() route : " + route.getName()
-                        + ", type : " + route.getType());
+                Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : "
+                        + route.getVolume() + ", type : " + route.getType());
             }
             addMediaDevice(route);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 7d95f19..f34903c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -394,7 +394,9 @@
         return mPackageName;
     }
 
-    private MediaDevice updateCurrentConnectedDevice() {
+    @VisibleForTesting
+    MediaDevice updateCurrentConnectedDevice() {
+        MediaDevice connectedDevice = null;
         synchronized (mMediaDevicesLock) {
             for (MediaDevice device : mMediaDevices) {
                 if (device instanceof BluetoothMediaDevice) {
@@ -402,12 +404,12 @@
                         return device;
                     }
                 } else if (device instanceof PhoneMediaDevice) {
-                    return device;
+                    connectedDevice = device;
                 }
             }
         }
-        Log.w(TAG, "updateCurrentConnectedDevice() can't found current connected device");
-        return null;
+
+        return connectedDevice;
     }
 
     private boolean isActiveDevice(CachedBluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 8a178a9..317077b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -153,6 +153,13 @@
     public abstract Drawable getIcon();
 
     /**
+     * Get icon of MediaDevice without background.
+     *
+     * @return drawable of icon
+     */
+    public abstract Drawable getIconWithoutBackground();
+
+    /**
      * Get unique ID that represent MediaDevice
      * @return unique id of MediaDevice
      */
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 9a3ae1b..c43a512 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -89,6 +89,11 @@
                 mContext.getDrawable(getDrawableResId()), getId().hashCode());
     }
 
+    @Override
+    public Drawable getIconWithoutBackground() {
+        return mContext.getDrawable(getDrawableResId());
+    }
+
     @VisibleForTesting
     int getDrawableResId() {
         int resId;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 734866f..c514671 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -590,7 +590,7 @@
         final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
         assertThat(mediaDevice).isNull();
 
-        mInfoMediaManager.mMediaRouterCallback.onTransferred(null, null);
+        mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
 
         final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
         assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
@@ -602,6 +602,7 @@
     @Test
     public void onTransferred_buildAllRoutes_shouldAddMediaDevice() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
         mInfoMediaManager.registerCallback(mCallback);
 
         when(info.getId()).thenReturn(TEST_ID);
@@ -616,7 +617,7 @@
         assertThat(mediaDevice).isNull();
 
         mInfoMediaManager.mPackageName = "";
-        mInfoMediaManager.mMediaRouterCallback.onTransferred(null, null);
+        mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
 
         final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
         assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 517071b..009f75a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -31,6 +31,7 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
@@ -725,4 +726,44 @@
 
         verify(mInfoMediaManager).adjustSessionVolume(info, 10);
     }
+
+    @Test
+    public void updateCurrentConnectedDevice_bluetoothDeviceIsActive_returnBluetoothDevice() {
+        final BluetoothMediaDevice device1 = mock(BluetoothMediaDevice.class);
+        final BluetoothMediaDevice device2 = mock(BluetoothMediaDevice.class);
+        final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
+        final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
+        final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
+
+        when(device1.getCachedDevice()).thenReturn(cachedDevice1);
+        when(device2.getCachedDevice()).thenReturn(cachedDevice2);
+        when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
+        when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
+
+        mLocalMediaManager.mMediaDevices.add(device1);
+        mLocalMediaManager.mMediaDevices.add(phoneDevice);
+        mLocalMediaManager.mMediaDevices.add(device2);
+
+        assertThat(mLocalMediaManager.updateCurrentConnectedDevice()).isEqualTo(device2);
+    }
+
+    @Test
+    public void updateCurrentConnectedDevice_phoneDeviceIsActive_returnPhoneDevice() {
+        final BluetoothMediaDevice device1 = mock(BluetoothMediaDevice.class);
+        final BluetoothMediaDevice device2 = mock(BluetoothMediaDevice.class);
+        final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
+        final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
+        final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
+
+        when(device1.getCachedDevice()).thenReturn(cachedDevice1);
+        when(device2.getCachedDevice()).thenReturn(cachedDevice2);
+        when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
+        when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
+
+        mLocalMediaManager.mMediaDevices.add(device1);
+        mLocalMediaManager.mMediaDevices.add(phoneDevice);
+        mLocalMediaManager.mMediaDevices.add(device2);
+
+        assertThat(mLocalMediaManager.updateCurrentConnectedDevice()).isEqualTo(phoneDevice);
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 3d7559b..aae72e5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1131,6 +1131,10 @@
                 Settings.Global.NSD_ON,
                 GlobalSettingsProto.NSD_ON);
 
+        dumpSetting(s, p,
+                Settings.Global.NR_NSA_TRACKING_SCREEN_OFF_MODE,
+                GlobalSettingsProto.NR_NSA_TRACKING_SCREEN_OFF_MODE);
+
         final long ntpToken = p.start(GlobalSettingsProto.NTP);
         dumpSetting(s, p,
                 Settings.Global.NTP_SERVER,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e537b76..fa87b62 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -391,6 +391,7 @@
                     Settings.Global.NITZ_UPDATE_DIFF,
                     Settings.Global.NITZ_UPDATE_SPACING,
                     Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+                    Settings.Global.NR_NSA_TRACKING_SCREEN_OFF_MODE,
                     Settings.Global.NSD_ON,
                     Settings.Global.NTP_SERVER,
                     Settings.Global.NTP_TIMEOUT,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 055b2be..985269b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -607,7 +607,7 @@
                   android:excludeFromRecents="true"
                   android:stateNotNeeded="true"
                   android:resumeWhilePausing="true"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
+                  android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
             <intent-filter>
                 <action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index c8cf7f50..28491d6 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -41,6 +41,7 @@
 tsuji@google.com
 twickham@google.com
 winsonc@google.com
+zakcohen@google.com
 
 #Android Auto
 stenning@google.com
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 52ace2b..8102043 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -573,6 +573,7 @@
         <item name="android:textColor">?attr/wallpaperTextColor</item>
         <item name="android:textAllCaps">false</item>
         <item name="android:textSize">14sp</item>
+        <item name="android:minWidth">0dp</item>
     </style>
 
     <style name="TextAppearance.HeadsUpStatusBarText"
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 2587369..eb4ba6f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -776,6 +776,10 @@
         }
     }
 
+    boolean inLandscape() {
+        return mOrientation == Configuration.ORIENTATION_LANDSCAPE;
+    }
+
     /**
      * Set a listener to be notified of bubble expand events.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 7697400..c4b4f43 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -226,8 +226,12 @@
     public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        updateDimensions();
+    }
+
+    void updateDimensions() {
         mDisplaySize = new Point();
-        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
         mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
         Resources res = getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index f4eb580..af6e66a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -61,9 +61,7 @@
     }
 
     void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
-        mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
-        mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
-                R.dimen.bubble_overflow_icon_bitmap_size);
+        updateDimensions();
 
         mExpandedView = (BubbleExpandedView) mInflater.inflate(
                 R.layout.bubble_expanded_view, parentViewGroup /* root */,
@@ -74,6 +72,15 @@
         updateIcon(mContext, parentViewGroup);
     }
 
+    void updateDimensions() {
+        mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
+        mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
+                R.dimen.bubble_overflow_icon_bitmap_size);
+        if (mExpandedView != null) {
+            mExpandedView.updateDimensions();
+        }
+    }
+
     void updateIcon(Context context, ViewGroup parentViewGroup) {
         mContext = context;
         mInflater = LayoutInflater.from(context);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 08ec789..8fec338 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -70,6 +70,9 @@
         }
         @Override
         public boolean canScrollVertically() {
+            if (mBubbleController.inLandscape()) {
+                return super.canScrollVertically();
+            }
             return false;
         }
     }
@@ -89,6 +92,14 @@
         mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
         mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image);
 
+        updateDimensions();
+        onDataChanged(mBubbleController.getOverflowBubbles());
+        mBubbleController.setOverflowCallback(() -> {
+            onDataChanged(mBubbleController.getOverflowBubbles());
+        });
+    }
+
+    void updateDimensions() {
         Resources res = getResources();
         final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
         mRecyclerView.setLayoutManager(
@@ -96,8 +107,9 @@
 
         DisplayMetrics displayMetrics = new DisplayMetrics();
         getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
-        final int recyclerViewWidth = (displayMetrics.widthPixels
-                - res.getDimensionPixelSize(R.dimen.bubble_overflow_padding));
+
+        final int overflowPadding = res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
+        final int recyclerViewWidth = displayMetrics.widthPixels - (overflowPadding * 2);
         final int viewWidth = recyclerViewWidth / columns;
 
         final int maxOverflowBubbles = res.getInteger(R.integer.bubbles_max_overflow);
@@ -109,17 +121,12 @@
         mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
                 mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
         mRecyclerView.setAdapter(mAdapter);
-        onDataChanged(mBubbleController.getOverflowBubbles());
-        mBubbleController.setOverflowCallback(() -> {
-            onDataChanged(mBubbleController.getOverflowBubbles());
-        });
-        onThemeChanged();
     }
 
     /**
      * Handle theme changes.
      */
-    void onThemeChanged() {
+    void updateTheme() {
         final int mode =
                 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
         switch (mode) {
@@ -178,7 +185,8 @@
     @Override
     public void onResume() {
         super.onResume();
-        onThemeChanged();
+        updateDimensions();
+        updateTheme();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index ec45f93..bdfcb35 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -32,6 +32,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.app.Notification;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -49,6 +50,7 @@
 import android.graphics.Region;
 import android.os.Bundle;
 import android.os.Vibrator;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.Choreographer;
@@ -763,9 +765,13 @@
         targetView.setTranslationY(
                 getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
 
+        final ContentResolver contentResolver = getContext().getContentResolver();
+        final int dismissRadius = Settings.Secure.getInt(
+                contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
+
         // Save the MagneticTarget instance for the newly set up view - we'll add this to the
         // MagnetizedObjects.
-        mMagneticTarget = new MagnetizedObject.MagneticTarget(targetView, mBubbleSize * 2);
+        mMagneticTarget = new MagnetizedObject.MagneticTarget(targetView, dismissRadius);
 
         mExpandedViewXAnim =
                 new SpringAnimation(mExpandedViewContainer, DynamicAnimation.TRANSLATION_X);
@@ -1027,6 +1033,7 @@
             mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
         } else {
             mBubbleContainer.removeView(mBubbleOverflow.getBtn());
+            mBubbleOverflow.updateDimensions();
             mBubbleOverflow.updateIcon(mContext,this);
             overflowBtnIndex = mBubbleContainer.getChildCount();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index d52c35b..fd1f879 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -16,10 +16,12 @@
 
 package com.android.systemui.bubbles.animation;
 
+import android.content.ContentResolver;
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
 import android.view.WindowInsets;
@@ -83,8 +85,7 @@
      * screen, we want less friction horizontally so that the stack has a better chance of making it
      * to the side without needing a spring.
      */
-    private static final float FLING_FRICTION_X = 2.2f;
-    private static final float FLING_FRICTION_Y = 2.2f;
+    private static final float FLING_FRICTION = 2.2f;
 
     /**
      * Values to use for the stack spring animation used to spring the stack to its final position
@@ -363,18 +364,26 @@
             return destinationRelativeX;
         }
 
+        final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
+        final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
+                SPRING_AFTER_FLING_STIFFNESS /* default */);
+        final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
+                SPRING_AFTER_FLING_DAMPING_RATIO);
+        final float friction = Settings.Secure.getFloat(contentResolver, "bubble_friction",
+                FLING_FRICTION);
+
         // Minimum velocity required for the stack to make it to the targeted side of the screen,
         // taking friction into account (4.2f is the number that friction scalars are multiplied by
         // in DynamicAnimation.DragForce). This is an estimate - it could possibly be slightly off,
         // but the SpringAnimation at the end will ensure that it reaches the destination X
         // regardless.
         final float minimumVelocityToReachEdge =
-                (destinationRelativeX - x) * (FLING_FRICTION_X * 4.2f);
+                (destinationRelativeX - x) * (friction * 4.2f);
 
         final float estimatedY = PhysicsAnimator.estimateFlingEndValue(
                 mStackPosition.y, velY,
                 new PhysicsAnimator.FlingConfig(
-                        FLING_FRICTION_Y, stackBounds.top, stackBounds.bottom));
+                        friction, stackBounds.top, stackBounds.bottom));
 
         notifyFloatingCoordinatorStackAnimatingTo(destinationRelativeX, estimatedY);
 
@@ -384,22 +393,24 @@
                 ? Math.min(minimumVelocityToReachEdge, velX)
                 : Math.max(minimumVelocityToReachEdge, velX);
 
+
+
         flingThenSpringFirstBubbleWithStackFollowing(
                 DynamicAnimation.TRANSLATION_X,
                 startXVelocity,
-                FLING_FRICTION_X,
+                friction,
                 new SpringForce()
-                        .setStiffness(SPRING_AFTER_FLING_STIFFNESS)
-                        .setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO),
+                        .setStiffness(stiffness)
+                        .setDampingRatio(dampingRatio),
                 destinationRelativeX);
 
         flingThenSpringFirstBubbleWithStackFollowing(
                 DynamicAnimation.TRANSLATION_Y,
                 velY,
-                FLING_FRICTION_Y,
+                friction,
                 new SpringForce()
-                        .setStiffness(SPRING_AFTER_FLING_STIFFNESS)
-                        .setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO),
+                        .setStiffness(stiffness)
+                        .setDampingRatio(dampingRatio),
                 /* destination */ null);
 
         // If we're flinging now, there's no more touch event to catch up to.
@@ -761,9 +772,15 @@
 
     @Override
     SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
+        final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
+        final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
+                mIsMovingFromFlinging ? FLING_FOLLOW_STIFFNESS : DEFAULT_STIFFNESS /* default */);
+        final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
+                DEFAULT_BOUNCINESS);
+
         return new SpringForce()
-                .setDampingRatio(DEFAULT_BOUNCINESS)
-                .setStiffness(mIsMovingFromFlinging ? FLING_FOLLOW_STIFFNESS : DEFAULT_STIFFNESS);
+                .setDampingRatio(dampingRatio)
+                .setStiffness(stiffness);
     }
 
     @Override
@@ -1011,6 +1028,21 @@
             mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
         }
 
+        final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
+        final float minVelocity = Settings.Secure.getFloat(contentResolver,
+                "bubble_dismiss_fling_min_velocity",
+                mMagnetizedStack.getFlingToTargetMinVelocity() /* default */);
+        final float maxVelocity = Settings.Secure.getFloat(contentResolver,
+                "bubble_dismiss_stick_max_velocity",
+                mMagnetizedStack.getStickToTargetMaxVelocity() /* default */);
+        final float targetWidth = Settings.Secure.getFloat(contentResolver,
+                "bubble_dismiss_target_width_percent",
+                mMagnetizedStack.getFlingToTargetWidthPercent() /* default */);
+
+        mMagnetizedStack.setFlingToTargetMinVelocity(minVelocity);
+        mMagnetizedStack.setStickToTargetMaxVelocity(maxVelocity);
+        mMagnetizedStack.setFlingToTargetWidthPercent(targetWidth);
+
         return mMagnetizedStack;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 181170b..f8f913e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -72,6 +72,9 @@
         private const val USER_CHANGE_RETRY_DELAY = 500L // ms
         private const val DEFAULT_ENABLED = 1
         private const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
+
+        private fun isAvailable(userId: Int, cr: ContentResolver) = Settings.Secure.getIntForUser(
+            cr, CONTROLS_AVAILABLE, DEFAULT_ENABLED, userId) != 0
     }
 
     private var userChanging: Boolean = true
@@ -85,8 +88,7 @@
 
     private val contentResolver: ContentResolver
         get() = context.contentResolver
-    override var available = Settings.Secure.getIntForUser(
-            contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED, currentUserId) != 0
+    override var available = isAvailable(currentUserId, contentResolver)
         private set
 
     private val persistenceWrapper: ControlsFavoritePersistenceWrapper
@@ -119,8 +121,7 @@
                 BackupManager(userStructure.userContext)
         )
         auxiliaryPersistenceWrapper.changeFile(userStructure.auxiliaryFile)
-        available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
-                DEFAULT_ENABLED, newUser.identifier) != 0
+        available = isAvailable(newUser.identifier, contentResolver)
         resetFavorites(available)
         bindingController.changeUser(newUser)
         listingController.changeUser(newUser)
@@ -131,7 +132,6 @@
         override fun onReceive(context: Context, intent: Intent) {
             if (intent.action == Intent.ACTION_USER_SWITCHED) {
                 userChanging = true
-                listingController.removeCallback(listingCallback)
                 val newUser =
                         UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId))
                 if (currentUser == newUser) {
@@ -151,7 +151,6 @@
                 executor.execute {
                     Log.d(TAG, "Restore finished, storing auxiliary favorites")
                     auxiliaryPersistenceWrapper.initialize()
-                    listingController.removeCallback(listingCallback)
                     persistenceWrapper.storeFavorites(auxiliaryPersistenceWrapper.favorites)
                     resetFavorites(available)
                 }
@@ -172,8 +171,7 @@
             if (userChanging || userId != currentUserId) {
                 return
             }
-            available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
-                DEFAULT_ENABLED, currentUserId) != 0
+            available = isAvailable(currentUserId, contentResolver)
             resetFavorites(available)
         }
     }
@@ -244,6 +242,7 @@
             null
         )
         contentResolver.registerContentObserver(URI, false, settingObserver, UserHandle.USER_ALL)
+        listingController.addCallback(listingCallback)
     }
 
     fun destroy() {
@@ -258,7 +257,6 @@
 
         if (shouldLoad) {
             Favorites.load(persistenceWrapper.readFavorites())
-            listingController.addCallback(listingCallback)
         }
     }
 
@@ -569,12 +567,12 @@
     val userContext = context.createContextAsUser(user, 0)
 
     val file = Environment.buildPath(
-            context.filesDir,
+            userContext.filesDir,
             ControlsFavoritePersistenceWrapper.FILE_NAME
     )
 
     val auxiliaryFile = Environment.buildPath(
-            context.filesDir,
+            userContext.filesDir,
             AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index a2adcf9..1cd9712 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.dagger.qualifiers.Background
 import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -75,6 +76,7 @@
 
     private var availableComponents = emptySet<ComponentName>()
     private var availableServices = emptyList<ServiceInfo>()
+    private var userChangeInProgress = AtomicInteger(0)
 
     override var currentUserId = ActivityManager.getCurrentUser()
         private set
@@ -85,6 +87,7 @@
             newServices.mapTo(mutableSetOf<ComponentName>(), { s -> s.getComponentName() })
 
         backgroundExecutor.execute {
+            if (userChangeInProgress.get() > 0) return@execute
             if (!newComponents.equals(availableComponents)) {
                 Log.d(TAG, "ServiceConfig reloaded, count: ${newComponents.size}")
                 availableComponents = newComponents
@@ -105,22 +108,18 @@
     }
 
     override fun changeUser(newUser: UserHandle) {
+        userChangeInProgress.incrementAndGet()
+        serviceListing.setListening(false)
+
         backgroundExecutor.execute {
-            serviceListing.setListening(false)
-
-            // Notify all callbacks in order to clear their existing state prior to attaching
-            // a new listener
-            availableServices = emptyList()
-            callbacks.forEach {
-                it.onServicesUpdated(emptyList())
+            if (userChangeInProgress.decrementAndGet() == 0) {
+                currentUserId = newUser.identifier
+                val contextForUser = context.createContextAsUser(newUser, 0)
+                serviceListing = serviceListingBuilder(contextForUser)
+                serviceListing.addCallback(serviceListingCallback)
+                serviceListing.setListening(true)
+                serviceListing.reload()
             }
-
-            currentUserId = newUser.identifier
-            val contextForUser = context.createContextAsUser(newUser, 0)
-            serviceListing = serviceListingBuilder(contextForUser)
-            serviceListing.addCallback(serviceListingCallback)
-            serviceListing.setListening(true)
-            serviceListing.reload()
         }
     }
 
@@ -134,10 +133,16 @@
      */
     override fun addCallback(listener: ControlsListingController.ControlsListingCallback) {
         backgroundExecutor.execute {
-            val services = getCurrentServices()
-            Log.d(TAG, "Subscribing callback, service count: ${services.size}")
-            callbacks.add(listener)
-            listener.onServicesUpdated(services)
+            if (userChangeInProgress.get() > 0) {
+                // repost this event, as callers may rely on the initial callback from
+                // onServicesUpdated
+                addCallback(listener)
+            } else {
+                val services = getCurrentServices()
+                Log.d(TAG, "Subscribing callback, service count: ${services.size}")
+                callbacks.add(listener)
+                listener.onServicesUpdated(services)
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 23bcb29..8368b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -96,12 +96,6 @@
         return new AmbientDisplayConfiguration(context);
     }
 
-    /** */
-    @Provides
-    public Handler provideHandler() {
-        return new Handler();
-    }
-
     @Singleton
     @Provides
     public DataSaverController provideDataSaverController(NetworkController networkController) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 3a8212c..58aad86 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -28,6 +28,7 @@
 import android.app.NotificationManager;
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
 import android.app.trust.TrustManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -314,4 +315,9 @@
         return context.getSystemService(WindowManager.class);
     }
 
+    @Provides
+    @Singleton
+    static RoleManager provideRoleManager(Context context) {
+        return context.getSystemService(RoleManager.class);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 6572937..95a9006 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -28,6 +28,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.FalsingManager;
@@ -69,7 +70,7 @@
             WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor,
             DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
             ProximitySensor proximitySensor,
-            DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
+            DelayedWakeLock.Builder delayedWakeLockBuilder, @Main Handler handler,
             DelayableExecutor delayableExecutor,
             BiometricUnlockController biometricUnlockController,
             BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ac289cb..61c9a96 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -129,6 +129,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
+import com.android.systemui.settings.CurrentUserContextTracker;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -238,10 +239,10 @@
     private final Executor mBackgroundExecutor;
     private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
     private ControlsController mControlsController;
-    private SharedPreferences mControlsPreferences;
     private final RingerModeTracker mRingerModeTracker;
     private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
     private Handler mMainHandler;
+    private CurrentUserContextTracker mCurrentUserContextTracker;
     @VisibleForTesting
     boolean mShowLockScreenCardsAndControls = false;
 
@@ -301,7 +302,8 @@
             @Background Executor backgroundExecutor,
             ControlsListingController controlsListingController,
             ControlsController controlsController, UiEventLogger uiEventLogger,
-            RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
+            RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
+            CurrentUserContextTracker currentUserContextTracker) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -330,6 +332,7 @@
         mControlsController = controlsController;
         mSysUiState = sysUiState;
         mMainHandler = handler;
+        mCurrentUserContextTracker = currentUserContextTracker;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -382,12 +385,6 @@
 
         controlsListingController.addCallback(list -> mControlsServiceInfos = list);
 
-        // Need to be user-specific with the context to make sure we read the correct prefs
-        Context userContext = context.createContextAsUser(
-                new UserHandle(mUserManager.getUserHandle()), 0);
-        mControlsPreferences = userContext.getSharedPreferences(PREFS_CONTROLS_FILE,
-            Context.MODE_PRIVATE);
-
         // Listen for changes to show controls on the power menu while locked
         onPowerMenuLockScreenSettingsChanged();
         mContext.getContentResolver().registerContentObserver(
@@ -403,8 +400,14 @@
 
     private void seedFavorites() {
         if (mControlsServiceInfos.isEmpty()
-                || mControlsController.getFavorites().size() > 0
-                || mControlsPreferences.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false)) {
+                || mControlsController.getFavorites().size() > 0) {
+            return;
+        }
+
+        // Need to be user-specific with the context to make sure we read the correct prefs
+        SharedPreferences prefs = mCurrentUserContextTracker.getCurrentUserContext()
+                .getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE);
+        if (prefs.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false)) {
             return;
         }
 
@@ -426,7 +429,7 @@
 
         if (preferredComponent == null) {
             Log.i(TAG, "Controls seeding: No preferred component has been set, will not seed");
-            mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, true).apply();
+            prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, true).apply();
             return;
         }
 
@@ -434,8 +437,7 @@
                 preferredComponent,
                 (accepted) -> {
                     Log.i(TAG, "Controls seeded: " + accepted);
-                    mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
-                        accepted).apply();
+                    prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, accepted).apply();
                 });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 0b04fd0..552fea6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -81,7 +81,7 @@
 
     private fun processDevice(key: String, device: MediaDevice?) {
         val enabled = device != null
-        val data = MediaDeviceData(enabled, device?.icon, device?.name)
+        val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
         listeners.forEach {
             it.onMediaDeviceChanged(key, data)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index b9b8a25..a10972e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -36,10 +36,11 @@
 import android.util.TypedValue;
 import android.view.DisplayInfo;
 import android.view.Gravity;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
 import android.window.WindowContainerTransaction;
 
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayLayout;
+
 import java.io.PrintWriter;
 
 import javax.inject.Inject;
@@ -56,10 +57,10 @@
     private static final float INVALID_SNAP_FRACTION = -1f;
 
     private final Context mContext;
-    private final IWindowManager mWindowManager;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
-    private final Rect mTmpInsets = new Rect();
+    private final DisplayController mDisplayController;
+    private final DisplayLayout mDisplayLayout;
 
     private ComponentName mLastPipComponentName;
     private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
@@ -80,11 +81,24 @@
     private boolean mIsShelfShowing;
     private int mShelfHeight;
 
+    private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
+            new DisplayController.OnDisplaysChangedListener() {
+        @Override
+        public void onDisplayAdded(int displayId) {
+            if (displayId == mContext.getDisplayId()) {
+                mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId));
+            }
+        }
+    };
+
     @Inject
-    public PipBoundsHandler(Context context, PipSnapAlgorithm pipSnapAlgorithm) {
+    public PipBoundsHandler(Context context, PipSnapAlgorithm pipSnapAlgorithm,
+            DisplayController displayController) {
         mContext = context;
         mSnapAlgorithm = pipSnapAlgorithm;
-        mWindowManager = WindowManagerGlobal.getWindowManagerService();
+        mDisplayLayout = new DisplayLayout();
+        mDisplayController = displayController;
+        mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
         reloadResources();
         // Initialize the aspect ratio to the default aspect ratio.  Don't do this in reload
         // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
@@ -272,8 +286,8 @@
      *
      * @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise.
      */
-    public boolean onDisplayRotationChanged(Rect outBounds, Rect oldBounds, int displayId,
-            int fromRotation, int toRotation, WindowContainerTransaction t) {
+    public boolean onDisplayRotationChanged(Rect outBounds, Rect oldBounds, Rect outInsetBounds,
+            int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
         // Bail early if the event is not sent to current {@link #mDisplayInfo}
         if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) {
             return false;
@@ -294,6 +308,9 @@
         final Rect postChangeStackBounds = new Rect(oldBounds);
         final float snapFraction = getSnapFraction(postChangeStackBounds);
 
+        // Update the display layout
+        mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
+
         // Populate the new {@link #mDisplayInfo}.
         // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
         // therefore, the width/height may require a swap first.
@@ -308,6 +325,7 @@
         mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
                 snapFraction);
 
+        getInsetBounds(outInsetBounds);
         outBounds.set(postChangeStackBounds);
         t.setBounds(pinnedStackInfo.stackToken, outBounds);
         return true;
@@ -425,15 +443,11 @@
      * Populates the bounds on the screen that the PIP can be visible in.
      */
     protected void getInsetBounds(Rect outRect) {
-        try {
-            mWindowManager.getStableInsets(mContext.getDisplayId(), mTmpInsets);
-            outRect.set(mTmpInsets.left + mScreenEdgeInsets.x,
-                    mTmpInsets.top + mScreenEdgeInsets.y,
-                    mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
-                    mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get stable insets from WM", e);
-        }
+        Rect insets = mDisplayLayout.stableInsets();
+        outRect.set(insets.left + mScreenEdgeInsets.x,
+                insets.top + mScreenEdgeInsets.y,
+                mDisplayInfo.logicalWidth - insets.right - mScreenEdgeInsets.x,
+                mDisplayInfo.logicalHeight - insets.bottom - mScreenEdgeInsets.y);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 64df2ff..02bf745 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -95,8 +95,21 @@
     private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
             int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
         final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mTmpNormalBounds,
-                mPipTaskOrganizer.getLastReportedBounds(), displayId, fromRotation, toRotation, t);
+                mPipTaskOrganizer.getLastReportedBounds(), mTmpInsetBounds, displayId, fromRotation,
+                toRotation, t);
         if (changed) {
+            // If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
+            // movement bounds
+            mTouchHandler.adjustBoundsForRotation(mTmpNormalBounds,
+                    mPipTaskOrganizer.getLastReportedBounds(), mTmpInsetBounds);
+
+            // The bounds are being applied to a specific snap fraction, so reset any known offsets
+            // for the previous orientation before updating the movement bounds
+            mPipBoundsHandler.setShelfHeight(false , 0);
+            mPipBoundsHandler.onImeVisibilityChanged(false, 0);
+            mTouchHandler.onShelfVisibilityChanged(false, 0);
+            mTouchHandler.onImeVisibilityChanged(false, 0);
+
             updateMovementBounds(mTmpNormalBounds, true /* fromRotation */,
                     false /* fromImeAdjustment */, false /* fromShelfAdjustment */);
         }
@@ -290,9 +303,10 @@
     @Override
     public void setShelfHeight(boolean visible, int height) {
         mHandler.post(() -> {
-            final boolean changed = mPipBoundsHandler.setShelfHeight(visible, height);
+            final int shelfHeight = visible ? height : 0;
+            final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
             if (changed) {
-                mTouchHandler.onShelfVisibilityChanged(visible, height);
+                mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
                 updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(),
                         false /* fromRotation */, false /* fromImeAdjustment */,
                         true /* fromShelfAdjustment */);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index cc3ab29d..4b23e67 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -37,6 +37,7 @@
 import android.view.InputEventReceiver;
 import android.view.InputMonitor;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 
 import com.android.internal.policy.TaskResizingAlgorithm;
 import com.android.systemui.R;
@@ -77,10 +78,12 @@
     private final Runnable mUpdateMovementBoundsRunnable;
 
     private int mDelta;
+    private float mTouchSlop;
     private boolean mAllowGesture;
     private boolean mIsAttached;
     private boolean mIsEnabled;
     private boolean mEnableUserResize;
+    private boolean mThresholdCrossed;
 
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
@@ -100,6 +103,7 @@
         mPipTaskOrganizer = pipTaskOrganizer;
         mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
+
         context.getDisplay().getRealSize(mMaxSize);
         reloadResources();
 
@@ -126,6 +130,7 @@
     private void reloadResources() {
         final Resources res = mContext.getResources();
         mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
+        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
     }
 
     private void resetDragCorners() {
@@ -270,7 +275,12 @@
                     break;
                 case MotionEvent.ACTION_MOVE:
                     // Capture inputs
-                    mInputMonitor.pilferPointers();
+                    float dx = Math.abs(ev.getX() - mDownPoint.x);
+                    float dy = Math.abs(ev.getY() - mDownPoint.y);
+                    if (!mThresholdCrossed && dx > mTouchSlop && dy > mTouchSlop) {
+                        mThresholdCrossed = true;
+                        mInputMonitor.pilferPointers();
+                    }
                     final Rect currentPipBounds = mMotionHelper.getBounds();
                     mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
                             mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
@@ -288,6 +298,7 @@
                             mUpdateMovementBoundsRunnable.run();
                             mCtrlType = CTRL_NONE;
                             mAllowGesture = false;
+                            mThresholdCrossed = false;
                         });
                     });
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index c408caa..c274ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -397,6 +397,15 @@
         mShelfHeight = shelfHeight;
     }
 
+    public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
+        final Rect toMovementBounds = new Rect();
+        mSnapAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0);
+        final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
+        if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) {
+            outBounds.offsetTo(outBounds.left, toMovementBounds.bottom);
+        }
+    }
+
     public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds,
             boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
         final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e88ce5a..f0a81e9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -26,7 +26,6 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
@@ -146,7 +145,7 @@
             CompletableFuture<List<Notification.Action>> smartActionsFuture =
                     ScreenshotSmartActions.getSmartActionsFuture(
                             mScreenshotId, uri, image, mSmartActionsProvider,
-                            mSmartActionsEnabled, isManagedProfile(mContext));
+                            mSmartActionsEnabled, getUserHandle(mContext));
 
             try {
                 // First, write the actual data for our screenshot
@@ -382,10 +381,9 @@
         }
     }
 
-    private boolean isManagedProfile(Context context) {
+    private UserHandle getUserHandle(Context context) {
         UserManager manager = UserManager.get(context);
-        UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context));
-        return info.isManagedProfile();
+        return manager.getUserInfo(getUserHandleOfForegroundApplication(context)).getUserHandle();
     }
 
     private List<Notification.Action> buildSmartActions(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
index 3edb33d..63f323e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.Collections;
@@ -64,14 +65,14 @@
      * @param componentName      Contains package and activity class names where the screenshot was
      *                           taken. This is used as an additional signal to generate and rank
      *                           more relevant actions.
-     * @param isManagedProfile   The screenshot was taken for a work profile app.
+     * @param userHandle         The user handle of the app where the screenshot was taken.
      */
     public CompletableFuture<List<Notification.Action>> getActions(
             String screenshotId,
             Uri screenshotUri,
             Bitmap bitmap,
             ComponentName componentName,
-            boolean isManagedProfile) {
+            UserHandle userHandle) {
         Log.d(TAG, "Returning empty smart action list.");
         return CompletableFuture.completedFuture(Collections.emptyList());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index c228fe2..442b373 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -26,6 +26,7 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -48,7 +49,7 @@
     static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
             String screenshotId, Uri screenshotUri, Bitmap image,
             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
-            boolean smartActionsEnabled, boolean isManagedProfile) {
+            boolean smartActionsEnabled, UserHandle userHandle) {
         if (!smartActionsEnabled) {
             Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list.");
             return CompletableFuture.completedFuture(Collections.emptyList());
@@ -60,7 +61,7 @@
             return CompletableFuture.completedFuture(Collections.emptyList());
         }
 
-        Slog.d(TAG, "Screenshot from a managed profile: " + isManagedProfile);
+        Slog.d(TAG, "Screenshot from user profile: " + userHandle.getIdentifier());
         CompletableFuture<List<Notification.Action>> smartActionsFuture;
         long startTimeMs = SystemClock.uptimeMillis();
         try {
@@ -71,7 +72,7 @@
                             ? runningTask.topActivity
                             : new ComponentName("", "");
             smartActionsFuture = smartActionsProvider.getActions(
-                    screenshotId, screenshotUri, image, componentName, isManagedProfile);
+                    screenshotId, screenshotUri, image, componentName, userHandle);
         } catch (Throwable e) {
             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
             smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 8ed69d8..db5329a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -36,6 +36,7 @@
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.AsyncTask;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
@@ -90,7 +91,8 @@
 public class NotificationMediaManager implements Dumpable {
     private static final String TAG = "NotificationMediaManager";
     public static final boolean DEBUG_MEDIA = false;
-    private static final long PAUSED_MEDIA_TIMEOUT = TimeUnit.MINUTES.toMillis(10);
+    private static final long PAUSED_MEDIA_TIMEOUT = SystemProperties
+            .getLong("debug.sysui.media_timeout", TimeUnit.MINUTES.toMillis(10));
 
     private final StatusBarStateController mStatusBarStateController
             = Dependency.get(StatusBarStateController.class);
@@ -163,6 +165,9 @@
                 Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
             }
             if (mMediaTimeoutCancellation != null) {
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: media timeout cancelled");
+                }
                 mMediaTimeoutCancellation.run();
                 mMediaTimeoutCancellation = null;
             }
@@ -182,8 +187,16 @@
             }
             if (entry != null) {
                 if (!isPlayingState(state.getState())) {
+                    if (DEBUG_MEDIA) {
+                        Log.v(TAG, "DEBUG_MEDIA: schedule timeout for "
+                                + mMediaNotificationKey);
+                    }
                     mMediaTimeoutCancellation = mMainExecutor.executeDelayed(() -> {
                         synchronized (mEntryManager) {
+                            if (DEBUG_MEDIA) {
+                                Log.v(TAG, "DEBUG_MEDIA: execute timeout for "
+                                        + mMediaNotificationKey);
+                            }
                             if (mMediaNotificationKey == null) {
                                 return;
                             }
@@ -375,21 +388,18 @@
                             UserHandle.USER_ALL);
 
                     for (MediaController aController : sessions) {
-                        if (PlaybackState.STATE_PLAYING ==
-                                getMediaControllerPlaybackState(aController)) {
-                            // now to see if we have one like this
-                            final String pkg = aController.getPackageName();
+                        // now to see if we have one like this
+                        final String pkg = aController.getPackageName();
 
-                            for (NotificationEntry entry : allNotifications) {
-                                if (entry.getSbn().getPackageName().equals(pkg)) {
-                                    if (DEBUG_MEDIA) {
-                                        Log.v(TAG, "DEBUG_MEDIA: found controller matching "
-                                                + entry.getSbn().getKey());
-                                    }
-                                    controller = aController;
-                                    mediaNotification = entry;
-                                    break;
+                        for (NotificationEntry entry : allNotifications) {
+                            if (entry.getSbn().getPackageName().equals(pkg)) {
+                                if (DEBUG_MEDIA) {
+                                    Log.v(TAG, "DEBUG_MEDIA: found controller matching "
+                                            + entry.getSbn().getKey());
                                 }
+                                controller = aController;
+                                mediaNotification = entry;
+                                break;
                             }
                         }
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index fefad53..da31fe0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -206,6 +206,13 @@
             return false;
         }
 
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG_HEADS_UP) {
+                Log.d(TAG, "No alerting: snoozed package: " + sbn.getKey());
+            }
+            return false;
+        }
+
         boolean inShade = mStatusBarStateController.getState() == SHADE;
         if (entry.isBubble() && inShade) {
             if (DEBUG_HEADS_UP) {
@@ -365,14 +372,6 @@
                 return false;
             }
         }
-
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG_HEADS_UP) {
-                Log.d(TAG, "No alerting: snoozed package: " + sbn.getKey());
-            }
-            return false;
-        }
-
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index e448d0a..d4e1aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 
 import android.content.Context;
@@ -105,6 +106,7 @@
                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                         | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                 PixelFormat.TRANSLUCENT);
+        mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
         mLp.token = new Binder();
         mLp.gravity = Gravity.TOP;
         mLp.setFitInsetsTypes(0 /* types */);
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
index 7729965..7c9ea6b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -92,6 +92,15 @@
     }
 
     /**
+     * @deprecated Please specify @Main or @Background when injecting a Handler or use an Executor.
+     */
+    @Deprecated
+    @Provides
+    public static Handler provideHandler() {
+        return new Handler();
+    }
+
+    /**
      * Provide a Background-Thread Executor by default.
      */
     @Provides
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index e3f25c6..b388887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -797,24 +797,6 @@
     }
 
     @Test
-    fun testListingCallbackNotListeningWhileReadingFavorites() {
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
-            putExtra(Intent.EXTRA_USER_HANDLE, otherUser)
-        }
-        val pendingResult = mock(BroadcastReceiver.PendingResult::class.java)
-        `when`(pendingResult.sendingUserId).thenReturn(otherUser)
-        broadcastReceiverCaptor.value.pendingResult = pendingResult
-
-        broadcastReceiverCaptor.value.onReceive(mContext, intent)
-
-        val inOrder = inOrder(persistenceWrapper, listingController)
-
-        inOrder.verify(listingController).removeCallback(listingCallbackCaptor.value)
-        inOrder.verify(persistenceWrapper).readFavorites()
-        inOrder.verify(listingController).addCallback(listingCallbackCaptor.value)
-    }
-
-    @Test
     fun testSeedFavoritesForComponent() {
         var succeeded = false
         val control = statelessBuilderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 128a7e8..841b255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -65,7 +65,10 @@
     @Mock
     private lateinit var serviceInfo: ServiceInfo
     @Mock
-    private lateinit var componentName: ComponentName
+    private lateinit var serviceInfo2: ServiceInfo
+
+    private var componentName = ComponentName("pkg1", "class1")
+    private var componentName2 = ComponentName("pkg2", "class2")
 
     private val executor = FakeExecutor(FakeSystemClock())
 
@@ -82,6 +85,7 @@
         MockitoAnnotations.initMocks(this)
 
         `when`(serviceInfo.componentName).thenReturn(componentName)
+        `when`(serviceInfo2.componentName).thenReturn(componentName2)
 
         val wrapper = object : ContextWrapper(mContext) {
             override fun createContextAsUser(user: UserHandle, flags: Int): Context {
@@ -179,7 +183,7 @@
     }
 
     @Test
-    fun testChangeUserResetsExistingCallbackServices() {
+    fun testChangeUserSendsCorrectServiceUpdate() {
         val list = listOf(serviceInfo)
         controller.addCallback(mockCallback)
 
@@ -197,10 +201,21 @@
         assertEquals(1, captor.value.size)
 
         reset(mockCallback)
+        reset(mockSL)
+
+        val updatedList = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(updatedList)
         controller.changeUser(UserHandle.of(otherUser))
         executor.runAllReady()
         assertEquals(otherUser, controller.currentUserId)
 
+        // this event should was triggered just before the user change, and should
+        // be ignored
+        verify(mockCallback, never()).onServicesUpdated(any())
+
+        serviceListingCallbackCaptor.value.onServicesReloaded(emptyList<ServiceInfo>())
+        executor.runAllReady()
+
         verify(mockCallback).onServicesUpdated(capture(captor))
         assertEquals(0, captor.value.size)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 8db57cd..487452b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -65,6 +65,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
+import com.android.systemui.settings.CurrentUserContextTracker;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -119,6 +120,7 @@
     @Mock GlobalActionsPanelPlugin mWalletPlugin;
     @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
     @Mock private Handler mHandler;
+    @Mock private CurrentUserContextTracker mCurrentUserContextTracker;
 
     private TestableLooper mTestableLooper;
 
@@ -129,6 +131,7 @@
         allowTestableLooperAsMainThread();
 
         when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
+        when(mCurrentUserContextTracker.getCurrentUserContext()).thenReturn(mContext);
         mGlobalActionsDialog = new GlobalActionsDialog(mContext,
                 mWindowManagerFuncs,
                 mAudioManager,
@@ -161,7 +164,8 @@
                 mUiEventLogger,
                 mRingerModeTracker,
                 mSysUiState,
-                mHandler
+                mHandler,
+                mCurrentUserContextTracker
         );
         mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 7b80a6e..c0aef8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media
 
 import android.app.Notification
+import android.graphics.drawable.Drawable
 import android.media.MediaMetadata
 import android.media.MediaRouter2Manager
 import android.media.RoutingSessionInfo
@@ -73,6 +74,7 @@
     private lateinit var fakeExecutor: FakeExecutor
     @Mock private lateinit var listener: MediaDeviceManager.Listener
     @Mock private lateinit var device: MediaDevice
+    @Mock private lateinit var icon: Drawable
     @Mock private lateinit var route: RoutingSessionInfo
     private lateinit var session: MediaSession
     private lateinit var metadataBuilder: MediaMetadata.Builder
@@ -89,6 +91,7 @@
 
         // Configure mocks.
         whenever(device.name).thenReturn(DEVICE_NAME)
+        whenever(device.iconWithoutBackground).thenReturn(icon)
         whenever(lmmFactory.create(PACKAGE)).thenReturn(lmm)
         whenever(lmm.getCurrentConnectedDevice()).thenReturn(device)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
@@ -157,6 +160,7 @@
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(DEVICE_NAME)
+        assertThat(data.icon).isEqualTo(icon)
     }
 
     @Test
@@ -170,6 +174,7 @@
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(DEVICE_NAME)
+        assertThat(data.icon).isEqualTo(icon)
     }
 
     @Test
@@ -183,6 +188,7 @@
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(DEVICE_NAME)
+        assertThat(data.icon).isEqualTo(icon)
     }
 
     @Test
@@ -204,6 +210,7 @@
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isNull()
+        assertThat(data.icon).isNull()
     }
 
     @Test
@@ -221,6 +228,7 @@
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isNull()
+        assertThat(data.icon).isNull()
     }
 
     @Test
@@ -238,6 +246,7 @@
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isNull()
+        assertThat(data.icon).isNull()
     }
 
     fun captureCallback(): LocalMediaManager.DeviceCallback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index 425bf88..f404f04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 
 import android.content.ComponentName;
 import android.graphics.Rect;
@@ -32,6 +33,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.wm.DisplayController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -63,7 +65,8 @@
     @Before
     public void setUp() throws Exception {
         initializeMockResources();
-        mPipBoundsHandler = new PipBoundsHandler(mContext, new PipSnapAlgorithm(mContext));
+        mPipBoundsHandler = new PipBoundsHandler(mContext, new PipSnapAlgorithm(mContext),
+                mock(DisplayController.class));
         mTestComponentName1 = new ComponentName(mContext, "component1");
         mTestComponentName2 = new ComponentName(mContext, "component2");
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 2c4304d..d3b3399 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -37,6 +36,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
@@ -79,13 +79,12 @@
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
         ScreenshotNotificationSmartActionsProvider smartActionsProvider = mock(
                 ScreenshotNotificationSmartActionsProvider.class);
-        when(smartActionsProvider.getActions(any(), any(), any(), any(),
-                eq(false))).thenThrow(
-                RuntimeException.class);
+        when(smartActionsProvider.getActions(any(), any(), any(), any(), any()))
+            .thenThrow(RuntimeException.class);
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
                 ScreenshotSmartActions.getSmartActionsFuture(
                         "", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
-                        true, false);
+                        true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
         assertNotNull(smartActionsFuture);
         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
         assertEquals(Collections.emptyList(), smartActions);
@@ -125,9 +124,8 @@
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
                 ScreenshotSmartActions.getSmartActionsFuture(
                         "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
-                        true, true);
-        verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(),
-                eq(false));
+                        true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
+        verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any());
         assertNotNull(smartActionsFuture);
         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
         assertEquals(Collections.emptyList(), smartActions);
@@ -140,9 +138,8 @@
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
         ScreenshotSmartActions.getSmartActionsFuture(
                 "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
-                true);
-        verify(mSmartActionsProvider, times(1))
-                .getActions(any(), any(), any(), any(), eq(true));
+                UserHandle.getUserHandleForUid(UserHandle.myUserId()));
+        verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any());
     }
 
     // Tests for a hardware bitmap, a completed future is returned.
@@ -157,7 +154,7 @@
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
                 ScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
                         actionsProvider,
-                        true, true);
+                        true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
         assertNotNull(smartActionsFuture);
         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
         assertEquals(smartActions.size(), 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 5cbfcc1..e254cd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -117,21 +117,12 @@
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptStateProviderImpl#canAlertAwakeCommon(NotificationEntry)} will
-     * pass as long its provided NotificationEntry fulfills launch fullscreen check.
-     */
-    private void ensureStateForAlertAwakeCommon() {
-        when(mHeadsUpManager.isSnoozed(any())).thenReturn(false);
-    }
-
-    /**
-     * Sets up the state such that any requests to
      * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills importance & DND checks.
      */
     private void ensureStateForHeadsUpWhenAwake() throws RemoteException {
         ensureStateForAlertCommon();
-        ensureStateForAlertAwakeCommon();
+        when(mHeadsUpManager.isSnoozed(any())).thenReturn(false);
 
         when(mStatusBarStateController.isDozing()).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
@@ -157,7 +148,6 @@
      */
     private void ensureStateForBubbleUp() {
         ensureStateForAlertCommon();
-        ensureStateForAlertAwakeCommon();
     }
 
     @Test
@@ -392,7 +382,6 @@
     @Test
     public void testShouldNotHeadsUp_snoozedPackage() {
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
-        ensureStateForAlertAwakeCommon();
 
         when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true);
 
@@ -402,7 +391,6 @@
 
     @Test
     public void testShouldNotHeadsUp_justLaunchedFullscreen() {
-        ensureStateForAlertAwakeCommon();
 
         // On screen alerts don't happen when that package has just launched fullscreen.
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index de53787..659d344 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -738,7 +738,7 @@
     //
     // TODO: Evaluate using a data structure than is more directly suited to
     // communicating only the relevant information.
-    private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
+    private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
         if (mRaDaemon == null) return;
 
         // Avoid unnecessary work on spurious updates.
@@ -761,7 +761,7 @@
             params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
             params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
 
-            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
+            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
 
             for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
                 if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -1052,12 +1052,11 @@
         }
     }
 
-    private byte getHopLimit(String upstreamIface) {
+    private byte getHopLimit(String upstreamIface, int adjustTTL) {
         try {
             int upstreamHopLimit = Integer.parseUnsignedInt(
                     mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
-            // Add one hop to account for this forwarding device
-            upstreamHopLimit++;
+            upstreamHopLimit = upstreamHopLimit + adjustTTL;
             // Cap the hop limit to 255.
             return (byte) Integer.min(upstreamHopLimit, 255);
         } catch (Exception e) {
@@ -1145,7 +1144,7 @@
                     transitionTo(mUnavailableState);
                     break;
                 case CMD_IPV6_TETHER_UPDATE:
-                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -1209,7 +1208,7 @@
                     if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
                     break;
                 case CMD_IPV6_TETHER_UPDATE:
-                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
                     sendLinkProperties();
                     break;
                 case CMD_IP_FORWARDING_ENABLE_ERROR:
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
index d450c46..f3dcaa2 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
@@ -161,11 +161,28 @@
     private void updateIPv6TetheringInterfaces() {
         for (IpServer ipServer : mNotifyList) {
             final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
-            ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+            ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp);
             break;
         }
     }
 
+    private int getTtlAdjustment() {
+        if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) {
+            return 0;
+        }
+
+        // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1
+        // for carrier requirement.
+        if (mUpstreamNetworkState.networkCapabilities.hasTransport(
+                NetworkCapabilities.TRANSPORT_CELLULAR)) {
+            return -1;
+        }
+
+        // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary
+        // distinction between tethered and untethered traffic.
+        return 1;
+    }
+
     private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
         final Downstream ds = findDownstream(ipServer);
         if (ds == null) return null;
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index cd1ff60..307ebf1 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -17,6 +17,7 @@
 package android.net.ip;
 
 import static android.net.INetd.IF_STATE_UP;
+import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
 import static android.net.TetheringManager.TETHERING_NCM;
 import static android.net.TetheringManager.TETHERING_USB;
@@ -74,6 +75,7 @@
 import android.net.dhcp.IDhcpServerCallbacks;
 import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
+import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
@@ -196,7 +198,7 @@
         if (upstreamIface != null) {
             LinkProperties lp = new LinkProperties();
             lp.setInterfaceName(upstreamIface);
-            dispatchTetherConnectionChanged(upstreamIface, lp);
+            dispatchTetherConnectionChanged(upstreamIface, lp, 0);
         }
         reset(mNetd, mCallback);
     }
@@ -694,7 +696,7 @@
         InOrder inOrder = inOrder(mNetd);
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName(UPSTREAM_IFACE2);
-        dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
         inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
         inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
         inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
@@ -702,7 +704,7 @@
         reset(mNetd);
 
         // When the upstream is lost, rules are removed.
-        dispatchTetherConnectionChanged(null, null);
+        dispatchTetherConnectionChanged(null, null, 0);
         verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
         verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
         reset(mNetd);
@@ -715,19 +717,19 @@
 
         // Rules can be added again once upstream IPv6 connectivity is available.
         lp.setInterfaceName(UPSTREAM_IFACE);
-        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
         recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
         verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
         verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
 
         // If upstream IPv6 connectivity is lost, rules are removed.
         reset(mNetd);
-        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
         verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
 
         // When the interface goes down, rules are removed.
         lp.setInterfaceName(UPSTREAM_IFACE);
-        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
         recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
         recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
         verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
@@ -788,6 +790,49 @@
         verify(mIpNeighborMonitor, never()).start();
     }
 
+    private LinkProperties buildIpv6OnlyLinkProperties(final String iface) {
+        final LinkProperties linkProp = new LinkProperties();
+        linkProp.setInterfaceName(iface);
+        linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+        linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST));
+        final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888");
+        linkProp.addDnsServer(dns);
+
+        return linkProp;
+    }
+
+    @Test
+    public void testAdjustTtlValue() throws Exception {
+        final ArgumentCaptor<RaParams> raParamsCaptor =
+                ArgumentCaptor.forClass(RaParams.class);
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams noV6Params = raParamsCaptor.getValue();
+        assertEquals(65, noV6Params.hopLimit);
+        reset(mRaDaemon);
+
+        when(mNetd.getProcSysNet(
+                INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64");
+        final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams nonCellularParams = raParamsCaptor.getValue();
+        assertEquals(65, nonCellularParams.hopLimit);
+        reset(mRaDaemon);
+
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams noUpstream = raParamsCaptor.getValue();
+        assertEquals(65, nonCellularParams.hopLimit);
+        reset(mRaDaemon);
+
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams cellularParams = raParamsCaptor.getValue();
+        assertEquals(63, cellularParams.hopLimit);
+        reset(mRaDaemon);
+    }
+
     private void assertDhcpServingParams(final DhcpServingParamsParcel params,
             final IpPrefix prefix) {
         // Last address byte is random
@@ -838,9 +883,10 @@
      * @param upstreamIface String name of upstream interface (or null)
      * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream.
      */
-    private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) {
+    private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp,
+            int ttlAdjustment) {
         dispatchTetherConnectionChanged(upstreamIface);
-        mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
+        mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp);
         mLooper.dispatchAll();
     }
 
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
index 820f255..f2b5314 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
@@ -128,7 +128,7 @@
         final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR);
         final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class);
         mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream);
-        verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0),
+        verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
                 lp.capture());
         final LinkProperties v6OnlyLink = lp.getValue();
         assertOnlyOneV6AddressAndNoV4(v6OnlyLink);
@@ -140,7 +140,7 @@
         mNotifyList.remove(firstServer);
         mIPv6TetheringCoordinator.removeActiveDownstream(firstServer);
         verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
-        verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0),
+        verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
                 lp.capture());
         final LinkProperties localOnlyLink = lp.getValue();
         assertNotNull(localOnlyLink);
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index e5c0540..45d53a1 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -68,8 +68,7 @@
 public abstract class SystemService {
 
     /** @hide */
-    // TODO(b/133242016) STOPSHIP: change to false before R ships
-    protected static final boolean DEBUG_USER = true;
+    protected static final boolean DEBUG_USER = false;
 
     /*
      * The earliest boot phase the system send to system services on boot.
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index cca6046..ec12aeb 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4914,8 +4914,13 @@
 
     // TODO: remove this toast after feature development is done
     void showWhileInUseDebugToastLocked(int uid, int op, int mode) {
-        for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
-            ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
+        final UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(uid);
+        if (uidRec == null) {
+            return;
+        }
+
+        for (int i = uidRec.procRecords.size() - 1; i >= 0; i--) {
+            ProcessRecord pr = uidRec.procRecords.valueAt(i);
             if (pr.uid != uid) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f7a158a..a81590c 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -324,8 +324,21 @@
         boolean success = applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
 
         if (uidRec != null) {
-            updateAppUidRecLocked(app);
-            // If this proc state is changed, need to update its uid record here
+            // After uidRec.reset() above, for UidRecord that has multiple processes (ProcessRecord)
+            // , We need to apply all ProcessRecord into UidRecord.
+            final ArraySet<ProcessRecord> procRecords = app.uidRecord.procRecords;
+            for (int i = procRecords.size() - 1; i >= 0; i--) {
+                final ProcessRecord pr = procRecords.valueAt(i);
+                if (!pr.killedByAm && pr.thread != null) {
+                    if (pr.isolated && pr.numberOfRunningServices() <= 0
+                            && pr.isolatedEntryPoint == null) {
+                        // No op.
+                    } else {
+                        // Keeping this process, update its uid.
+                        updateAppUidRecLocked(pr);
+                    }
+                }
+            }
             if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
                     && (uidRec.setProcState != uidRec.getCurProcState()
                     || uidRec.setCapability != uidRec.curCapability
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 108fb7d..9f2a77c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2808,6 +2808,7 @@
                     uidRec.curCapability);
         }
         proc.uidRecord = uidRec;
+        uidRec.procRecords.add(proc);
 
         // Reset render thread tid if it was already set, so new process can set it again.
         proc.renderThreadTid = 0;
@@ -2901,6 +2902,7 @@
         }
         if (old != null && old.uidRecord != null) {
             old.uidRecord.numProcs--;
+            old.uidRecord.procRecords.remove(old);
             if (old.uidRecord.numProcs == 0) {
                 // No more processes using this uid, tell clients it is gone.
                 if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index acf8b2e..cb781c4 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
@@ -44,6 +45,7 @@
     boolean idle;
     boolean setIdle;
     int numProcs;
+    ArraySet<ProcessRecord> procRecords = new ArraySet<>();
 
     /**
      * Sequence number associated with the {@link #mCurProcState}. This is incremented using
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index f5d6e5a..08d2664 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -89,7 +89,7 @@
             return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk);
         }
         // Only allow to opt-in for a targetSdk gated change.
-        if (disabled || applicationInfo.targetSdkVersion < minTargetSdk) {
+        if (disabled || appTargetSdk <= minTargetSdk) {
             return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
         }
         return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk);
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 34d0bed..3091a71 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -198,6 +198,9 @@
         if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
             stopPrefixDiscovery();
         }
+        if (!mPrefixDiscoveryRunning) {
+            setPrefix64(mNat64PrefixInUse);
+        }
     }
 
     /**
@@ -221,6 +224,10 @@
         mIface = null;
         mBaseIface = null;
 
+        if (!mPrefixDiscoveryRunning) {
+            setPrefix64(null);
+        }
+
         if (isPrefixDiscoveryNeeded()) {
             if (!mPrefixDiscoveryRunning) {
                 startPrefixDiscovery();
@@ -308,6 +315,16 @@
         return requiresClat(mNetwork) && mNat64PrefixFromRa == null;
     }
 
+    private void setPrefix64(IpPrefix prefix) {
+        final String prefixString = (prefix != null) ? prefix.toString() : "";
+        try {
+            mDnsResolver.setPrefix64(getNetId(), prefixString);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+                    + prefix + ": " + e);
+        }
+    }
+
     private void maybeHandleNat64PrefixChange() {
         final IpPrefix newPrefix = selectNat64Prefix();
         if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 910ed44..19fa920 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -30,6 +30,7 @@
 
 import com.android.server.FgThread;
 
+import java.io.File;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -124,9 +125,13 @@
         }
     }
 
-    String getIdmapPath(String overlayPath, int userId) throws TimeoutException, RemoteException {
+    boolean idmapExists(String overlayPath, int userId) {
         try (Connection c = connect()) {
-            return mService.getIdmapPath(overlayPath, userId);
+            return new File(mService.getIdmapPath(overlayPath, userId)).isFile();
+        } catch (Exception e) {
+            Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath + ": "
+                    + e.getMessage());
+            return false;
         }
     }
 
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 90c85ad..735d669 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -27,10 +27,10 @@
 import android.os.Build.VERSION_CODES;
 import android.os.OverlayablePolicy;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.util.Slog;
 
-import java.io.File;
+import com.android.internal.util.ArrayUtils;
+
 import java.io.IOException;
 
 /**
@@ -55,12 +55,16 @@
         VENDOR_IS_Q_OR_LATER = isQOrLater;
     }
 
-    private final OverlayableInfoCallback mOverlayableCallback;
     private final IdmapDaemon mIdmapDaemon;
+    private final OverlayableInfoCallback mOverlayableCallback;
+    private final String mOverlayableConfigurator;
+    private final String[] mOverlayableConfiguratorTargets;
 
-    IdmapManager(final OverlayableInfoCallback verifyCallback) {
+    IdmapManager(final IdmapDaemon idmapDaemon, final OverlayableInfoCallback verifyCallback) {
         mOverlayableCallback = verifyCallback;
-        mIdmapDaemon = IdmapDaemon.getInstance();
+        mIdmapDaemon = idmapDaemon;
+        mOverlayableConfigurator = verifyCallback.getOverlayableConfigurator();
+        mOverlayableConfiguratorTargets = verifyCallback.getOverlayableConfiguratorTargets() ;
     }
 
     /**
@@ -103,23 +107,11 @@
     }
 
     boolean idmapExists(@NonNull final OverlayInfo oi) {
-        return new File(getIdmapPath(oi.baseCodePath, oi.userId)).isFile();
+        return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId);
     }
 
     boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
-        return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath(), userId))
-            .isFile();
-    }
-
-    private @NonNull String getIdmapPath(@NonNull final String overlayPackagePath,
-            final int userId) {
-        try {
-            return mIdmapDaemon.getIdmapPath(overlayPackagePath, userId);
-        } catch (Exception e) {
-            Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
-                    + e.getMessage());
-            return "";
-        }
+        return mIdmapDaemon.idmapExists(overlayPackage.applicationInfo.getBaseCodePath(), userId);
     }
 
     /**
@@ -198,9 +190,17 @@
         String targetOverlayableName = overlayPackage.targetOverlayableName;
         if (targetOverlayableName != null) {
             try {
+                if (!mOverlayableConfigurator.isEmpty()
+                        && ArrayUtils.contains(mOverlayableConfiguratorTargets,
+                                targetPackage.packageName)
+                        && mOverlayableCallback.signaturesMatching(mOverlayableConfigurator,
+                                overlayPackage.packageName, userId)) {
+                    return true;
+                }
+
                 OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget(
                         targetPackage.packageName, targetOverlayableName, userId);
-                if (overlayableInfo != null) {
+                if (overlayableInfo != null && overlayableInfo.actor != null) {
                     String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
                             overlayableInfo.actor, mOverlayableCallback.getNamedActors()).first;
                     if (mOverlayableCallback.signaturesMatching(actorPackageName,
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index ef6655d..2bc3499 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -50,8 +50,8 @@
     /**
      * @return nullable actor result with {@link ActorState} failure status
      */
-    static Pair<String, ActorState> getPackageNameForActor(String actorUriString,
-            Map<String, Map<String, String>> namedActors) {
+    static Pair<String, ActorState> getPackageNameForActor(@NonNull String actorUriString,
+            @NonNull Map<String, Map<String, String>> namedActors) {
         Uri actorUri = Uri.parse(actorUriString);
 
         String actorScheme = actorUri.getScheme();
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 6a8e465..086ab81 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -45,6 +45,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
 import android.content.res.ApkAssets;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
@@ -62,6 +63,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.R;
 import com.android.internal.content.om.OverlayConfig;
 import com.android.server.FgThread;
 import com.android.server.IoThread;
@@ -246,7 +248,7 @@
                     new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
             mPackageManager = new PackageManagerHelperImpl(context);
             mUserManager = UserManagerService.getInstance();
-            IdmapManager im = new IdmapManager(mPackageManager);
+            IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
             mSettings = new OverlayManagerSettings();
             mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
                     OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
@@ -1119,6 +1121,17 @@
         }
 
         @Override
+        public String getOverlayableConfigurator() {
+            return Resources.getSystem().getString(R.string.config_overlayableConfigurator);
+        }
+
+        @Override
+        public String[] getOverlayableConfiguratorTargets() {
+            return Resources.getSystem().getStringArray(
+                    R.array.config_overlayableConfiguratorTargets);
+        }
+
+        @Override
         public List<PackageInfo> getOverlayPackages(final int userId) {
             final List<PackageInfo> overlays = mPackageManagerInternal.getOverlayPackages(userId);
             for (final PackageInfo info : overlays) {
diff --git a/services/core/java/com/android/server/om/OverlayableInfoCallback.java b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
index 5066ecd..41c341a 100644
--- a/services/core/java/com/android/server/om/OverlayableInfoCallback.java
+++ b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
@@ -80,4 +80,24 @@
      *     in the system returns {@link PackageManager#SIGNATURE_MATCH}
      */
     boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId);
+
+    /**
+     * Retrieves the package name that is recognized as an actor for the packages specified by
+     * {@link #getOverlayableConfiguratorTargets()}.
+     */
+    @NonNull
+    default String getOverlayableConfigurator() {
+        return "";
+    }
+
+    /**
+     * Retrieves the target packages that recognize the {@link #getOverlayableConfigurator} as an
+     * actor for its overlayable declarations. Overlays targeting one of the specified targets that
+     * are signed with the same signature as the overlayable configurator will be granted the
+     * "actor" policy.
+     */
+    @NonNull
+    default String[] getOverlayableConfiguratorTargets() {
+        return new String[0];
+    }
 }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 830388d..4b6ee71 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -446,11 +446,6 @@
             }
         }
 
-        if (!newPkgSetting.pkg.getProtectedBroadcasts().isEmpty()) {
-            mProtectedBroadcasts.addAll(newPkgSetting.pkg.getProtectedBroadcasts());
-            recomputeComponentVisibility(existingSettings, newPkgSetting.pkg.getPackageName());
-        }
-
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
         try {
             final AndroidPackage newPkg = newPkgSetting.pkg;
@@ -459,6 +454,11 @@
                 return;
             }
 
+            if (!newPkg.getProtectedBroadcasts().isEmpty()) {
+                mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts());
+                recomputeComponentVisibility(existingSettings, newPkg.getPackageName());
+            }
+
             final boolean newIsForceQueryable =
                     mForceQueryable.contains(newPkgSetting.appId)
                             /* shared user that is already force queryable */
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 395e835..aa7a1ad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -369,6 +369,7 @@
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -4406,6 +4407,11 @@
         if (getInstantAppPackageName(callingUid) != null) {
             throw new SecurityException("Instant applications don't have access to this method");
         }
+        if (!mUserManager.exists(userId)) {
+            throw new SecurityException("User doesn't exist");
+        }
+        mPermissionManager.enforceCrossUserPermission(
+                callingUid, userId, false, false, "checkPackageStartable");
         final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
         synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -5778,9 +5784,15 @@
 
     @Override
     public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+        final int callingUid = Binder.getCallingUid();
+        if (getInstantAppPackageName(callingUid) != null) {
             return null;
         }
+        if (!mUserManager.exists(userId)) {
+            return null;
+        }
+        mPermissionManager.enforceCrossUserPermission(
+                callingUid, userId, false, false, "getChangedPackages");
         synchronized (mLock) {
             if (sequenceNumber >= mChangedPackagesSequenceNumber) {
                 return null;
@@ -8800,9 +8812,23 @@
 
     private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
         if (!mUserManager.exists(userId)) return null;
-        flags = updateFlagsForComponent(flags, userId);
         final int callingUid = Binder.getCallingUid();
+        flags = updateFlagsForComponent(flags, userId);
         final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
+        boolean checkedGrants = false;
+        if (providerInfo != null) {
+            // Looking for cross-user grants before enforcing the typical cross-users permissions
+            if (userId != UserHandle.getUserId(callingUid)) {
+                final UriGrantsManagerInternal mUgmInternal =
+                        LocalServices.getService(UriGrantsManagerInternal.class);
+                checkedGrants =
+                        mUgmInternal.checkAuthorityGrants(callingUid, providerInfo, userId, true);
+            }
+        }
+        if (!checkedGrants) {
+            mPermissionManager.enforceCrossUserPermission(
+                    callingUid, userId, false, false, "resolveContentProvider");
+        }
         if (providerInfo == null) {
             return null;
         }
@@ -16267,10 +16293,16 @@
                     // signing certificate than the existing one, and if so, copy over the new
                     // details
                     if (signatureCheckPs.sharedUser != null) {
-                        if (parsedPackage.getSigningDetails().hasAncestor(
-                                signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
-                            signatureCheckPs.sharedUser.signatures.mSigningDetails =
-                                    parsedPackage.getSigningDetails();
+                        // Attempt to merge the existing lineage for the shared SigningDetails with
+                        // the lineage of the new package; if the shared SigningDetails are not
+                        // returned this indicates the new package added new signers to the lineage
+                        // and/or changed the capabilities of existing signers in the lineage.
+                        SigningDetails sharedSigningDetails =
+                                signatureCheckPs.sharedUser.signatures.mSigningDetails;
+                        SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
+                                signingDetails);
+                        if (mergedDetails != sharedSigningDetails) {
+                            signatureCheckPs.sharedUser.signatures.mSigningDetails = mergedDetails;
                         }
                         if (signatureCheckPs.sharedUser.signaturesChanged == null) {
                             signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 5c175a6..1fce07b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -611,7 +611,6 @@
         final String packageName = pkgSetting.name;
         boolean compatMatch = false;
         if (pkgSetting.signatures.mSigningDetails.signatures != null) {
-
             // Already existing package. Make sure signatures match
             boolean match = parsedSignatures.checkCapability(
                     pkgSetting.signatures.mSigningDetails,
@@ -664,6 +663,13 @@
                     || pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
                             parsedSignatures,
                             PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+            // Special case: if the sharedUserId capability check failed it could be due to this
+            // being the only package in the sharedUserId so far and the lineage being updated to
+            // deny the sharedUserId capability of the previous key in the lineage.
+            if (!match && pkgSetting.getSharedUser().packages.size() == 1
+                    && pkgSetting.getSharedUser().packages.valueAt(0).name.equals(packageName)) {
+                match = true;
+            }
             if (!match && compareCompat) {
                 match = matchSignaturesCompat(
                         packageName, pkgSetting.getSharedUser().signatures, parsedSignatures);
@@ -686,6 +692,42 @@
                         + " has no signatures that match those in shared user "
                         + pkgSetting.getSharedUser().name + "; ignoring!");
             }
+            // It is possible that this package contains a lineage that blocks sharedUserId access
+            // to an already installed package in the sharedUserId signed with a previous key.
+            // Iterate over all of the packages in the sharedUserId and ensure any that are signed
+            // with a key in this package's lineage have the SHARED_USER_ID capability granted.
+            if (parsedSignatures.hasPastSigningCertificates()) {
+                for (PackageSetting shUidPkgSetting : pkgSetting.getSharedUser().packages) {
+                    // if the current package in the sharedUserId is the package being updated then
+                    // skip this check as the update may revoke the sharedUserId capability from
+                    // the key with which this app was previously signed.
+                    if (packageName.equals(shUidPkgSetting.name)) {
+                        continue;
+                    }
+                    PackageParser.SigningDetails shUidSigningDetails =
+                            shUidPkgSetting.getSigningDetails();
+                    // The capability check only needs to be performed against the package if it is
+                    // signed with a key that is in the lineage of the package being installed.
+                    if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
+                        if (!parsedSignatures.checkCapability(shUidSigningDetails,
+                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)) {
+                            throw new PackageManagerException(
+                                    INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+                                    "Package " + packageName
+                                            + " revoked the sharedUserId capability from the "
+                                            + "signing key used to sign " + shUidPkgSetting.name);
+                        }
+                    }
+                }
+            }
+            // If the lineage of this package diverges from the lineage of the sharedUserId then
+            // do not allow the installation to proceed.
+            if (!parsedSignatures.hasCommonAncestor(
+                    pkgSetting.getSharedUser().signatures.mSigningDetails)) {
+                throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+                        "Package " + packageName + " has a signing lineage "
+                                + "that diverges from the lineage of the sharedUserId");
+            }
         }
         return compatMatch;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b5b82d3..5a6e0a1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6535,14 +6535,14 @@
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
         final int requestedOrientation = getRequestedConfigurationOrientation();
-        final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
+        final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED
+                && !mDisplayContent.ignoreRotationForApps();
         final int orientation = orientationRequested
                 ? requestedOrientation
                 : newParentConfiguration.orientation;
         int rotation = newParentConfiguration.windowConfiguration.getRotation();
         final boolean canChangeOrientation = handlesOrientationChangeFromDescendant();
-        if (canChangeOrientation && mCompatDisplayInsets.mIsRotatable
-                && !mCompatDisplayInsets.mIsFloating) {
+        if (canChangeOrientation && !mCompatDisplayInsets.mIsFloating) {
             // Use parent rotation because the original display can rotate by requested orientation.
             resolvedConfig.windowConfiguration.setRotation(rotation);
         } else {
@@ -7628,7 +7628,6 @@
         private final int mWidth;
         private final int mHeight;
         final boolean mIsFloating;
-        final boolean mIsRotatable;
 
         /**
          * The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
@@ -7645,7 +7644,6 @@
         /** Constructs the environment to simulate the bounds behavior of the given container. */
         CompatDisplayInsets(DisplayContent display, WindowContainer container) {
             mIsFloating = container.getWindowConfiguration().tasksAreFloating();
-            mIsRotatable = !mIsFloating && !display.ignoreRotationForApps();
             if (mIsFloating) {
                 final Rect containerBounds = container.getWindowConfiguration().getBounds();
                 mWidth = containerBounds.width();
@@ -7702,7 +7700,7 @@
                 return;
             }
 
-            if (mIsRotatable && canChangeOrientation) {
+            if (canChangeOrientation) {
                 getBoundsByRotation(outBounds, rotation);
                 if (orientationRequested) {
                     getFrameByOrientation(outAppBounds, orientation);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eb85db6..dfae702 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1106,12 +1106,14 @@
     }
 
     void removeShellRoot(int windowType) {
-        ShellRoot root = mShellRoots.get(windowType);
-        if (root == null) {
-            return;
+        synchronized(mWmService.mGlobalLock) {
+            ShellRoot root = mShellRoots.get(windowType);
+            if (root == null) {
+                return;
+            }
+            root.clear();
+            mShellRoots.remove(windowType);
         }
-        root.clear();
-        mShellRoots.remove(windowType);
     }
 
     void setRemoteInsetsController(IDisplayWindowInsetsController controller) {
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 99f710b..7a38bb6 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -156,4 +156,3 @@
         }
     }
 }
-
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e9aff88..ec03f24 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -334,6 +334,8 @@
     private boolean mDragResizing;
     private boolean mDragResizingChangeReported = true;
     private int mResizeMode;
+    private boolean mResizeForBlastSyncReported;
+
     /**
      * Special mode that is intended only for the rounded corner overlay: during rotation
      * transition, we un-rotate the window token such that the window appears as it did before the
@@ -1370,11 +1372,14 @@
         // variables, because mFrameSizeChanged only tracks the width and height changing.
         updateLastFrames();
 
+        // Add a window that is using blastSync to the resizing list if it hasn't been reported
+        // already. This because the window is waiting on a finishDrawing from the client.
         if (didFrameInsetsChange
                 || winAnimator.mSurfaceResized
                 || configChanged
                 || dragResizingChanged
-                || mReportOrientationChanged) {
+                || mReportOrientationChanged
+                || requestResizeForBlastSync()) {
             ProtoLog.v(WM_DEBUG_RESIZE,
                         "Resize reasons for w=%s:  %s surfaceResized=%b configChanged=%b "
                                 + "dragResizingChanged=%b reportOrientationChanged=%b",
@@ -3483,6 +3488,7 @@
         mReportOrientationChanged = false;
         mDragResizingChangeReported = true;
         mWinAnimator.mSurfaceResized = false;
+        mResizeForBlastSyncReported = true;
         mWindowFrames.resetInsetsChanged();
 
         final Rect frame = mWindowFrames.mCompatFrame;
@@ -5733,6 +5739,7 @@
         if (!willSync) {
             return false;
         }
+        mResizeForBlastSyncReported = false;
 
         mLocalSyncId = mBLASTSyncEngine.startSyncSet(this);
         addChildrenToSyncSet(mLocalSyncId);
@@ -5777,4 +5784,8 @@
         mWaitingListener = null;
         return mWinAnimator.finishDrawingLocked(null);
     }
+
+    private boolean requestResizeForBlastSync() {
+        return useBLASTSync() && !mResizeForBlastSyncReported;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index 425c724..d45589d 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -288,7 +288,8 @@
     public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1).build();
+                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1)
+                        .addTargetSdkChangeWithId(TARGET_SDK, 2).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
@@ -296,19 +297,23 @@
                         .withTargetSdk(TARGET_SDK)
                         .withPackageName(PACKAGE_NAME).build());
 
-        OverrideAllowedState allowedState =
+        OverrideAllowedState stateTargetSdkGreaterChange =
                 overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
 
-        assertThat(allowedState)
+        assertThat(stateTargetSdkGreaterChange)
                 .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK));
     }
 
     @Test
     public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2).build();
+                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
@@ -319,14 +324,10 @@
 
         OverrideAllowedState stateTargetSdkLessChange =
                 overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
-        OverrideAllowedState stateTargetSdkEqualChange =
-                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
 
         assertThat(stateTargetSdkLessChange).isEqualTo(
                 new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK,
                                          TARGET_SDK_BEFORE));
-        assertThat(stateTargetSdkEqualChange).isEqualTo(
-                new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK, TARGET_SDK));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index f35eecf..b7692f9 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -44,9 +44,9 @@
     @Test
     public void testUpdateOverlaysForUser() {
         final OverlayManagerServiceImpl impl = getImpl();
-        installTargetPackage(TARGET, USER);
-        installTargetPackage("some.other.target", USER);
-        installOverlayPackage(OVERLAY, TARGET, USER);
+        addSystemPackage(target(TARGET), USER);
+        addSystemPackage(target("some.other.target"), USER);;
+        addSystemPackage(overlay(OVERLAY, TARGET), USER);
 
         // do nothing, expect no change
         final List<String> a = impl.updateOverlaysForUser(USER);
@@ -54,8 +54,7 @@
         assertTrue(a.contains(TARGET));
 
         // upgrade overlay, keep target
-        beginUpgradeOverlayPackage(OVERLAY, USER);
-        endUpgradeOverlayPackage(OVERLAY, TARGET, USER);
+        addSystemPackage(overlay(OVERLAY, TARGET), USER);
 
         final List<String> b = impl.updateOverlaysForUser(USER);
         assertEquals(1, b.size());
@@ -67,7 +66,7 @@
         assertTrue(c.contains(TARGET));
 
         // upgrade overlay, switch to new target
-        addOverlayPackage(OVERLAY, "some.other.target", USER, true, false, 0);
+        addSystemPackage(overlay(OVERLAY, "some.other.target"), USER);
         final List<String> d = impl.updateOverlaysForUser(USER);
         assertEquals(2, d.size());
         assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
@@ -81,23 +80,24 @@
     @Test
     public void testImmutableEnabledChange() {
         final OverlayManagerServiceImpl impl = getImpl();
-        installTargetPackage(TARGET, USER);
+        installNewPackage(target(TARGET), USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
 
-        addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0);
+        configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */);
         impl.updateOverlaysForUser(USER);
         final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o1);
         assertFalse(o1.isEnabled());
         assertFalse(o1.isMutable);
 
-        addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0);
+        configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
         impl.updateOverlaysForUser(USER);
         final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o2);
         assertTrue(o2.isEnabled());
         assertFalse(o2.isMutable);
 
-        addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0);
+        configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */);
         impl.updateOverlaysForUser(USER);
         final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o3);
@@ -108,23 +108,24 @@
     @Test
     public void testMutableEnabledChangeHasNoEffect() {
         final OverlayManagerServiceImpl impl = getImpl();
-        installTargetPackage(TARGET, USER);
+        installNewPackage(target(TARGET), USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
 
-        addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0);
         impl.updateOverlaysForUser(USER);
         final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o1);
         assertFalse(o1.isEnabled());
         assertTrue(o1.isMutable);
 
-        addOverlayPackage(OVERLAY, TARGET, USER, true, true, 0);
+        configureSystemOverlay(OVERLAY, true /* mutable */, true /* enabled */, 0 /* priority */);
         impl.updateOverlaysForUser(USER);
         final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o2);
         assertFalse(o2.isEnabled());
         assertTrue(o2.isMutable);
 
-        addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0);
+        configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
         impl.updateOverlaysForUser(USER);
         final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o3);
@@ -135,15 +136,16 @@
     @Test
     public void testMutableEnabledToImmutableEnabled() {
         final OverlayManagerServiceImpl impl = getImpl();
-        installTargetPackage(TARGET, USER);
+        installNewPackage(target(TARGET), USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
 
         final BiConsumer<Boolean, Boolean> setOverlay = (mutable, enabled) -> {
-            addOverlayPackage(OVERLAY, TARGET, USER, mutable, enabled, 0);
+            configureSystemOverlay(OVERLAY, mutable, enabled, 0 /* priority */);
             impl.updateOverlaysForUser(USER);
-            final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
-            assertNotNull(o1);
-            assertEquals(enabled, o1.isEnabled());
-            assertEquals(mutable, o1.isMutable);
+            final OverlayInfo o = impl.getOverlayInfo(OVERLAY, USER);
+            assertNotNull(o);
+            assertEquals(enabled, o.isEnabled());
+            assertEquals(mutable, o.isMutable);
         };
 
         // Immutable/enabled -> mutable/enabled
@@ -178,70 +180,76 @@
     @Test
     public void testMutablePriorityChange() {
         final OverlayManagerServiceImpl impl = getImpl();
-        installTargetPackage(TARGET, USER);
-        addOverlayPackage(OVERLAY, TARGET, USER, true, true, 0);
-        addOverlayPackage(OVERLAY2, TARGET, USER, true, true, 1);
+        installNewPackage(target(TARGET), USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        installNewPackage(overlay(OVERLAY2, TARGET), USER);
+        configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
+        configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 1 /* priority */);
         impl.updateOverlaysForUser(USER);
 
         final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o1);
         assertEquals(0, o1.priority);
+        assertFalse(o1.isEnabled());
 
         final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
         assertNotNull(o2);
         assertEquals(1, o2.priority);
+        assertFalse(o2.isEnabled());
 
         // Overlay priority changing between reboots should not affect enable state of mutable
-        // overlays
+        // overlays.
         impl.setEnabled(OVERLAY, true, USER);
 
         // Reorder the overlays
-        addOverlayPackage(OVERLAY, TARGET, USER, true, true, 1);
-        addOverlayPackage(OVERLAY2, TARGET, USER, true, true, 0);
+        configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 1 /* priority */);
+        configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 0 /* priority */);
         impl.updateOverlaysForUser(USER);
 
         final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o3);
         assertEquals(1, o3.priority);
+        assertTrue(o3.isEnabled());
 
         final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
         assertNotNull(o4);
         assertEquals(0, o4.priority);
-        assertTrue(o1.isEnabled());
+        assertFalse(o4.isEnabled());
     }
 
     @Test
     public void testImmutablePriorityChange() {
         final OverlayManagerServiceImpl impl = getImpl();
-        installTargetPackage(TARGET, USER);
-        addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0);
-        addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 1);
+        installNewPackage(target(TARGET), USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        installNewPackage(overlay(OVERLAY2, TARGET), USER);
+        configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
+        configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 1 /* priority */);
         impl.updateOverlaysForUser(USER);
 
         final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o1);
         assertEquals(0, o1.priority);
+        assertTrue(o1.isEnabled());
 
         final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
         assertNotNull(o2);
         assertEquals(1, o2.priority);
-
-        // Overlay priority changing between reboots should not affect enable state of mutable
-        // overlays
-        impl.setEnabled(OVERLAY, true, USER);
+        assertTrue(o2.isEnabled());
 
         // Reorder the overlays
-        addOverlayPackage(OVERLAY, TARGET, USER, false, true, 1);
-        addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 0);
+        configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 1 /* priority */);
+        configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 0 /* priority */);
         impl.updateOverlaysForUser(USER);
 
         final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
         assertNotNull(o3);
         assertEquals(1, o3.priority);
+        assertTrue(o3.isEnabled());
 
         final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
         assertNotNull(o4);
         assertEquals(0, o4.priority);
-        assertTrue(o1.isEnabled());
+        assertTrue(o4.isEnabled());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index cd73432..b25af05 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.om.OverlayInfo;
+import android.os.OverlayablePolicy;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -49,11 +50,9 @@
     private static final String OVERLAY3 = OVERLAY + "3";
     private static final int USER3 = USER2 + 1;
 
-    // tests: basics
-
     @Test
-    public void testGetOverlayInfo() throws Exception {
-        installOverlayPackage(OVERLAY, TARGET, USER);
+    public void testGetOverlayInfo() {
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
 
         final OverlayManagerServiceImpl impl = getImpl();
         final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER);
@@ -64,10 +63,10 @@
     }
 
     @Test
-    public void testGetOverlayInfosForTarget() throws Exception {
-        installOverlayPackage(OVERLAY, TARGET, USER);
-        installOverlayPackage(OVERLAY2, TARGET, USER);
-        installOverlayPackage(OVERLAY3, TARGET, USER2);
+    public void testGetOverlayInfosForTarget() {
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        installNewPackage(overlay(OVERLAY2, TARGET), USER);
+        installNewPackage(overlay(OVERLAY3, TARGET), USER2);
 
         final OverlayManagerServiceImpl impl = getImpl();
         final List<OverlayInfo> ois = impl.getOverlayInfosForTarget(TARGET, USER);
@@ -89,11 +88,11 @@
     }
 
     @Test
-    public void testGetOverlayInfosForUser() throws Exception {
-        installTargetPackage(TARGET, USER);
-        installOverlayPackage(OVERLAY, TARGET, USER);
-        installOverlayPackage(OVERLAY2, TARGET, USER);
-        installOverlayPackage(OVERLAY3, TARGET2, USER);
+    public void testGetOverlayInfosForUser() {
+        installNewPackage(target(TARGET), USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        installNewPackage(overlay(OVERLAY2, TARGET), USER);
+        installNewPackage(overlay(OVERLAY3, TARGET2), USER);
 
         final OverlayManagerServiceImpl impl = getImpl();
         final Map<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER);
@@ -116,91 +115,86 @@
     }
 
     @Test
-    public void testPriority() throws Exception {
-        installOverlayPackage(OVERLAY, TARGET, USER);
-        installOverlayPackage(OVERLAY2, TARGET, USER);
-        installOverlayPackage(OVERLAY3, TARGET, USER);
+    public void testPriority() {
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        installNewPackage(overlay(OVERLAY2, TARGET), USER);
+        installNewPackage(overlay(OVERLAY3, TARGET), USER);
 
         final OverlayManagerServiceImpl impl = getImpl();
         final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
         final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
         final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY3, USER);
 
-        assertOverlayInfoList(TARGET, USER, o1, o2, o3);
+        assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
         assertTrue(impl.setLowestPriority(OVERLAY3, USER));
-        assertOverlayInfoList(TARGET, USER, o3, o1, o2);
+        assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
 
         assertTrue(impl.setHighestPriority(OVERLAY3, USER));
-        assertOverlayInfoList(TARGET, USER, o1, o2, o3);
+        assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
         assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
-        assertOverlayInfoList(TARGET, USER, o2, o1, o3);
+        assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
     }
 
     @Test
-    public void testOverlayInfoStateTransitions() throws Exception {
+    public void testOverlayInfoStateTransitions() {
         final OverlayManagerServiceImpl impl = getImpl();
         assertNull(impl.getOverlayInfo(OVERLAY, USER));
 
-        installOverlayPackage(OVERLAY, TARGET, USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
         assertState(STATE_MISSING_TARGET, OVERLAY, USER);
 
-        installTargetPackage(TARGET, USER);
+        final DummyDeviceState.PackageBuilder target = target(TARGET);
+        installNewPackage(target, USER);
         assertState(STATE_DISABLED, OVERLAY, USER);
 
         impl.setEnabled(OVERLAY, true, USER);
         assertState(STATE_ENABLED, OVERLAY, USER);
 
         // target upgrades do not change the state of the overlay
-        beginUpgradeTargetPackage(TARGET, USER);
+        upgradePackage(target, USER);
         assertState(STATE_ENABLED, OVERLAY, USER);
 
-        endUpgradeTargetPackage(TARGET, USER);
-        assertState(STATE_ENABLED, OVERLAY, USER);
-
-        uninstallTargetPackage(TARGET, USER);
+        uninstallPackage(TARGET, USER);
         assertState(STATE_MISSING_TARGET, OVERLAY, USER);
 
-        installTargetPackage(TARGET, USER);
+        installNewPackage(target, USER);
         assertState(STATE_ENABLED, OVERLAY, USER);
     }
 
     @Test
-    public void testOnOverlayPackageUpgraded() throws Exception {
-        final OverlayManagerServiceImpl impl = getImpl();
+    public void testOnOverlayPackageUpgraded() {
         final DummyListener listener = getListener();
-        installTargetPackage(TARGET, USER);
-        installOverlayPackage(OVERLAY, TARGET, USER);
-        impl.onOverlayPackageReplacing(OVERLAY, USER);
+        final DummyDeviceState.PackageBuilder target = target(TARGET);
+        final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
+        installNewPackage(target, USER);
+        installNewPackage(overlay, USER);
         listener.count = 0;
-        impl.onOverlayPackageReplaced(OVERLAY, USER);
-        assertEquals(1, listener.count);
-
-        // upgrade to a version where the overlay has changed its target
-        beginUpgradeOverlayPackage(OVERLAY, USER);
-        listener.count = 0;
-        endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER);
-        // expect once for the old target package, once for the new target package
+        upgradePackage(overlay, USER);
         assertEquals(2, listener.count);
 
-        beginUpgradeOverlayPackage(OVERLAY, USER);
+        // upgrade to a version where the overlay has changed its target
+        // expect once for the old target package, once for the new target package
         listener.count = 0;
-        endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER);
-        assertEquals(1, listener.count);
+        final DummyDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
+        upgradePackage(overlay2, USER);
+        assertEquals(3, listener.count);
+
+        listener.count = 0;
+        upgradePackage(overlay2, USER);
+        assertEquals(2, listener.count);
     }
 
-    // tests: listener interface
-
     @Test
-    public void testListener() throws Exception {
+    public void testListener() {
         final OverlayManagerServiceImpl impl = getImpl();
         final DummyListener listener = getListener();
-        installOverlayPackage(OVERLAY, TARGET, USER);
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
         assertEquals(1, listener.count);
         listener.count = 0;
 
-        installTargetPackage(TARGET, USER);
+        installNewPackage(target(TARGET), USER);
         assertEquals(1, listener.count);
         listener.count = 0;
 
@@ -211,4 +205,49 @@
         impl.setEnabled(OVERLAY, true, USER);
         assertEquals(0, listener.count);
     }
+
+    @Test
+    public void testConfigurator() {
+        final DummyPackageManagerHelper packageManager = getPackageManager();
+        packageManager.overlayableConfigurator = "actor";
+        packageManager.overlayableConfiguratorTargets = new String[]{TARGET};
+        reinitializeImpl();
+
+        installNewPackage(target("actor").setCertificate("one"), USER);
+        installNewPackage(target(TARGET)
+                .addOverlayable("TestResources")
+                .setCertificate("two"), USER);
+
+        final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET, "TestResources")
+                .setCertificate("one");
+        installNewPackage(overlay, USER);
+
+        final DummyIdmapDaemon idmapDaemon = getIdmapDaemon();
+        final DummyIdmapDaemon.IdmapHeader idmap = idmapDaemon.getIdmap(overlay.build().apkPath);
+        assertNotNull(idmap);
+        assertEquals(OverlayablePolicy.ACTOR_SIGNATURE,
+                idmap.policies & OverlayablePolicy.ACTOR_SIGNATURE);
+    }
+
+    @Test
+    public void testConfiguratorDifferentSignatures() {
+        final DummyPackageManagerHelper packageManager = getPackageManager();
+        packageManager.overlayableConfigurator = "actor";
+        packageManager.overlayableConfiguratorTargets = new String[]{TARGET};
+        reinitializeImpl();
+
+        installNewPackage(target("actor").setCertificate("one"), USER);
+        installNewPackage(target(TARGET)
+                .addOverlayable("TestResources")
+                .setCertificate("two"), USER);
+
+        final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET, "TestResources")
+                .setCertificate("two");
+        installNewPackage(overlay, USER);
+
+        final DummyIdmapDaemon idmapDaemon = getIdmapDaemon();
+        final DummyIdmapDaemon.IdmapHeader idmap = idmapDaemon.getIdmap(overlay.build().apkPath);
+        assertNotNull(idmap);
+        assertEquals(0, idmap.policies & OverlayablePolicy.ACTOR_SIGNATURE);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 9eda718..ec6a481 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -17,6 +17,7 @@
 package com.android.server.om;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -26,17 +27,19 @@
 import android.content.om.OverlayableInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.content.om.OverlayConfig;
 
+import org.junit.Assert;
 import org.junit.Before;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Iterator;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -47,29 +50,48 @@
     private OverlayManagerServiceImpl mImpl;
     private DummyDeviceState mState;
     private DummyListener mListener;
+    private DummyPackageManagerHelper mPackageManager;
+    private DummyIdmapDaemon mIdmapDaemon;
+    private OverlayConfig mOverlayConfig;
 
     @Before
     public void setUp() {
         mState = new DummyDeviceState();
         mListener = new DummyListener();
-        final DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState);
+        mPackageManager = new DummyPackageManagerHelper(mState);
+        mIdmapDaemon = new DummyIdmapDaemon(mState);
+        mOverlayConfig = mock(OverlayConfig.class);
+        when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY);
+        when(mOverlayConfig.isEnabled(any())).thenReturn(false);
+        when(mOverlayConfig.isMutable(any())).thenReturn(true);
+        reinitializeImpl();
+    }
 
-        mImpl = new OverlayManagerServiceImpl(pmh,
-                new DummyIdmapManager(mState, pmh),
+    void reinitializeImpl() {
+        mImpl = new OverlayManagerServiceImpl(mPackageManager,
+                new IdmapManager(mIdmapDaemon, mPackageManager),
                 new OverlayManagerSettings(),
-                mState.mOverlayConfig,
+                mOverlayConfig,
                 new String[0],
                 mListener);
     }
 
-    public OverlayManagerServiceImpl getImpl() {
+    OverlayManagerServiceImpl getImpl() {
         return mImpl;
     }
 
-    public DummyListener getListener() {
+    DummyListener getListener() {
         return mListener;
     }
 
+    DummyPackageManagerHelper getPackageManager() {
+        return mPackageManager;
+    }
+
+    DummyIdmapDaemon getIdmapDaemon() {
+        return mIdmapDaemon;
+    }
+
     void assertState(@State int expected, final String overlayPackageName, int userId) {
         final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId);
         if (info == null) {
@@ -81,7 +103,7 @@
         assertEquals(msg, expected, info.state);
     }
 
-    void assertOverlayInfoList(final String targetPackageName, int userId,
+    void assertOverlayInfoForTarget(final String targetPackageName, int userId,
             OverlayInfo... overlayInfos) {
         final List<OverlayInfo> expected =
                 mImpl.getOverlayInfosForTarget(targetPackageName, userId);
@@ -89,198 +111,202 @@
         assertEquals(expected, actual);
     }
 
-    /**
-     * Creates an overlay configured through {@link OverlayConfig}.
-     *
-     * @throws IllegalStateException if the package is already installed
-     */
-    void addOverlayPackage(String packageName, String targetPackageName, int userId,
-            boolean mutable, boolean enabled, int priority) {
-        mState.addOverlay(packageName, targetPackageName, userId, mutable, enabled, priority);
+    DummyDeviceState.PackageBuilder target(String packageName) {
+        return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
+                null /* targetOverlayableName */);
+    }
+
+    DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) {
+        return overlay(packageName, targetPackageName, null /* targetOverlayableName */);
+    }
+
+    DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName,
+            String targetOverlayableName) {
+        return new DummyDeviceState.PackageBuilder(packageName, targetPackageName,
+                targetOverlayableName);
+    }
+
+    void addSystemPackage(DummyDeviceState.PackageBuilder pkg, int userId) {
+        mState.add(pkg, userId);
+    }
+
+    void configureSystemOverlay(String packageName, boolean mutable, boolean enabled,
+            int priority) {
+        when(mOverlayConfig.getPriority(packageName)).thenReturn(priority);
+        when(mOverlayConfig.isEnabled(packageName)).thenReturn(enabled);
+        when(mOverlayConfig.isMutable(packageName)).thenReturn(mutable);
     }
 
     /**
-     * Adds the target package to the device.
+     * Adds the package to the device.
      *
      * This corresponds to when the OMS receives the
      * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast.
      *
-     * @throws IllegalStateException if the package is not currently installed
+     * @throws IllegalStateException if the package is currently installed
      */
-    void installTargetPackage(String packageName, int userId) {
-        if (mState.select(packageName, userId) != null) {
-            throw new IllegalStateException("package already installed");
+    void installNewPackage(DummyDeviceState.PackageBuilder pkg, int userId) {
+        if (mState.select(pkg.packageName, userId) != null) {
+            throw new IllegalStateException("package " + pkg.packageName + " already installed");
         }
-        mState.addTarget(packageName, userId);
-        mImpl.onTargetPackageAdded(packageName, userId);
+        mState.add(pkg, userId);
+        if (pkg.targetPackage == null) {
+            mImpl.onTargetPackageAdded(pkg.packageName, userId);
+        } else {
+            mImpl.onOverlayPackageAdded(pkg.packageName, userId);
+        }
     }
 
     /**
-     * Begins upgrading the target package.
+     * Begins upgrading the package.
      *
      * This corresponds to when the OMS receives the
      * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the
-     * {@link android.content.Intent#EXTRA_REPLACING} extra.
-     *
-     * @throws IllegalStateException if the package is not currently installed
-     */
-    void beginUpgradeTargetPackage(String packageName, int userId) {
-        if (mState.select(packageName, userId) == null) {
-            throw new IllegalStateException("package not installed");
-        }
-        mImpl.onTargetPackageReplacing(packageName, userId);
-    }
-
-    /**
-     * Ends upgrading the target package.
-     *
-     * This corresponds to when the OMS receives the
+     * {@link android.content.Intent#EXTRA_REPLACING} extra and then receives the
      * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
      * {@link android.content.Intent#EXTRA_REPLACING} extra.
      *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void endUpgradeTargetPackage(String packageName, int userId) {
-        if (mState.select(packageName, userId) == null) {
-            throw new IllegalStateException("package not installed");
+    void upgradePackage(DummyDeviceState.PackageBuilder pkg, int userId) {
+        final DummyDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
+        if (replacedPackage == null) {
+            throw new IllegalStateException("package " + pkg.packageName + " not installed");
         }
-        mState.addTarget(packageName, userId);
-        mImpl.onTargetPackageReplaced(packageName, userId);
+        if (replacedPackage.targetPackageName != null) {
+            mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
+        }
+
+        mState.add(pkg, userId);
+        if (pkg.targetPackage == null) {
+            mImpl.onTargetPackageReplaced(pkg.packageName, userId);
+        } else {
+            mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
+        }
     }
 
     /**
-     * Removes the target package from the device.
+     * Removes the package from the device.
      *
      * This corresponds to when the OMS receives the
      * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast.
      *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void uninstallTargetPackage(String packageName, int userId) {
-        if (mState.select(packageName, userId) == null) {
-            throw new IllegalStateException("package not installed");
+    void uninstallPackage(String packageName, int userId) {
+        final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+        if (pkg == null) {
+            throw new IllegalStateException("package " + packageName+ " not installed");
         }
-        mState.remove(packageName, userId);
-        mImpl.onTargetPackageRemoved(packageName, userId);
+        mState.remove(pkg.packageName);
+        if (pkg.targetPackageName == null) {
+            mImpl.onTargetPackageRemoved(pkg.packageName, userId);
+        } else {
+            mImpl.onOverlayPackageRemoved(pkg.packageName, userId);
+        }
     }
 
-    /**
-     * Adds the overlay package to the device.
-     *
-     * This corresponds to when the OMS receives the
-     * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast.
-     *
-     * @throws IllegalStateException if the package is already installed
-     */
-    void installOverlayPackage(String packageName, String targetPackageName, int userId) {
-        if (mState.select(packageName, userId) != null) {
-            throw new IllegalStateException("package already installed");
-        }
-        mState.addOverlay(packageName, targetPackageName, userId);
-        mImpl.onOverlayPackageAdded(packageName, userId);
-    }
+    /** Represents the state of packages installed on a fake device. */
+    static class DummyDeviceState {
+        private ArrayMap<String, Package> mPackages = new ArrayMap<>();
 
-    /**
-     * Begins upgrading the overlay package.
-     *
-     * This corresponds to when the OMS receives the
-     * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the
-     * {@link android.content.Intent#EXTRA_REPLACING} extra.
-     *
-     * @throws IllegalStateException if the package is not currently installed
-     */
-    void beginUpgradeOverlayPackage(String packageName, int userId) {
-        if (mState.select(packageName, userId) == null) {
-            throw new IllegalStateException("package not installed, cannot upgrade");
-        }
+        void add(PackageBuilder pkgBuilder, int userId) {
+            final Package pkg = pkgBuilder.build();
+            final Package previousPkg = select(pkg.packageName, userId);
+            mPackages.put(pkg.packageName, pkg);
 
-        mImpl.onOverlayPackageReplacing(packageName, userId);
-    }
-
-    /**
-     * Ends upgrading the overlay package, potentially changing its target package.
-     *
-     * This corresponds to when the OMS receives the
-     * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
-     * {@link android.content.Intent#EXTRA_REPLACING} extra.
-     *
-     * @throws IllegalStateException if the package is not currently installed
-     */
-    void endUpgradeOverlayPackage(String packageName, String targetPackageName, int userId) {
-        if (mState.select(packageName, userId) == null) {
-            throw new IllegalStateException("package not installed, cannot upgrade");
-        }
-
-        mState.addOverlay(packageName, targetPackageName, userId);
-        mImpl.onOverlayPackageReplaced(packageName, userId);
-    }
-
-    private static final class DummyDeviceState {
-        private List<Package> mPackages = new ArrayList<>();
-        private OverlayConfig mOverlayConfig = mock(OverlayConfig.class);
-
-        /** Adds a non-overlay to the device. */
-        public void addTarget(String packageName, int userId) {
-            remove(packageName, userId);
-            mPackages.add(new Package(packageName, userId, null, false, false, 0));
-        }
-
-        /** Adds an overlay to the device. */
-        public void addOverlay(String packageName, String targetPackageName, int userId) {
-            addOverlay(packageName, targetPackageName, userId, true, false, OverlayConfig.DEFAULT_PRIORITY);
-        }
-
-        /** Adds a configured overlay to the device. */
-        public void addOverlay(String packageName, String targetPackageName, int userId,
-                boolean mutable, boolean enabled, int priority) {
-            remove(packageName, userId);
-            mPackages.add(new Package(packageName, userId, targetPackageName, mutable, enabled,
-                    priority));
-            when(mOverlayConfig.getPriority(packageName)).thenReturn(priority);
-            when(mOverlayConfig.isEnabled(packageName)).thenReturn(enabled);
-            when(mOverlayConfig.isMutable(packageName)).thenReturn(mutable);
-        }
-
-        /** Remove a package from the device. */
-        public void remove(String packageName, int userId) {
-            final Iterator<Package> iter = mPackages.iterator();
-            while (iter.hasNext()) {
-                final Package pkg = iter.next();
-                if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
-                    iter.remove();
-                    return;
-                }
+            pkg.installedUserIds.add(userId);
+            if (previousPkg != null) {
+                pkg.installedUserIds.addAll(previousPkg.installedUserIds);
             }
         }
 
-        /** Retrieves all packages on device for a particular user. */
-        public List<Package> select(int userId) {
-            return mPackages.stream().filter(p -> p.userId == userId).collect(Collectors.toList());
+        void remove(String packageName) {
+            mPackages.remove(packageName);
         }
 
-        /** Retrieves the package with the specified package name for a particular user. */
-        public Package select(String packageName, int userId) {
-            return mPackages.stream().filter(
-                    p -> p.packageName.equals(packageName) && p.userId == userId)
-                    .findFirst().orElse(null);
+        void uninstall(String packageName, int userId) {
+            final Package pkg = mPackages.get(packageName);
+            if (pkg != null) {
+                pkg.installedUserIds.remove(userId);
+            }
         }
 
-        private static final class Package {
-            public final String packageName;
-            public final int userId;
-            public final String targetPackageName;
-            public final boolean mutable;
-            public final boolean enabled;
-            public final int priority;
+        List<Package> select(int userId) {
+            return mPackages.values().stream().filter(p -> p.installedUserIds.contains(userId))
+                    .collect(Collectors.toList());
+        }
 
-            private Package(String packageName, int userId, String targetPackageName,
-                    boolean mutable, boolean enabled, int priority) {
+        Package select(String packageName, int userId) {
+            final Package pkg = mPackages.get(packageName);
+            return pkg != null && pkg.installedUserIds.contains(userId) ? pkg : null;
+        }
+
+        private Package selectFromPath(String path) {
+            return mPackages.values().stream()
+                    .filter(p -> p.apkPath.equals(path)).findFirst().orElse(null);
+        }
+
+        static final class PackageBuilder {
+            private String packageName;
+            private String targetPackage;
+            private String certificate = "[default]";
+            private int version = 0;
+            private ArrayList<String> overlayableNames = new ArrayList<>();
+            private String targetOverlayableName;
+
+            private PackageBuilder(String packageName, String targetPackage,
+                    String targetOverlayableName) {
                 this.packageName = packageName;
-                this.userId = userId;
+                this.targetPackage = targetPackage;
+                this.targetOverlayableName = targetOverlayableName;
+            }
+
+            PackageBuilder setCertificate(String certificate) {
+                this.certificate = certificate;
+                return this;
+            }
+
+            PackageBuilder addOverlayable(String overlayableName) {
+                overlayableNames.add(overlayableName);
+                return this;
+            }
+
+            PackageBuilder setVersion(int version) {
+                this.version = version;
+                return this;
+            }
+
+            Package build() {
+                final String apkPath = String.format("%s/%s/base.apk",
+                        targetPackage == null ? "/system/app/:" : "/vendor/overlay/:",
+                        packageName);
+                final Package newPackage = new Package(packageName, targetPackage,
+                        targetOverlayableName, version, apkPath, certificate);
+                newPackage.overlayableNames.addAll(overlayableNames);
+                return newPackage;
+            }
+        }
+
+        static final class Package {
+            final String packageName;
+            final String targetPackageName;
+            final String targetOverlayableName;
+            final int versionCode;
+            final String apkPath;
+            final String certificate;
+            final ArrayList<String> overlayableNames = new ArrayList<>();
+            private final ArraySet<Integer> installedUserIds = new ArraySet<>();
+
+            private Package(String packageName, String targetPackageName,
+                    String targetOverlayableName, int versionCode, String apkPath,
+                    String certificate) {
+                this.packageName = packageName;
                 this.targetPackageName = targetPackageName;
-                this.mutable = mutable;
-                this.enabled = enabled;
-                this.priority = priority;
+                this.targetOverlayableName = targetOverlayableName;
+                this.versionCode = versionCode;
+                this.apkPath = apkPath;
+                this.certificate = certificate;
             }
         }
     }
@@ -288,6 +314,8 @@
     static final class DummyPackageManagerHelper implements PackageManagerHelper,
             OverlayableInfoCallback {
         private final DummyDeviceState mState;
+        String[] overlayableConfiguratorTargets = new String[0];
+        String overlayableConfigurator = "";
 
         private DummyPackageManagerHelper(DummyDeviceState state) {
             mState = state;
@@ -300,13 +328,12 @@
                 return null;
             }
             final ApplicationInfo ai = new ApplicationInfo();
-            ai.sourceDir = String.format("%s/%s/base.apk",
-                    pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/",
-                    pkg.packageName);
+            ai.sourceDir = pkg.apkPath;
             PackageInfo pi = new PackageInfo();
             pi.applicationInfo = ai;
             pi.packageName = pkg.packageName;
             pi.overlayTarget = pkg.targetPackageName;
+            pi.targetOverlayableName = pkg.targetOverlayableName;
             pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
             return pi;
         }
@@ -314,14 +341,16 @@
         @Override
         public boolean signaturesMatching(@NonNull String packageName1,
                 @NonNull String packageName2, int userId) {
-            return false;
+            final DummyDeviceState.Package pkg1 = mState.select(packageName1, userId);
+            final DummyDeviceState.Package pkg2 = mState.select(packageName2, userId);
+            return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate);
         }
 
         @Override
         public List<PackageInfo> getOverlayPackages(int userId) {
             return mState.select(userId).stream()
                     .filter(p -> p.targetPackageName != null)
-                    .map(p -> getPackageInfo(p.packageName, p.userId))
+                    .map(p -> getPackageInfo(p.packageName, userId))
                     .collect(Collectors.toList());
         }
 
@@ -329,7 +358,11 @@
         @Override
         public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
                 @NonNull String targetOverlayableName, int userId) {
-            throw new UnsupportedOperationException();
+            final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+            if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) {
+                return null;
+            }
+            return new OverlayableInfo(targetOverlayableName, null /* actor */);
         }
 
         @Nullable
@@ -341,69 +374,98 @@
         @NonNull
         @Override
         public Map<String, Map<String, String>> getNamedActors() {
-            throw new UnsupportedOperationException();
+            return Collections.emptyMap();
         }
 
         @Override
         public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) {
-            throw new UnsupportedOperationException();
+            final DummyDeviceState.Package pkg = mState.select(targetPackageName, userId);
+            return pkg != null && pkg.overlayableNames.contains(targetPackageName);
         }
 
         @Override
         public void enforcePermission(String permission, String message) throws SecurityException {
             throw new UnsupportedOperationException();
         }
+
+        @Override
+        public String[] getOverlayableConfiguratorTargets() {
+            return overlayableConfiguratorTargets;
+        }
+
+        @Override
+        public String getOverlayableConfigurator() {
+            return overlayableConfigurator;
+        }
     }
 
-    static class DummyIdmapManager extends IdmapManager {
+    static class DummyIdmapDaemon extends IdmapDaemon {
         private final DummyDeviceState mState;
-        private Set<String> mIdmapFiles = new ArraySet<>();
+        private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>();
 
-        private DummyIdmapManager(DummyDeviceState state,
-                DummyPackageManagerHelper packageManagerHelper) {
-            super(packageManagerHelper);
-            mState = state;
+        DummyIdmapDaemon(DummyDeviceState state) {
+            this.mState = state;
+        }
+
+        private int getCrc(@NonNull final String path) {
+            final DummyDeviceState.Package pkg = mState.selectFromPath(path);
+            Assert.assertNotNull(pkg);
+            return pkg.versionCode;
         }
 
         @Override
-        boolean createIdmap(@NonNull final PackageInfo targetPackage,
-                @NonNull final PackageInfo overlayPackage, int userId) {
-            final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId);
-            if (t == null) {
+        String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
+                int userId) {
+            mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath),
+                    getCrc(overlayPath), targetPath, policies, enforce));
+            return overlayPath;
+        }
+
+        @Override
+        boolean removeIdmap(String overlayPath, int userId) {
+            return mIdmapFiles.remove(overlayPath) != null;
+        }
+
+        @Override
+        boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
+                int userId) {
+            final IdmapHeader idmap = mIdmapFiles.get(overlayPath);
+            if (idmap == null) {
                 return false;
             }
-            final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId);
-            if (o == null) {
-                return false;
+            return idmap.isUpToDate(getCrc(targetPath), getCrc(overlayPath), targetPath);
+        }
+
+        @Override
+        boolean idmapExists(String overlayPath, int userId) {
+            return mIdmapFiles.containsKey(overlayPath);
+        }
+
+        IdmapHeader getIdmap(String overlayPath) {
+            return mIdmapFiles.get(overlayPath);
+        }
+
+        static class IdmapHeader {
+            private final int targetCrc;
+            private final int overlayCrc;
+            final String targetPath;
+            final int policies;
+            final boolean enforceOverlayable;
+
+            private IdmapHeader(int targetCrc, int overlayCrc, String targetPath, int policies,
+                    boolean enforceOverlayable) {
+                this.targetCrc = targetCrc;
+                this.overlayCrc = overlayCrc;
+                this.targetPath = targetPath;
+                this.policies = policies;
+                this.enforceOverlayable = enforceOverlayable;
             }
-            final String key = createKey(overlayPackage.packageName, userId);
-            return mIdmapFiles.add(key);
-        }
 
-        @Override
-        boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
-            final String key = createKey(oi.packageName, oi.userId);
-            if (!mIdmapFiles.contains(key)) {
-                return false;
+            private boolean isUpToDate(int expectedTargetCrc, int expectedOverlayCrc,
+                    String expectedTargetPath) {
+                return expectedTargetCrc == targetCrc && expectedOverlayCrc == overlayCrc
+                        && expectedTargetPath.equals(targetPath);
             }
-            mIdmapFiles.remove(key);
-            return true;
-        }
-
-        @Override
-        boolean idmapExists(@NonNull final OverlayInfo oi) {
-            final String key = createKey(oi.packageName, oi.userId);
-            return mIdmapFiles.contains(key);
-        }
-
-        @Override
-        boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
-            final String key = createKey(overlayPackage.packageName, userId);
-            return mIdmapFiles.contains(key);
-        }
-
-        private String createKey(@NonNull final String packageName, final int userId) {
-            return String.format("%s:%d", packageName, userId);
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index e47792f..68bc584 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -138,7 +138,7 @@
         // Rotation is ignored so because the display size is close to square (700/600<1.333).
         assertTrue(mActivity.mDisplayContent.ignoreRotationForApps());
 
-        final Rect displayBounds = mActivity.mDisplayContent.getBounds();
+        final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
         final float aspectRatio = 1.2f;
         mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = aspectRatio;
         prepareUnresizable(-1f, SCREEN_ORIENTATION_UNSPECIFIED);
@@ -160,13 +160,22 @@
         assertFitted();
 
         // After the orientation of activity is changed, even display is not rotated, the aspect
-        // ratio should be the same (bounds=[0, 0 - 600, 600], appBounds=[0, 100 - 600, 600]).
+        // ratio should be the same (appBounds=[9, 100 - 592, 800], x-offset=round((600-583)/2)=9).
         assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
         // The notch is still on top.
         assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
+
+        // Close-to-square display can rotate without being restricted by the requested orientation.
+        // The notch becomes on the left side. The activity is horizontal centered in 100 ~ 800.
+        // So the bounds and appBounds will be [200, 0 - 700, 600] (500x600) that is still fitted.
+        // Left = 100 + (800 - 100 - 500) / 2 = 200.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+        assertFitted();
+        assertEquals(appBounds.left,
+                notchHeight + (displayBounds.width() - notchHeight - appBounds.width()) / 2);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 8ce5daa..e9ed20b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -603,6 +603,29 @@
     }
 
     @Test
+    public void testRequestResizeForBlastSync() {
+        final WindowState win = mChildAppWindowAbove;
+        makeWindowVisible(win, win.getParentWindow());
+        win.mLayoutSeq = win.getDisplayContent().mLayoutSeq;
+        win.reportResized();
+        win.updateResizingWindowIfNeeded();
+        assertThat(mWm.mResizingWindows).doesNotContain(win);
+
+        // Check that the window is in resizing if using blast sync.
+        win.reportResized();
+        win.prepareForSync(mock(BLASTSyncEngine.TransactionReadyListener.class), 1);
+        win.updateResizingWindowIfNeeded();
+        assertThat(mWm.mResizingWindows).contains(win);
+
+        // Don't re-add the window again if it's been reported to the client and still waiting on
+        // the client draw for blast sync.
+        win.reportResized();
+        mWm.mResizingWindows.remove(win);
+        win.updateResizingWindowIfNeeded();
+        assertThat(mWm.mResizingWindows).doesNotContain(win);
+    }
+
+    @Test
     public void testGetTransformationMatrix() {
         final int PARENT_WINDOW_OFFSET = 1;
         final int DISPLAY_IN_PARENT_WINDOW_OFFSET = 2;
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 7d750b7..1a58f17 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -115,7 +115,7 @@
     private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 3000; // 3s between launching apps
     private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save
     private static final int IORAP_TRACE_DURATION_TIMEOUT = 7000; // Allow 7s for trace to complete.
-    private static final int IORAP_TRIAL_LAUNCH_ITERATIONS = 3;  // min 3 launches to merge traces.
+    private static final int IORAP_TRIAL_LAUNCH_ITERATIONS = 5;  // min 5 launches to merge traces.
     private static final int IORAP_COMPILE_CMD_TIMEOUT = 60;  // in seconds: 1 minutes
     private static final int IORAP_COMPILE_MIN_TRACES = 1;  // configure iorapd to need 1 trace.
     private static final int IORAP_COMPILE_RETRIES = 3;  // retry compiler 3 times if it fails.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 83ca9b2..c86d388 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -6314,6 +6314,7 @@
         int netId = network.getNetId();
         callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
         callback.assertNoCallback();
         assertEquals(pref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
@@ -6323,6 +6324,7 @@
         mCellNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mCellNetworkAgent, null);
         inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
         inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
 
         // If the RA prefix appears while DNS discovery is in progress, discovery is stopped and
@@ -6332,6 +6334,7 @@
         expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
         inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
 
         // Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS
         // discovery has succeeded.
@@ -6339,6 +6342,7 @@
         mCellNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mCellNetworkAgent, null);
         inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
         inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
 
         mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
@@ -6346,13 +6350,40 @@
         expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
         inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
 
-        // If the RA prefix reappears, clatd is restarted and prefix discovery is stopped.
+        // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
+        // discovery is not stopped, and there are no callbacks.
+        lp.setNat64Prefix(pref64FromDns);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        callback.assertNoCallback();
+        inOrder.verify(mMockNetd, never()).clatdStop(iface);
+        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        // If the RA is later withdrawn, nothing happens again.
+        lp.setNat64Prefix(null);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        callback.assertNoCallback();
+        inOrder.verify(mMockNetd, never()).clatdStop(iface);
+        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        // If the RA prefix changes, clatd is restarted and prefix discovery is stopped.
         lp.setNat64Prefix(pref64FromRa);
         mCellNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
         inOrder.verify(mMockNetd).clatdStop(iface);
         inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+
+        // Stopping prefix discovery results in a prefix removed notification.
+        mService.mNetdEventCallback.onNat64PrefixEvent(netId, false /* added */,
+                pref64FromDnsStr, 96);
+
         inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
 
         // If the RA prefix changes, clatd is restarted and prefix discovery is not started.
@@ -6360,7 +6391,9 @@
         mCellNetworkAgent.sendLinkProperties(lp);
         expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa);
         inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
         inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString());
         inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
 
@@ -6373,11 +6406,45 @@
         inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
         inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
         inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
 
         // The transition between no prefix and DNS prefix is tested in testStackedLinkProperties.
 
+        // If the same prefix is learned first by DNS and then by RA, and clat is later stopped,
+        // (e.g., because the network disconnects) setPrefix64(netid, "") is never called.
+        lp.setNat64Prefix(null);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+        inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+        mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
+                pref64FromDnsStr, 96);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
+
+        lp.setNat64Prefix(pref64FromDns);
+        mCellNetworkAgent.sendLinkProperties(lp);
         callback.assertNoCallback();
+        inOrder.verify(mMockNetd, never()).clatdStop(iface);
+        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but
+        // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that
+        // clat has been stopped, or the test will be flaky.
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.disconnect();
+        callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        waitFor(cv);
+
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
         mCm.unregisterNetworkCallback(callback);
     }