Merge "Update Passpoint R1 AccessPoint with R2 config" into qt-dev
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index c149195..bd3b673 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -52,7 +52,7 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             textClassificationManager.getTextClassifier();
-            textClassificationManager.invalidate();
+            textClassificationManager.invalidateForTesting();
         }
     }
 
@@ -68,7 +68,7 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             textClassificationManager.getTextClassifier();
-            textClassificationManager.invalidate();
+            textClassificationManager.invalidateForTesting();
         }
     }
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index aacf2c1..95f4a2e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3262,6 +3262,7 @@
     ctor public AutofillId(@NonNull android.view.autofill.AutofillId, int);
     ctor public AutofillId(int, int);
     ctor public AutofillId(@NonNull android.view.autofill.AutofillId, long, int);
+    method public boolean equalsIgnoreSession(@Nullable android.view.autofill.AutofillId);
   }
 
   public final class AutofillManager {
@@ -3390,6 +3391,7 @@
 package android.view.inputmethod {
 
   public final class InputMethodManager {
+    method public int getDisplayId();
     method public boolean isInputMethodPickerShown();
   }
 
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto
index 8e29f96..23e9519 100644
--- a/cmds/am/proto/instrumentation_data.proto
+++ b/cmds/am/proto/instrumentation_data.proto
@@ -38,6 +38,7 @@
 message TestStatus {
     optional sint32 result_code = 3;
     optional ResultsBundle results = 4;
+    optional string logcat = 5;
 }
 
 enum SessionStatusCode {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 70baa87..4d7b5a7 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -38,6 +38,7 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -62,8 +63,15 @@
  * other: Failure
  */
 public class Instrument {
+    private static final String TAG = "am";
+
     public static final String DEFAULT_LOG_DIR = "instrument-logs";
 
+    private static final int STATUS_TEST_PASSED = 0;
+    private static final int STATUS_TEST_STARTED = 1;
+    private static final int STATUS_TEST_FAILED_ASSERTION = -1;
+    private static final int STATUS_TEST_FAILED_OTHER = -2;
+
     private final IActivityManager mAm;
     private final IPackageManager mPm;
     private final IWindowManager mWm;
@@ -207,6 +215,8 @@
 
         private File mLog;
 
+        private long mTestStartMs;
+
         ProtoStatusReporter() {
             if (protoFile) {
                 if (logPath == null) {
@@ -241,10 +251,22 @@
                 Bundle results) {
             final ProtoOutputStream proto = new ProtoOutputStream();
 
-            final long token = proto.start(InstrumentationData.Session.TEST_STATUS);
+            final long testStatusToken = proto.start(InstrumentationData.Session.TEST_STATUS);
+
             proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
             writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
-            proto.end(token);
+
+            if (resultCode == STATUS_TEST_STARTED) {
+                // Logcat -T takes wall clock time (!?)
+                mTestStartMs = System.currentTimeMillis();
+            } else {
+                if (mTestStartMs > 0) {
+                    proto.write(InstrumentationData.TestStatus.LOGCAT, readLogcat(mTestStartMs));
+                }
+                mTestStartMs = 0;
+            }
+
+            proto.end(testStatusToken);
 
             outputProto(proto);
         }
@@ -254,12 +276,12 @@
                 Bundle results) {
             final ProtoOutputStream proto = new ProtoOutputStream();
 
-            final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+            final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS);
             proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
                     InstrumentationData.SESSION_FINISHED);
             proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
             writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
-            proto.end(token);
+            proto.end(sessionStatusToken);
 
             outputProto(proto);
         }
@@ -268,11 +290,11 @@
         public void onError(String errorText, boolean commandError) {
             final ProtoOutputStream proto = new ProtoOutputStream();
 
-            final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+            final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS);
             proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
                     InstrumentationData.SESSION_ABORTED);
             proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
-            proto.end(token);
+            proto.end(sessionStatusToken);
 
             outputProto(proto);
         }
@@ -514,5 +536,43 @@
             }
         }
     }
+
+    private static String readLogcat(long startTimeMs) {
+        try {
+            // Figure out the timestamp arg for logcat.
+            final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+            final String timestamp = format.format(new Date(startTimeMs));
+
+            // Start the process
+            final Process process = new ProcessBuilder()
+                    .command("logcat", "-d", "-v threadtime,uid", "-T", timestamp)
+                    .start();
+
+            // Nothing to write. Don't let the command accidentally block.
+            process.getOutputStream().close();
+
+            // Read the output
+            final StringBuilder str = new StringBuilder();
+            final InputStreamReader reader = new InputStreamReader(process.getInputStream());
+            char[] buffer = new char[4096];
+            int amt;
+            while ((amt = reader.read(buffer, 0, buffer.length)) >= 0) {
+                if (amt > 0) {
+                    str.append(buffer, 0, amt);
+                }
+            }
+
+            try {
+                process.waitFor();
+            } catch (InterruptedException ex) {
+                // We already have the text, drop the exception.
+            }
+
+            return str.toString();
+
+        } catch (IOException ex) {
+            return "Error reading logcat command:\n" + ex.toString();
+        }
+    }
 }
 
diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS
index cede4ea..bcdcfc3 100644
--- a/cmds/incidentd/OWNERS
+++ b/cmds/incidentd/OWNERS
@@ -1,3 +1,4 @@
 joeo@google.com
-kwekua@google.com
+yaochen@google.com
 yanmin@google.com
+zhouwenjie@google.com
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
index 1315750..380e499 100644
--- a/cmds/statsd/OWNERS
+++ b/cmds/statsd/OWNERS
@@ -1,11 +1,7 @@
-bookatz@google.com
-cjyu@google.com
-dwchen@google.com
-jinyithu@google.com
+jianjin@google.com
 joeo@google.com
-kwekua@google.com
+jtnguyen@google.com
+muhammadq@google.com
 singhtejinder@google.com
-stlafon@google.com
 yaochen@google.com
-yanglu@google.com
 yro@google.com
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 318d90c..3a84b79 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -48,6 +48,7 @@
 import "frameworks/base/core/proto/android/stats/enums.proto";
 import "frameworks/base/core/proto/android/stats/intelligence/enums.proto";
 import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
+import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto";
 import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
 import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
@@ -283,6 +284,16 @@
         ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
         RoleRequestResultReported role_request_result_reported =
             190 [(log_from_module) = "permissioncontroller"];
+        MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191;
+        MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192;
+        MediametricsAudiothreadReported mediametrics_audiothread_reported = 193;
+        MediametricsAudiotrackReported mediametrics_audiotrack_reported = 194;
+        MediametricsCodecReported mediametrics_codec_reported = 195;
+        MediametricsDrmWidevineReported mediametrics_drm_widevine_reported = 196;
+        MediametricsExtractorReported mediametrics_extractor_reported = 197;
+        MediametricsMediadrmReported mediametrics_mediadrm_reported = 198;
+        MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
+        MediametricsRecorderReported mediametrics_recorder_reported = 200;
     }
 
     // Pulled events will start at field 10000.
@@ -5815,6 +5826,160 @@
 }
 
 /**
+ * Track Media Codec usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/MediaCodec.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_codec.cpp
+ */
+message MediametricsCodecReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.CodecData codec_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track Media Extractor (pulling video/audio streams out of containers) usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_extractor.cpp
+ */
+message MediametricsExtractorReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.ExtractorData extractor_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp
+ */
+message MediametricsAudiopolicyReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioPolicyData audiopolicy_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone requests.
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioRecord.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp
+ */
+message MediametricsAudiorecordReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioRecordData audiorecord_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/media/libnblog/ReportPerformance.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiothread.cpp
+ */
+message MediametricsAudiothreadReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioThreadData audiothread_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioTrack.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp
+ */
+message MediametricsAudiotrackReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioTrackData audiotrack_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about DRM framework performance
+ * Logged from
+ *   frameworks/av/drm/libmediadrm/DrmHal.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_drm.cpp
+ */
+message MediametricsMediadrmReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    // vendor+description tell about which DRM plugin is in use on this device
+    optional string vendor = 5;
+    optional string description = 6;
+    // from frameworks/av/drm/libmediadrm/protos/metrics.proto
+    optional bytes framework_stats = 7 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about the widevine DRM plugin performance
+ * Logged from
+ *   vendor/widevine/libwvdrmengine/cdm/metrics
+ *   frameworks/av/services/mediaanalytics/statsd_drm.cpp
+ */
+message MediametricsDrmWidevineReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional bytes vendor_specific_stats = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about recordings (e.g. camcorder)
+ * Logged from
+ *   frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_recorder.cpp
+ */
+message MediametricsRecorderReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.RecorderData recorder_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track Media Player usage
+ * Logged from:
+ *   frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp
+ */
+message MediametricsNuPlayerReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.NuPlayerData nuplayer_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
  * State of a dangerous permission requested by a package
  */
 message DangerousPermissionState {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 74a4c87..ce0e561 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -432,12 +432,13 @@
 void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
     lock_guard<std::mutex> lock(mLock);
 
-    if (atomId > android::util::kMaxPushedAtomId) {
-        ALOGW("not interested in atom %d", atomId);
-        return;
+    if (atomId <= android::util::kMaxPushedAtomId) {
+        mPushedAtomStats[atomId]++;
+    } else {
+        if (mNonPlatformPushedAtomStats.size() < kMaxNonPlatformPushedAtoms) {
+            mNonPlatformPushedAtomStats[atomId]++;
+        }
     }
-
-    mPushedAtomStats[atomId]++;
 }
 
 void StatsdStats::noteSystemServerRestart(int32_t timeSec) {
@@ -551,6 +552,7 @@
     mStartTimeSec = getWallClockSec();
     mIceBox.clear();
     std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
+    mNonPlatformPushedAtomStats.clear();
     mAnomalyAlarmRegisteredStats = 0;
     mPeriodicAlarmRegisteredStats = 0;
     mSystemServerRestartSec.clear();
@@ -705,6 +707,9 @@
             dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
         }
     }
+    for (const auto& pair : mNonPlatformPushedAtomStats) {
+        dprintf(out, "Atom %lu->%d\n", (unsigned long)pair.first, pair.second);
+    }
 
     dprintf(out, "********Pulled Atom stats***********\n");
     for (const auto& pair : mPulledAtomStats) {
@@ -890,6 +895,14 @@
         }
     }
 
+    for (const auto& pair : mNonPlatformPushedAtomStats) {
+        uint64_t token =
+                proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second);
+        proto.end(token);
+    }
+
     for (const auto& pair : mPulledAtomStats) {
         android::os::statsd::writePullerStatsToStream(pair, &proto);
     }
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 4d21a29..8b39f5f 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -24,6 +24,7 @@
 #include <mutex>
 #include <string>
 #include <vector>
+#include <unordered_map>
 
 namespace android {
 namespace os {
@@ -160,6 +161,9 @@
     // Max time to do a pull.
     static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
 
+    // Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
+    static const int kMaxNonPlatformPushedAtoms = 100;
+
     // Max platform atom tag number.
     static const int32_t kMaxPlatformAtomTag = 100000;
 
@@ -508,10 +512,14 @@
 
     // Stores the number of times a pushed atom is logged.
     // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
-    // out of that range will be dropped (it's either pulled atoms or test atoms).
+    // out of that range will be put in mNonPlatformPushedAtomStats.
     // This is a vector, not a map because it will be accessed A LOT -- for each stats log.
     std::vector<int> mPushedAtomStats;
 
+    // Stores the number of times a pushed atom is logged for atom ids above kMaxPushedAtomId.
+    // The max size of the map is kMaxNonPlatformPushedAtoms.
+    std::unordered_map<int, int> mNonPlatformPushedAtomStats;
+
     // Maps PullAtomId to its stats. The size is capped by the puller atom counts.
     std::map<int, PulledAtomStats> mPulledAtomStats;
 
@@ -587,6 +595,7 @@
     FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
     FRIEND_TEST(StatsdStatsTest, TestSubStats);
     FRIEND_TEST(StatsdStatsTest, TestAtomLog);
+    FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog);
     FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
     FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
     FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 49fe7ef..41000da 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -312,7 +312,7 @@
     }
 }
 
-void GaugeMetricProducer::prepareFistBucketLocked() {
+void GaugeMetricProducer::prepareFirstBucketLocked() {
     if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
         pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
     }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index d3007c8..1b43d43 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -122,7 +122,7 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
-    void prepareFistBucketLocked() override;
+    void prepareFirstBucketLocked() override;
 
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 7676f59..ec3484c 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -236,9 +236,9 @@
     void addActivation(int activationTrackerIndex, const ActivationType& activationType,
             int64_t ttl_seconds, int deactivationTrackerIndex = -1);
 
-    void prepareFistBucket() {
+    void prepareFirstBucket() {
         std::lock_guard<std::mutex> lock(mMutex);
-        prepareFistBucketLocked();
+        prepareFirstBucketLocked();
     }
 
     void flushIfExpire(int64_t elapsedTimestampNs);
@@ -272,7 +272,7 @@
 
     void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
 
-    virtual void prepareFistBucketLocked() {};
+    virtual void prepareFirstBucketLocked() {};
     /**
      * Flushes the current bucket if the eventTime is after the current bucket's end time. This will
        also flush the current partial bucket in memory.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 0bd6e62..17f2994 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -170,7 +170,7 @@
     }
 }
 
-void ValueMetricProducer::prepareFistBucketLocked() {
+void ValueMetricProducer::prepareFirstBucketLocked() {
     // Kicks off the puller immediately if condition is true and diff based.
     if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
         pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 1821dea..de01e72 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -113,7 +113,7 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
-    void prepareFistBucketLocked() override;
+    void prepareFirstBucketLocked() override;
 
     void dropDataLocked(const int64_t dropTimeNs) override;
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index b027fa0..dd32c08 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -766,9 +766,9 @@
     return true;
 }
 
-void prepareFistBucket(const vector<sp<MetricProducer>>& allMetricProducers) {
+void prepareFirstBucket(const vector<sp<MetricProducer>>& allMetricProducers) {
     for (const auto& metric: allMetricProducers) {
-        metric->prepareFistBucket();
+        metric->prepareFirstBucket();
     }
 }
 
@@ -829,7 +829,7 @@
         return false;
     }
 
-    prepareFistBucket(allMetricProducers);
+    prepareFirstBucket(allMetricProducers);
 
     return true;
 }
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 2a18e22..53dd5b7 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -92,10 +92,6 @@
 // Returns the truncated timestamp.
 int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs);
 
-inline bool isPushedAtom(int atomId) {
-    return atomId <= util::kMaxPushedAtomId && atomId > 1;
-}
-
 inline bool isVendorPulledAtom(int atomId) {
     return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag;
 }
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 1ff7982..1b8a3b5 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -227,8 +227,6 @@
     stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1);
     stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
     stats.noteAtomLogged(android::util::APP_CRASH_OCCURRED, now + 3);
-    // pulled event, should ignore
-    stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFER, now + 4);
 
     vector<uint8_t> output;
     stats.dumpStats(&output, false);
@@ -253,6 +251,39 @@
     EXPECT_TRUE(sensorAtomGood);
 }
 
+TEST(StatsdStatsTest, TestNonPlatformAtomLog) {
+    StatsdStats stats;
+    time_t now = time(nullptr);
+    int newAtom1 = android::util::kMaxPushedAtomId + 1;
+    int newAtom2 = android::util::kMaxPushedAtomId + 2;
+
+    stats.noteAtomLogged(newAtom1, now + 1);
+    stats.noteAtomLogged(newAtom1, now + 2);
+    stats.noteAtomLogged(newAtom2, now + 3);
+
+    vector<uint8_t> output;
+    stats.dumpStats(&output, false);
+    StatsdStatsReport report;
+    bool good = report.ParseFromArray(&output[0], output.size());
+    EXPECT_TRUE(good);
+
+    EXPECT_EQ(2, report.atom_stats_size());
+    bool newAtom1Good = false;
+    bool newAtom2Good = false;
+
+    for (const auto& atomStats : report.atom_stats()) {
+        if (atomStats.tag() == newAtom1 && atomStats.count() == 2) {
+            newAtom1Good = true;
+        }
+        if (atomStats.tag() == newAtom2 && atomStats.count() == 1) {
+            newAtom2Good = true;
+        }
+    }
+
+    EXPECT_TRUE(newAtom1Good);
+    EXPECT_TRUE(newAtom2Good);
+}
+
 TEST(StatsdStatsTest, TestPullAtomStats) {
     StatsdStats stats;
 
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index b9a5867..b9553a8 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -79,7 +79,7 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
                                       pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
 
     EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
@@ -126,7 +126,7 @@
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
 
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -211,7 +211,7 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -303,7 +303,7 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -370,7 +370,7 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -431,7 +431,7 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
@@ -529,7 +529,7 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
 
@@ -583,7 +583,7 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     Alert alert;
     alert.set_id(101);
@@ -692,7 +692,7 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
 
@@ -777,7 +777,7 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFistBucket();
+    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 0e82bad..2262c76 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -105,7 +105,7 @@
                 kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                 logEventMatcherIndex, eventMatcherWizard, tagId,
                 bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-        valueProducer->prepareFistBucket();
+        valueProducer->prepareFirstBucket();
         return valueProducer;
     }
 
@@ -125,7 +125,7 @@
                 new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
                                         eventMatcherWizard, tagId, bucketStartTimeNs,
                                         bucketStartTimeNs, pullerManager);
-        valueProducer->prepareFistBucket();
+        valueProducer->prepareFirstBucket();
         valueProducer->mCondition = ConditionState::kFalse;
         return valueProducer;
     }
@@ -169,7 +169,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
                                       22, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
     EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
@@ -199,7 +199,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, -1, 5,
                                       600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
@@ -381,7 +381,7 @@
             kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
             logEventMatcherIndex, eventMatcherWizard, tagId,
             bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer->prepareFistBucket();
+    valueProducer->prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -670,7 +670,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -728,7 +728,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -779,7 +779,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -854,7 +854,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -897,7 +897,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -972,7 +972,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
 
@@ -1269,7 +1269,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1314,7 +1314,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1361,7 +1361,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1412,7 +1412,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1458,7 +1458,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1532,7 +1532,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -2081,7 +2081,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucket2StartTimeNs,
                                       bucket2StartTimeNs, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
 
     // Event should be skipped since it is from previous bucket.
@@ -2862,7 +2862,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
     std::set<string> strSet;
@@ -2905,7 +2905,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -2969,7 +2969,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFistBucket();
+    valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
     std::set<string> strSet;
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index fc7b778..3e705fd 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -57,6 +57,7 @@
         "AID_BLUETOOTH",
         "AID_LMKD",
         "com.android.managedprovisioning",
+        "AID_MEDIA",
         "AID_NETWORK_STACK"
     };
     private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 1b7fbfe..0524450 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -95,7 +95,8 @@
                 + "telecom set-phone-account-disabled: Disables the given phone account, if it \n"
                 + " has already been registered with telecom.\n"
                 + "\n"
-                + "telecom set-default-dialer: Sets the default dialer to the given component. \n"
+                + "telecom set-default-dialer: Sets the override default dialer to the given "
+                + "component; this will override whatever the dialer role is set to. \n"
                 + "\n"
                 + "telecom get-default-dialer: Displays the current default dialer. \n"
                 + "\n"
@@ -254,13 +255,8 @@
 
     private void runSetDefaultDialer() throws RemoteException {
         final String packageName = nextArgRequired();
-        final boolean success = mTelecomService.setDefaultDialer(packageName);
-        if (success) {
-            System.out.println("Success - " + packageName + " set as default dialer.");
-        } else {
-            System.out.println("Error - " + packageName + " is not an installed Dialer app, \n"
-                    + " or is already the default dialer.");
-        }
+        mTelecomService.setTestDefaultDialer(packageName);
+        System.out.println("Success - " + packageName + " set as override default dialer.");
     }
 
     private void runGetDefaultDialer() throws RemoteException {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 43531ed..2914f6c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -126,7 +126,6 @@
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
 import android.view.autofill.IAutofillWindowPresenter;
-import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureManager;
 import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
 import android.widget.AdapterView;
@@ -840,7 +839,7 @@
     /** The autofill manager. Always access via {@link #getAutofillManager()}. */
     @Nullable private AutofillManager mAutofillManager;
 
-    /** The content capture manager. Always access via {@link #getContentCaptureManager()}. */
+    /** The content capture manager. Access via {@link #getContentCaptureManager()}. */
     @Nullable private ContentCaptureManager mContentCaptureManager;
 
     private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
@@ -1092,12 +1091,11 @@
                 case CONTENT_CAPTURE_START:
                     //TODO(b/111276913): decide whether the InteractionSessionId should be
                     // saved / restored in the activity bundle - probably not
-                    int flags = 0;
-                    if ((getWindow().getAttributes().flags
-                            & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
-                        flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+                    final Window window = getWindow();
+                    if (window != null) {
+                        cm.updateWindowAttributes(window.getAttributes());
                     }
-                    cm.onActivityCreated(mToken, getComponentName(), flags);
+                    cm.onActivityCreated(mToken, getComponentName());
                     break;
                 case CONTENT_CAPTURE_RESUME:
                     cm.onActivityResumed();
@@ -2349,7 +2347,7 @@
      *
      * @param cancellationSignal A signal to cancel the operation in progress.
      * @param callback The callback to send the action list. The actions list cannot
-     *     contain <code>null</code> elements.
+     *     contain <code>null</code> elements. You can call this on any thread.
      */
     public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal,
             @NonNull Consumer<List<DirectAction>> callback) {
@@ -2360,10 +2358,13 @@
      * This is called to perform an action previously defined by the app.
      * Apps also have access to {@link #getVoiceInteractor()} to follow up on the action.
      *
-     * @param actionId The ID for the action
-     * @param arguments Any additional arguments provided by the caller
+     * @param actionId The ID for the action you previously reported via
+     *     {@link #onGetDirectActions(CancellationSignal, Consumer)}.
+     * @param arguments Any additional arguments provided by the caller that are
+     *     specific to the given action.
      * @param cancellationSignal A signal to cancel the operation in progress.
-     * @param resultListener The callback to provide the result back to the caller
+     * @param resultListener The callback to provide the result back to the caller.
+     *     You can call this on any thread. The result bundle is action specific.
      *
      * @see #onGetDirectActions(CancellationSignal, Consumer)
      */
@@ -3782,6 +3783,9 @@
             View decor = mDecor;
             if (decor != null && decor.getParent() != null) {
                 getWindowManager().updateViewLayout(decor, params);
+                if (mContentCaptureManager != null) {
+                    mContentCaptureManager.updateWindowAttributes(params);
+                }
             }
         }
     }
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index fc6fffa..864af8c 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Insets;
+import android.graphics.Matrix;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
@@ -51,6 +52,7 @@
 import android.view.ViewParent;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.view.inputmethod.InputMethodManager;
 
 import dalvik.system.CloseGuard;
 
@@ -317,7 +319,15 @@
      * regions and avoid focus switches by touches on this view.
      */
     public void onLocationChanged() {
-        updateTapExcludeRegion();
+        updateLocationAndTapExcludeRegion();
+    }
+
+    private void clearActivityViewGeometryForIme() {
+        if (mVirtualDisplay == null) {
+            return;
+        }
+        final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
+        mContext.getSystemService(InputMethodManager.class).reportActivityView(displayId, null);
     }
 
     @Override
@@ -329,37 +339,59 @@
     public boolean gatherTransparentRegion(Region region) {
         // The tap exclude region may be affected by any view on top of it, so we detect the
         // possible change by monitoring this function.
-        updateTapExcludeRegion();
+        updateLocationAndTapExcludeRegion();
         return super.gatherTransparentRegion(region);
     }
 
-    /** Compute and send current tap exclude region to WM for this view. */
-    private void updateTapExcludeRegion() {
-        if (!isAttachedToWindow()) {
+    /**
+     * Sends current location in window and tap exclude region to WM for this view.
+     */
+    private void updateLocationAndTapExcludeRegion() {
+        if (mVirtualDisplay == null || !isAttachedToWindow()) {
             return;
         }
+        try {
+            int x = mLocationInWindow[0];
+            int y = mLocationInWindow[1];
+            getLocationInWindow(mLocationInWindow);
+            if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) {
+                x = mLocationInWindow[0];
+                y = mLocationInWindow[1];
+                final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
+                WindowManagerGlobal.getWindowSession().updateDisplayContentLocation(
+                        getWindow(), x, y, displayId);
+
+                // Also report this geometry information to InputMethodManagerService.
+                // TODO(b/115693908): Unify this logic into the above WMS-based one.
+                final Matrix matrix = new Matrix();
+                matrix.set(getMatrix());
+                matrix.postTranslate(x, y);
+                mContext.getSystemService(InputMethodManager.class)
+                        .reportActivityView(displayId, matrix);
+            }
+            updateTapExcludeRegion(x, y);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** Computes and sends current tap exclude region to WM for this view. */
+    private void updateTapExcludeRegion(int x, int y) throws RemoteException {
         if (!canReceivePointerEvents()) {
             cleanTapExcludeRegion();
             return;
         }
-        try {
-            getLocationInWindow(mLocationInWindow);
-            final int x = mLocationInWindow[0];
-            final int y = mLocationInWindow[1];
-            mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
+        mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
 
-            // There might be views on top of us. We need to subtract those areas from the tap
-            // exclude region.
-            final ViewParent parent = getParent();
-            if (parent instanceof ViewGroup) {
-                ((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this);
-            }
-
-            WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
-                    mTapExcludeRegion);
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+        // There might be views on top of us. We need to subtract those areas from the tap
+        // exclude region.
+        final ViewParent parent = getParent();
+        if (parent != null) {
+            parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this);
         }
+
+        WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
+                mTapExcludeRegion);
     }
 
     private class SurfaceCallback implements SurfaceHolder.Callback {
@@ -379,7 +411,7 @@
                 mVirtualDisplay.setDisplayState(true);
             }
 
-            updateTapExcludeRegion();
+            updateLocationAndTapExcludeRegion();
         }
 
         @Override
@@ -387,7 +419,7 @@
             if (mVirtualDisplay != null) {
                 mVirtualDisplay.resize(width, height, getBaseDisplayDensity());
             }
-            updateTapExcludeRegion();
+            updateLocationAndTapExcludeRegion();
         }
 
         @Override
@@ -395,6 +427,7 @@
             if (mVirtualDisplay != null) {
                 mVirtualDisplay.setDisplayState(false);
             }
+            clearActivityViewGeometryForIme();
             cleanTapExcludeRegion();
         }
     }
@@ -471,7 +504,8 @@
 
         try {
             // TODO: Find a way to consolidate these calls to the server.
-            wm.reparentDisplayContent(displayId, mRootSurfaceControl);
+            WindowManagerGlobal.getWindowSession().reparentDisplayContent(
+                    getWindow(), mRootSurfaceControl, displayId);
             wm.dontOverrideDisplayInfo(displayId);
             if (mSingleTaskInstance) {
                 mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 82a34ce..1ad0cfd 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2764,6 +2764,9 @@
         if (itemInfo.packageName != null) {
             dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
         }
+        if (dr == null && itemInfo != appInfo) {
+            dr = loadUnbadgedItemIcon(appInfo, appInfo);
+        }
         if (dr == null) {
             dr = itemInfo.loadDefaultIcon(this);
         }
diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java
index d191f4b..ef3627b 100644
--- a/core/java/android/app/DirectAction.java
+++ b/core/java/android/app/DirectAction.java
@@ -22,12 +22,19 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
+
 /**
  * Represents a abstract action that can be perform on this app. This are requested from
- * outside the app's UI (eg by SystemUI or assistant).
+ * outside the app's UI (eg by SystemUI or assistant). The semantics of these actions are
+ * not specified by the OS. This allows open-ended and scalable approach for defining how
+ * an app interacts with components that expose alternative interaction models to the user
+ * such as the assistant, SystemUI, etc. You can use {@link #equals(Object)} to compare
+ * instances of this class.
  */
 public final class DirectAction implements Parcelable {
 
@@ -91,7 +98,7 @@
     }
 
     /**
-     * Returns the ID for this action.
+     * @return the ID for this action.
      */
     @NonNull
     public String getId() {
@@ -99,7 +106,7 @@
     }
 
     /**
-     * Returns any extras associated with this action.
+     * @return any extras associated with this action.
      */
     @Nullable
     public Bundle getExtras() {
@@ -107,7 +114,7 @@
     }
 
     /**
-     * Returns the LocusId for the current state for the app
+     * @return the LocusId for the current state for the app
      */
     @Nullable
     public LocusId getLocusId() {
@@ -120,6 +127,28 @@
     }
 
     @Override
+    public int hashCode() {
+        return mID.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == null) {
+            return false;
+        }
+
+        if (other == this) {
+            return true;
+        }
+
+        if (getClass() != other.getClass()) {
+            return false;
+        }
+
+        return mID.equals(((DirectAction) other).mID);
+    }
+
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mTaskId);
         dest.writeStrongBinder(mActivityId);
@@ -139,7 +168,8 @@
         /**
          * Creates a new instance.
          *
-         * @param id The mandatory action id.
+         * @param id The mandatory action id which must be unique in the
+         *     current application state.
          */
         public Builder(@NonNull String id) {
             Preconditions.checkNotNull(id);
@@ -147,7 +177,9 @@
         }
 
         /**
-         * Sets the optional action extras.
+         * Sets the optional action extras. These extras are action specific
+         * and their semantics are open-ended potentially representing how
+         * the action is visualized, interpreted, what its arguments are, etc.
          *
          * @param extras The extras.
          * @return This builder.
@@ -158,7 +190,9 @@
         }
 
         /**
-         * Sets the optional locus id.
+         * Sets the optional locus id. This is an identifier of the application
+         * state from a user perspective. For example, a specific chat in a
+         * messaging app.
          *
          * @param locusId The locus id.
          * @return This builder.
diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java
index 9fabfde..0287564 100644
--- a/core/java/android/app/SharedElementCallback.java
+++ b/core/java/android/app/SharedElementCallback.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.RectF;
@@ -49,6 +50,7 @@
     private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap";
     private static final String BUNDLE_SNAPSHOT_GRAPHIC_BUFFER =
             "sharedElement:snapshot:graphicBuffer";
+    private static final String BUNDLE_SNAPSHOT_COLOR_SPACE = "sharedElement:snapshot:colorSpace";
     private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType";
     private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix";
 
@@ -186,6 +188,10 @@
                     } else {
                         GraphicBuffer graphicBuffer = bitmap.createGraphicBufferHandle();
                         bundle.putParcelable(BUNDLE_SNAPSHOT_GRAPHIC_BUFFER, graphicBuffer);
+                        ColorSpace cs = bitmap.getColorSpace();
+                        if (cs != null) {
+                            bundle.putInt(BUNDLE_SNAPSHOT_COLOR_SPACE, cs.getId());
+                        }
                     }
                     bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE,
                             imageView.getScaleType().toString());
@@ -235,8 +241,13 @@
                 return null;
             }
             if (bitmap == null) {
+                ColorSpace colorSpace = null;
+                int colorSpaceId = bundle.getInt(BUNDLE_SNAPSHOT_COLOR_SPACE, 0);
+                if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+                    colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+                }
                 bitmap = Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer),
-                                                   null);
+                                                   colorSpace);
             }
             ImageView imageView = new ImageView(context);
             view = imageView;
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 97b9176..a4a97c4 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -19,6 +19,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.ComponentName;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -155,6 +156,11 @@
     @Override
     @UnsupportedAppUsage
     public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException {
+        if (Binder.getCallingPid() != android.os.Process.myPid()
+                && snapshot != null && snapshot.getSnapshot() != null) {
+            // Preemptively clear any reference to the buffer
+            snapshot.getSnapshot().destroy();
+        }
     }
 
     @Override
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 0d5a763..3fca311 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -699,6 +699,7 @@
         static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS =             0x100;
         static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS =             0x200;
         static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH =          0x400;
+        static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID =      0x800;
 
         int mFlags;
         int mAutofillFlags;
@@ -754,6 +755,9 @@
                     } else {
                         mAutofillId = new AutofillId(autofillViewId);
                     }
+                    if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
+                        mAutofillId.setSessionId(in.readInt());
+                    }
                 }
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
                     mAutofillType = in.readInt();
@@ -899,6 +903,9 @@
                 if (mAutofillId.isVirtualInt()) {
                     autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID;
                 }
+                if (mAutofillId.hasSession()) {
+                    autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID;
+                }
             }
             if (mAutofillValue != null) {
                 autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE;
@@ -965,7 +972,9 @@
                     if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) {
                         out.writeInt(mAutofillId.getVirtualChildIntId());
                     }
-                    // TODO(b/113593220): write session id as well
+                    if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
+                        out.writeInt(mAutofillId.getSessionId());
+                    }
                 }
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
                     out.writeInt(mAutofillType);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f8dc20e..7fa4360 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -198,6 +198,8 @@
     /** @hide */
     public static final int REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED = 0x000E;
     /** @hide */
+    public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000F;
+    /** @hide */
     public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
 
 
@@ -997,6 +999,9 @@
                     case REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED:
                         sb.append("-uss");
                         break;
+                    case REASON_SUB_USAGE_FOREGROUND_SERVICE_START:
+                        sb.append("-fss");
+                        break;
                 }
                 break;
         }
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 442b239..1fe1b10 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -22,23 +22,16 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 
 /**
@@ -209,101 +202,32 @@
     @UnsupportedAppUsage
     public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
-    @GuardedBy("mServiceLock")
-    private IBluetoothA2dp mService;
     private BluetoothAdapter mAdapter;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        try {
-                            mServiceLock.writeLock().lock();
-                            if (mService != null) {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            }
-                        } catch (Exception re) {
-                            Log.e(TAG, "", re);
-                        } finally {
-                            mServiceLock.writeLock().unlock();
-                        }
-                    } else {
-                        try {
-                            mServiceLock.readLock().lock();
-                            if (mService == null) {
-                                if (VDBG) Log.d(TAG, "Binding service...");
-                                doBind();
-                            }
-                        } catch (Exception re) {
-                            Log.e(TAG, "", re);
-                        } finally {
-                            mServiceLock.readLock().unlock();
-                        }
-                    }
+    private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
+                    IBluetoothA2dp.class.getName()) {
+                @Override
+                public IBluetoothA2dp getServiceInterface(IBinder service) {
+                    return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothA2dp proxy object for interacting with the local
      * Bluetooth A2DP service.
      */
-    /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
+    /*package*/ BluetoothA2dp(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothA2dp.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     @UnsupportedAppUsage
     /*package*/ void close() {
-        mServiceListener = null;
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        try {
-            mServiceLock.writeLock().lock();
-            if (mService != null) {
-                mService = null;
-                mContext.unbindService(mConnection);
-            }
-        } catch (Exception re) {
-            Log.e(TAG, "", re);
-        } finally {
-            mServiceLock.writeLock().unlock();
-        }
+    private IBluetoothA2dp getService() {
+        return mProfileConnector.getService();
     }
 
     @Override
@@ -333,17 +257,15 @@
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled() && isValidDevice(device)) {
-                return mService.connect(device);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.connect(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -376,17 +298,15 @@
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled() && isValidDevice(device)) {
-                return mService.disconnect(device);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.disconnect(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -397,17 +317,15 @@
     public List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getConnectedDevices();
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.getConnectedDevices();
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return new ArrayList<BluetoothDevice>();
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return new ArrayList<BluetoothDevice>();
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -418,17 +336,15 @@
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getDevicesMatchingConnectionStates(states);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.getDevicesMatchingConnectionStates(states);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return new ArrayList<BluetoothDevice>();
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return new ArrayList<BluetoothDevice>();
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -439,18 +355,16 @@
     public @BtProfileState int getConnectionState(BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                return mService.getConnectionState(device);
+                return service.getConnectionState(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return BluetoothProfile.STATE_DISCONNECTED;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return BluetoothProfile.STATE_DISCONNECTED;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -480,18 +394,16 @@
     public boolean setActiveDevice(@Nullable BluetoothDevice device) {
         if (DBG) log("setActiveDevice(" + device + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()
                     && ((device == null) || isValidDevice(device))) {
-                return mService.setActiveDevice(device);
+                return service.setActiveDevice(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -511,17 +423,15 @@
     public BluetoothDevice getActiveDevice() {
         if (VDBG) log("getActiveDevice()");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getActiveDevice();
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.getActiveDevice();
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return null;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return null;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -543,22 +453,20 @@
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
                 if (priority != BluetoothProfile.PRIORITY_OFF
                         && priority != BluetoothProfile.PRIORITY_ON) {
                     return false;
                 }
-                return mService.setPriority(device, priority);
+                return service.setPriority(device, priority);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -578,18 +486,16 @@
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                return mService.getPriority(device);
+                return service.getPriority(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return BluetoothProfile.PRIORITY_OFF;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return BluetoothProfile.PRIORITY_OFF;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -602,17 +508,15 @@
     public boolean isAvrcpAbsoluteVolumeSupported() {
         if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.isAvrcpAbsoluteVolumeSupported();
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.isAvrcpAbsoluteVolumeSupported();
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -625,15 +529,13 @@
     public void setAvrcpAbsoluteVolume(int volume) {
         if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                mService.setAvrcpAbsoluteVolume(volume);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                service.setAvrcpAbsoluteVolume(volume);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -646,18 +548,16 @@
      */
     public boolean isA2dpPlaying(BluetoothDevice device) {
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                return mService.isA2dpPlaying(device);
+                return service.isA2dpPlaying(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -694,19 +594,17 @@
     public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getCodecStatus(device);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                return service.getCodecStatus(device);
             }
-            if (mService == null) {
+            if (service == null) {
                 Log.w(TAG, "Proxy not attached to service");
             }
             return null;
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
             return null;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -723,17 +621,15 @@
                                          BluetoothCodecConfig codecConfig) {
         if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                mService.setCodecConfigPreference(device, codecConfig);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
+                service.setCodecConfigPreference(device, codecConfig);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return;
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
             return;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -772,21 +668,19 @@
      */
     private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()) {
                 if (enable) {
-                    mService.enableOptionalCodecs(device);
+                    service.enableOptionalCodecs(device);
                 } else {
-                    mService.disableOptionalCodecs(device);
+                    service.disableOptionalCodecs(device);
                 }
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return;
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
             return;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -801,17 +695,15 @@
     @UnsupportedAppUsage
     public int supportsOptionalCodecs(BluetoothDevice device) {
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled() && isValidDevice(device)) {
-                return mService.supportsOptionalCodecs(device);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.supportsOptionalCodecs(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
             return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -826,17 +718,15 @@
     @UnsupportedAppUsage
     public int getOptionalCodecsEnabled(BluetoothDevice device) {
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled() && isValidDevice(device)) {
-                return mService.getOptionalCodecsEnabled(device);
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.getOptionalCodecsEnabled(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return OPTIONAL_CODECS_PREF_UNKNOWN;
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
             return OPTIONAL_CODECS_PREF_UNKNOWN;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -858,18 +748,16 @@
                 Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
                 return;
             }
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                mService.setOptionalCodecsEnabled(device, value);
+                service.setOptionalCodecsEnabled(device, value);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -900,35 +788,6 @@
         }
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            try {
-                mServiceLock.writeLock().lock();
-                mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
-            } finally {
-                mServiceLock.writeLock().unlock();
-            }
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            try {
-                mServiceLock.writeLock().lock();
-                mService = null;
-            } finally {
-                mServiceLock.writeLock().unlock();
-            }
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
-            }
-        }
-    };
-
     private boolean isEnabled() {
         if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
         return false;
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index cb996f3..5a8055a 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -17,14 +17,10 @@
 package android.bluetooth;
 
 import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -125,93 +121,31 @@
     public static final String EXTRA_AUDIO_CONFIG =
             "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG";
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothA2dpSink mService;
     private BluetoothAdapter mAdapter;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+    private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK,
+                    "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
+                @Override
+                public IBluetoothA2dpSink getServiceInterface(IBinder service) {
+                    return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothA2dp proxy object for interacting with the local
      * Bluetooth A2DP service.
      */
-    /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
+    /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothA2dpSink.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     /*package*/ void close() {
-        mServiceListener = null;
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
+    private IBluetoothA2dpSink getService() {
+        return mProfileConnector.getService();
     }
 
     @Override
@@ -242,7 +176,7 @@
      */
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.connect(device);
@@ -283,7 +217,7 @@
     @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.disconnect(device);
@@ -302,7 +236,7 @@
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -321,7 +255,7 @@
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -340,7 +274,7 @@
     @Override
     public int getConnectionState(BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -366,7 +300,7 @@
      */
     public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
         if (VDBG) log("getAudioConfig(" + device + ")");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getAudioConfig(device);
@@ -396,7 +330,7 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (priority != BluetoothProfile.PRIORITY_OFF
                     && priority != BluetoothProfile.PRIORITY_ON) {
@@ -428,7 +362,7 @@
      */
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getPriority(device);
@@ -449,7 +383,7 @@
      * @param device BluetoothDevice device
      */
     public boolean isA2dpPlaying(BluetoothDevice device) {
-        final IBluetoothA2dpSink service = mService;
+        final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.isA2dpPlaying(device);
@@ -488,25 +422,6 @@
         }
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK,
-                        BluetoothA2dpSink.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK);
-            }
-        }
-    };
-
     private boolean isEnabled() {
         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
     }
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index c447868..4e7e441 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -16,14 +16,10 @@
 
 package android.bluetooth;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -80,93 +76,32 @@
     public static final String EXTRA_PLAYER_SETTING =
             "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothAvrcpController mService;
     private BluetoothAdapter mAdapter;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+    private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER,
+                    "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
+                @Override
+                public IBluetoothAvrcpController getServiceInterface(IBinder service) {
+                    return IBluetoothAvrcpController.Stub.asInterface(
+                            Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothAvrcpController proxy object for interacting with the local
      * Bluetooth AVRCP service.
      */
-    /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
+    /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothAvrcpController.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     /*package*/ void close() {
-        mServiceListener = null;
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
+    private IBluetoothAvrcpController getService() {
+        return mProfileConnector.getService();
     }
 
     @Override
@@ -180,7 +115,8 @@
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
-        final IBluetoothAvrcpController service = mService;
+        final IBluetoothAvrcpController service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -199,7 +135,8 @@
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothAvrcpController service = mService;
+        final IBluetoothAvrcpController service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -218,7 +155,8 @@
     @Override
     public int getConnectionState(BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
-        final IBluetoothAvrcpController service = mService;
+        final IBluetoothAvrcpController service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -239,7 +177,8 @@
     public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getPlayerSettings");
         BluetoothAvrcpPlayerSettings settings = null;
-        final IBluetoothAvrcpController service = mService;
+        final IBluetoothAvrcpController service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 settings = service.getPlayerSettings(device);
@@ -257,7 +196,8 @@
      */
     public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
         if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
-        final IBluetoothAvrcpController service = mService;
+        final IBluetoothAvrcpController service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.setPlayerApplicationSetting(plAppSetting);
@@ -277,7 +217,8 @@
     public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
         Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
                 + keyState);
-        final IBluetoothAvrcpController service = mService;
+        final IBluetoothAvrcpController service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 service.sendGroupNavigationCmd(device, keyCode, keyState);
@@ -290,25 +231,6 @@
         if (service == null) Log.w(TAG, "Proxy not attached to service");
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
-                        BluetoothAvrcpController.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER);
-            }
-        }
-    };
-
     private boolean isEnabled() {
         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
     }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 8d9d340..9862a63 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -337,19 +337,9 @@
                 public void onBluetoothStateChange(boolean up) {
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
                         doUnbind();
                     } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
+                        doBind();
                     }
                 }
             };
@@ -374,24 +364,32 @@
         doBind();
     }
 
-    boolean doBind() {
-        try {
-            return mAdapter.getBluetoothManager().bindBluetoothProfileService(
-                    BluetoothProfile.HEADSET, mConnection);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to bind HeadsetService", e);
+    private boolean doBind() {
+        synchronized (mConnection) {
+            if (mService == null) {
+                if (VDBG) Log.d(TAG, "Binding service...");
+                try {
+                    return mAdapter.getBluetoothManager().bindBluetoothProfileService(
+                            BluetoothProfile.HEADSET, mConnection);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to bind HeadsetService", e);
+                }
+            }
         }
         return false;
     }
 
-    void doUnbind() {
+    private void doUnbind() {
         synchronized (mConnection) {
             if (mService != null) {
+                if (VDBG) Log.d(TAG, "Unbinding service...");
                 try {
                     mAdapter.getBluetoothManager().unbindBluetoothProfileService(
                             BluetoothProfile.HEADSET, mConnection);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Unable to unbind HeadsetService", e);
+                } finally {
+                    mService = null;
                 }
             }
         }
@@ -411,8 +409,8 @@
         if (mgr != null) {
             try {
                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
+            } catch (RemoteException re) {
+                Log.e(TAG, "", re);
             }
         }
         mServiceListener = null;
@@ -1169,7 +1167,7 @@
         @Override
         public void onServiceDisconnected(ComponentName className) {
             if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
+            doUnbind();
             mHandler.sendMessage(mHandler.obtainMessage(
                     MESSAGE_HEADSET_SERVICE_DISCONNECTED));
         }
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 549c1fa..05833b5 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -17,15 +17,11 @@
 package android.bluetooth;
 
 import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -367,73 +363,22 @@
     public static final int CALL_ACCEPT_HOLD = 1;
     public static final int CALL_ACCEPT_TERMINATE = 2;
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothHeadsetClient mService;
     private BluetoothAdapter mAdapter;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
+    private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT,
+                    "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
                 @Override
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    Intent intent = new Intent(
-                                            IBluetoothHeadsetClient.class.getName());
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+                public IBluetoothHeadsetClient getServiceInterface(IBinder service) {
+                    return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothHeadsetClient proxy object.
      */
-    /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
+    /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothHeadsetClient.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     /**
@@ -444,27 +389,11 @@
      */
     /*package*/ void close() {
         if (VDBG) log("close()");
+        mProfileConnector.disconnect();
+    }
 
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
+    private IBluetoothHeadsetClient getService() {
+        return mProfileConnector.getService();
     }
 
     /**
@@ -481,7 +410,8 @@
     @UnsupportedAppUsage
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.connect(device);
@@ -504,7 +434,8 @@
     @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.disconnect(device);
@@ -525,7 +456,8 @@
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -548,7 +480,8 @@
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -570,7 +503,8 @@
     @Override
     public int getConnectionState(BluetoothDevice device) {
         if (VDBG) log("getConnectionState(" + device + ")");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -590,7 +524,8 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (priority != BluetoothProfile.PRIORITY_OFF
                     && priority != BluetoothProfile.PRIORITY_ON) {
@@ -612,7 +547,8 @@
      */
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getPriority(device);
@@ -638,7 +574,8 @@
      */
     public boolean startVoiceRecognition(BluetoothDevice device) {
         if (DBG) log("startVoiceRecognition()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.startVoiceRecognition(device);
@@ -663,7 +600,8 @@
      */
     public boolean stopVoiceRecognition(BluetoothDevice device) {
         if (DBG) log("stopVoiceRecognition()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.stopVoiceRecognition(device);
@@ -683,7 +621,8 @@
      */
     public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
         if (DBG) log("getCurrentCalls()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getCurrentCalls(device);
@@ -703,7 +642,8 @@
      */
     public Bundle getCurrentAgEvents(BluetoothDevice device) {
         if (DBG) log("getCurrentCalls()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getCurrentAgEvents(device);
@@ -727,7 +667,8 @@
     @UnsupportedAppUsage
     public boolean acceptCall(BluetoothDevice device, int flag) {
         if (DBG) log("acceptCall()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.acceptCall(device, flag);
@@ -748,7 +689,8 @@
      */
     public boolean holdCall(BluetoothDevice device) {
         if (DBG) log("holdCall()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.holdCall(device);
@@ -774,7 +716,8 @@
     @UnsupportedAppUsage
     public boolean rejectCall(BluetoothDevice device) {
         if (DBG) log("rejectCall()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.rejectCall(device);
@@ -804,7 +747,8 @@
      */
     public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
         if (DBG) log("terminateCall()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.terminateCall(device, call);
@@ -832,7 +776,8 @@
      */
     public boolean enterPrivateMode(BluetoothDevice device, int index) {
         if (DBG) log("enterPrivateMode()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.enterPrivateMode(device, index);
@@ -859,7 +804,8 @@
      */
     public boolean explicitCallTransfer(BluetoothDevice device) {
         if (DBG) log("explicitCallTransfer()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.explicitCallTransfer(device);
@@ -882,7 +828,8 @@
      */
     public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
         if (DBG) log("dial()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.dial(device, number);
@@ -906,7 +853,8 @@
      */
     public boolean sendDTMF(BluetoothDevice device, byte code) {
         if (DBG) log("sendDTMF()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.sendDTMF(device, code);
@@ -932,7 +880,8 @@
      */
     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
         if (DBG) log("getLastVoiceTagNumber()");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getLastVoiceTagNumber(device);
@@ -952,7 +901,8 @@
     @UnsupportedAppUsage
     public int getAudioState(BluetoothDevice device) {
         if (VDBG) log("getAudioState");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getAudioState(device);
@@ -975,7 +925,8 @@
      */
     public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
         if (VDBG) log("setAudioRouteAllowed");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 service.setAudioRouteAllowed(device, allowed);
@@ -997,7 +948,8 @@
      */
     public boolean getAudioRouteAllowed(BluetoothDevice device) {
         if (VDBG) log("getAudioRouteAllowed");
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getAudioRouteAllowed(device);
@@ -1021,7 +973,8 @@
      * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
      */
     public boolean connectAudio(BluetoothDevice device) {
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.connectAudio(device);
@@ -1045,7 +998,8 @@
      * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
      */
     public boolean disconnectAudio(BluetoothDevice device) {
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.disconnectAudio(device);
@@ -1066,7 +1020,8 @@
      * @return bundle of AG features; null if no service or AG not connected
      */
     public Bundle getCurrentAgFeatures(BluetoothDevice device) {
-        final IBluetoothHeadsetClient service = mService;
+        final IBluetoothHeadsetClient service =
+                getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getCurrentAgFeatures(device);
@@ -1080,29 +1035,6 @@
         return null;
     }
 
-
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT,
-                        BluetoothHeadsetClient.this);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT);
-            }
-        }
-    };
-
     private boolean isEnabled() {
         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
     }
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index d6edb90..60fb6fb 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -22,21 +22,14 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * This class provides the public APIs to control the Hearing Aid profile.
@@ -129,97 +122,31 @@
      */
     public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
-    @GuardedBy("mServiceLock")
-    private IBluetoothHearingAid mService;
     private BluetoothAdapter mAdapter;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        try {
-                            mServiceLock.writeLock().lock();
-                            mService = null;
-                            mContext.unbindService(mConnection);
-                        } catch (Exception re) {
-                            Log.e(TAG, "", re);
-                        } finally {
-                            mServiceLock.writeLock().unlock();
-                        }
-                    } else {
-                        try {
-                            mServiceLock.readLock().lock();
-                            if (mService == null) {
-                                if (VDBG) Log.d(TAG, "Binding service...");
-                                doBind();
-                            }
-                        } catch (Exception re) {
-                            Log.e(TAG, "", re);
-                        } finally {
-                            mServiceLock.readLock().unlock();
-                        }
-                    }
+    private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID,
+                    "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
+                @Override
+                public IBluetoothHearingAid getServiceInterface(IBinder service) {
+                    return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothHearingAid proxy object for interacting with the local
      * Bluetooth Hearing Aid service.
      */
-    /*package*/ BluetoothHearingAid(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
+    /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    void doBind() {
-        Intent intent = new Intent(IBluetoothHearingAid.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
-            return;
-        }
+        mProfileConnector.connect(context, listener);
     }
 
     /*package*/ void close() {
-        mServiceListener = null;
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        try {
-            mServiceLock.writeLock().lock();
-            if (mService != null) {
-                mService = null;
-                mContext.unbindService(mConnection);
-            }
-        } catch (Exception re) {
-            Log.e(TAG, "", re);
-        } finally {
-            mServiceLock.writeLock().unlock();
-        }
+    private IBluetoothHearingAid getService() {
+        return mProfileConnector.getService();
     }
 
     /**
@@ -241,18 +168,16 @@
      */
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled() && isValidDevice(device)) {
-                return mService.connect(device);
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.connect(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -283,18 +208,16 @@
      */
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled() && isValidDevice(device)) {
-                return mService.disconnect(device);
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.disconnect(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -304,18 +227,16 @@
     @Override
     public @NonNull List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getConnectedDevices();
+            if (service != null && isEnabled()) {
+                return service.getConnectedDevices();
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return new ArrayList<BluetoothDevice>();
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return new ArrayList<BluetoothDevice>();
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -326,18 +247,16 @@
     public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
     @NonNull int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getDevicesMatchingConnectionStates(states);
+            if (service != null && isEnabled()) {
+                return service.getDevicesMatchingConnectionStates(states);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return new ArrayList<BluetoothDevice>();
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return new ArrayList<BluetoothDevice>();
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -348,19 +267,17 @@
     public @BluetoothProfile.BtProfileState int getConnectionState(
     @NonNull BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                return mService.getConnectionState(device);
+                return service.getConnectionState(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return BluetoothProfile.STATE_DISCONNECTED;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return BluetoothProfile.STATE_DISCONNECTED;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -388,20 +305,18 @@
      */
     public boolean setActiveDevice(@Nullable BluetoothDevice device) {
         if (DBG) log("setActiveDevice(" + device + ")");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            if (service != null && isEnabled()
                     && ((device == null) || isValidDevice(device))) {
-                mService.setActiveDevice(device);
+                service.setActiveDevice(device);
                 return true;
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -419,18 +334,16 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public List<BluetoothDevice> getActiveDevices() {
         if (VDBG) log("getActiveDevices()");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getActiveDevices();
+            if (service != null && isEnabled()) {
+                return service.getActiveDevices();
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return new ArrayList<>();
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return new ArrayList<>();
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -451,23 +364,21 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
                 if (priority != BluetoothProfile.PRIORITY_OFF
                         && priority != BluetoothProfile.PRIORITY_ON) {
                     return false;
                 }
-                return mService.setPriority(device, priority);
+                return service.setPriority(device, priority);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return false;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -485,19 +396,17 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                return mService.getPriority(device);
+                return service.getPriority(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return BluetoothProfile.PRIORITY_OFF;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return BluetoothProfile.PRIORITY_OFF;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -536,18 +445,16 @@
         if (VDBG) {
             log("getVolume()");
         }
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                return mService.getVolume();
+            if (service != null && isEnabled()) {
+                return service.getVolume();
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return 0;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return 0;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -567,21 +474,18 @@
     public void adjustVolume(int direction) {
         if (DBG) log("adjustVolume(" + direction + ")");
 
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-
-            if (mService == null) {
+            if (service == null) {
                 Log.w(TAG, "Proxy not attached to service");
                 return;
             }
 
             if (!isEnabled()) return;
 
-            mService.adjustVolume(direction);
+            service.adjustVolume(direction);
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -594,20 +498,18 @@
     public void setVolume(int volume) {
         if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
 
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService == null) {
+            if (service == null) {
                 Log.w(TAG, "Proxy not attached to service");
                 return;
             }
 
             if (!isEnabled()) return;
 
-            mService.setVolume(volume);
+            service.setVolume(volume);
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -623,21 +525,19 @@
         if (VDBG) {
             log("getCustomerId(" + device + ")");
         }
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService == null) {
+            if (service == null) {
                 Log.w(TAG, "Proxy not attached to service");
                 return HI_SYNC_ID_INVALID;
             }
 
             if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
 
-            return mService.getHiSyncId(device);
+            return service.getHiSyncId(device);
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return HI_SYNC_ID_INVALID;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -653,19 +553,17 @@
         if (VDBG) {
             log("getDeviceSide(" + device + ")");
         }
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                return mService.getDeviceSide(device);
+                return service.getDeviceSide(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return SIDE_LEFT;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return SIDE_LEFT;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
@@ -681,52 +579,20 @@
         if (VDBG) {
             log("getDeviceMode(" + device + ")");
         }
+        final IBluetoothHearingAid service = getService();
         try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
+            if (service != null && isEnabled()
                     && isValidDevice(device)) {
-                return mService.getDeviceMode(device);
+                return service.getDeviceMode(device);
             }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
             return MODE_MONAURAL;
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             return MODE_MONAURAL;
-        } finally {
-            mServiceLock.readLock().unlock();
         }
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            try {
-                mServiceLock.writeLock().lock();
-                mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
-            } finally {
-                mServiceLock.writeLock().unlock();
-            }
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID,
-                                                    BluetoothHearingAid.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            try {
-                mServiceLock.writeLock().lock();
-                mService = null;
-            } finally {
-                mServiceLock.writeLock().unlock();
-            }
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
-            }
-        }
-    };
-
     private boolean isEnabled() {
         if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
         return false;
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index e44f36e..e9b0be2 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -18,13 +18,10 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -328,11 +325,6 @@
         }
     }
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothHidDevice mService;
-    private BluetoothAdapter mAdapter;
-
     private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
 
         private final Executor mExecutor;
@@ -386,114 +378,33 @@
         }
     }
 
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-
-                public void onBluetoothStateChange(boolean up) {
-                    Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    synchronized (mConnection) {
-                        if (up) {
-                            try {
-                                if (mService == null) {
-                                    Log.d(TAG, "Binding HID Device service...");
-                                    doBind();
-                                }
-                            } catch (IllegalStateException e) {
-                                Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev "
-                                        + "service: ", e);
-                            } catch (SecurityException e) {
-                                Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev "
-                                        + "service: ", e);
-                            }
-                        } else {
-                            Log.d(TAG, "Unbinding service...");
-                            doUnbind();
-                        }
-                    }
+    private BluetoothAdapter mAdapter;
+    private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE,
+                    "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
+                @Override
+                public IBluetoothHidDevice getServiceInterface(IBinder service) {
+                    return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
-
-    private final ServiceConnection mConnection =
-            new ServiceConnection() {
-                public void onServiceConnected(ComponentName className, IBinder service) {
-                    Log.d(TAG, "onServiceConnected()");
-                    mService = IBluetoothHidDevice.Stub.asInterface(service);
-                    if (mServiceListener != null) {
-                        mServiceListener.onServiceConnected(
-                                BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this);
-                    }
-                }
-
-                public void onServiceDisconnected(ComponentName className) {
-                    Log.d(TAG, "onServiceDisconnected()");
-                    mService = null;
-                    if (mServiceListener != null) {
-                        mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE);
-                    }
-                }
-            };
+    };
 
     BluetoothHidDevice(Context context, ServiceListener listener) {
-        mContext = context;
-        mServiceListener = listener;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        }
-
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothHidDevice.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
-            return false;
-        }
-        Log.d(TAG, "Bound to HID Device Service");
-        return true;
-    }
-
-    void doUnbind() {
-        if (mService != null) {
-            mService = null;
-            try {
-                mContext.unbindService(mConnection);
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Unable to unbind HidDevService", e);
-            }
-        }
+        mProfileConnector.connect(context, listener);
     }
 
     void close() {
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            doUnbind();
-        }
-        mServiceListener = null;
+    private IBluetoothHidDevice getService() {
+        return mProfileConnector.getService();
     }
 
     /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 return service.getConnectedDevices();
@@ -510,7 +421,7 @@
     /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -527,7 +438,7 @@
     /** {@inheritDoc} */
     @Override
     public int getConnectionState(BluetoothDevice device) {
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 return service.getConnectionState(device);
@@ -584,7 +495,7 @@
             throw new IllegalArgumentException("callback parameter cannot be null");
         }
 
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 CallbackWrapper cbw = new CallbackWrapper(executor, callback);
@@ -612,7 +523,7 @@
     public boolean unregisterApp() {
         boolean result = false;
 
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 result = service.unregisterApp();
@@ -637,7 +548,7 @@
     public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
         boolean result = false;
 
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 result = service.sendReport(device, id, data);
@@ -663,7 +574,7 @@
     public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
         boolean result = false;
 
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 result = service.replyReport(device, type, id, data);
@@ -687,7 +598,7 @@
     public boolean reportError(BluetoothDevice device, byte error) {
         boolean result = false;
 
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 result = service.reportError(device, error);
@@ -708,7 +619,7 @@
      * {@hide}
      */
     public String getUserAppName() {
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
 
         if (service != null) {
             try {
@@ -734,7 +645,7 @@
     public boolean connect(BluetoothDevice device) {
         boolean result = false;
 
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 result = service.connect(device);
@@ -758,7 +669,7 @@
     public boolean disconnect(BluetoothDevice device) {
         boolean result = false;
 
-        final IBluetoothHidDevice service = mService;
+        final IBluetoothHidDevice service = getService();
         if (service != null) {
             try {
                 result = service.disconnect(device);
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 7c925a1..4afb382 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -18,14 +18,10 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -220,97 +216,32 @@
     public static final String EXTRA_IDLE_TIME =
             "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
     private BluetoothAdapter mAdapter;
-    private volatile IBluetoothHidHost mService;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                if (mService != null) {
-                                    mService = null;
-                                    mContext.unbindService(mConnection);
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+    private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST,
+                    "BluetoothHidHost", IBluetoothHidHost.class.getName()) {
+                @Override
+                public IBluetoothHidHost getServiceInterface(IBinder service) {
+                    return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothHidHost proxy object for interacting with the local
      * Bluetooth Service which handles the InputDevice profile
      */
-    /*package*/ BluetoothHidHost(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
+    /*package*/ BluetoothHidHost(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothHidHost.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     /*package*/ void close() {
         if (VDBG) log("close()");
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
+    private IBluetoothHidHost getService() {
+        return mProfileConnector.getService();
     }
 
     /**
@@ -334,7 +265,7 @@
      */
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.connect(device);
@@ -374,7 +305,7 @@
      */
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.disconnect(device);
@@ -393,7 +324,7 @@
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -412,7 +343,7 @@
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -431,7 +362,7 @@
     @Override
     public int getConnectionState(BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -461,7 +392,7 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (priority != BluetoothProfile.PRIORITY_OFF
                     && priority != BluetoothProfile.PRIORITY_ON) {
@@ -493,7 +424,7 @@
      */
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getPriority(device);
@@ -506,26 +437,6 @@
         return BluetoothProfile.PRIORITY_OFF;
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST,
-                        BluetoothHidHost.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST);
-            }
-        }
-    };
-
     private boolean isEnabled() {
         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
     }
@@ -545,7 +456,7 @@
      */
     public boolean virtualUnplug(BluetoothDevice device) {
         if (DBG) log("virtualUnplug(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.virtualUnplug(device);
@@ -571,7 +482,7 @@
      */
     public boolean getProtocolMode(BluetoothDevice device) {
         if (VDBG) log("getProtocolMode(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getProtocolMode(device);
@@ -595,7 +506,7 @@
      */
     public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
         if (DBG) log("setProtocolMode(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.setProtocolMode(device, protocolMode);
@@ -626,7 +537,7 @@
             log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
                     + "bufferSize=" + bufferSize);
         }
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getReport(device, reportType, reportId, bufferSize);
@@ -652,7 +563,7 @@
      */
     public boolean setReport(BluetoothDevice device, byte reportType, String report) {
         if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.setReport(device, reportType, report);
@@ -677,7 +588,7 @@
      */
     public boolean sendData(BluetoothDevice device, String report) {
         if (DBG) log("sendData(" + device + "), report=" + report);
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.sendData(device, report);
@@ -701,7 +612,7 @@
      */
     public boolean getIdleTime(BluetoothDevice device) {
         if (DBG) log("getIdletime(" + device + ")");
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getIdleTime(device);
@@ -726,7 +637,7 @@
      */
     public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
         if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
-        final IBluetoothHidHost service = mService;
+        final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.setIdleTime(device, idleTime);
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index fc5f830..dd2f150 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -17,14 +17,10 @@
 package android.bluetooth;
 
 import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -45,11 +41,6 @@
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
 
-    private volatile IBluetoothMap mService;
-    private final Context mContext;
-    private ServiceListener mServiceListener;
-    private BluetoothAdapter mAdapter;
-
     /** There was an error trying to obtain the state */
     public static final int STATE_ERROR = -1;
 
@@ -58,64 +49,23 @@
     /** Connection canceled before completion. */
     public static final int RESULT_CANCELED = 2;
 
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+    private BluetoothAdapter mAdapter;
+    private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.MAP,
+                    "BluetoothMap", IBluetoothMap.class.getName()) {
+                @Override
+                public IBluetoothMap getServiceInterface(IBinder service) {
+                    return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothMap proxy object.
      */
-    /*package*/ BluetoothMap(Context context, ServiceListener l) {
+    /*package*/ BluetoothMap(Context context, ServiceListener listener) {
         if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
-        mContext = context;
-        mServiceListener = l;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothMap.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     protected void finalize() throws Throwable {
@@ -133,26 +83,11 @@
      * are ok.
      */
     public synchronized void close() {
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
+    private IBluetoothMap getService() {
+        return mProfileConnector.getService();
     }
 
     /**
@@ -163,7 +98,7 @@
      */
     public int getState() {
         if (VDBG) log("getState()");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null) {
             try {
                 return service.getState();
@@ -185,7 +120,7 @@
      */
     public BluetoothDevice getClient() {
         if (VDBG) log("getClient()");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null) {
             try {
                 return service.getClient();
@@ -206,7 +141,7 @@
      */
     public boolean isConnected(BluetoothDevice device) {
         if (VDBG) log("isConnected(" + device + ")");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null) {
             try {
                 return service.isConnected(device);
@@ -238,7 +173,7 @@
     @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.disconnect(device);
@@ -279,7 +214,7 @@
      */
     public List<BluetoothDevice> getConnectedDevices() {
         if (DBG) log("getConnectedDevices()");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -299,7 +234,7 @@
      */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -319,7 +254,7 @@
      */
     public int getConnectionState(BluetoothDevice device) {
         if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -345,7 +280,7 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (priority != BluetoothProfile.PRIORITY_OFF
                     && priority != BluetoothProfile.PRIORITY_ON) {
@@ -374,7 +309,7 @@
      */
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        final IBluetoothMap service = mService;
+        final IBluetoothMap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getPriority(device);
@@ -387,24 +322,6 @@
         return PRIORITY_OFF;
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) log("Proxy object connected");
-            mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) log("Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.MAP);
-            }
-        }
-    };
-
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 1c82e19..ec0180c5 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -18,14 +18,11 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -61,11 +58,6 @@
     public static final String EXTRA_SENDER_CONTACT_NAME =
             "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
 
-    private volatile IBluetoothMapClient mService;
-    private final Context mContext;
-    private ServiceListener mServiceListener;
-    private BluetoothAdapter mAdapter;
-
     /** There was an error trying to obtain the state */
     public static final int STATE_ERROR = -1;
 
@@ -76,64 +68,23 @@
 
     private static final int UPLOADING_FEATURE_BITMASK = 0x08;
 
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+    private BluetoothAdapter mAdapter;
+    private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
+                    "BluetoothMapClient", IBluetoothMapClient.class.getName()) {
+                @Override
+                public IBluetoothMapClient getServiceInterface(IBinder service) {
+                    return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothMapClient proxy object.
      */
-    /*package*/ BluetoothMapClient(Context context, ServiceListener l) {
+    /*package*/ BluetoothMapClient(Context context, ServiceListener listener) {
         if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
-        mContext = context;
-        mServiceListener = l;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothMapClient.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     protected void finalize() throws Throwable {
@@ -151,26 +102,11 @@
      * are ok.
      */
     public void close() {
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
+    private IBluetoothMapClient getService() {
+        return mProfileConnector.getService();
     }
 
     /**
@@ -180,7 +116,7 @@
      */
     public boolean isConnected(BluetoothDevice device) {
         if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null) {
             try {
                 return service.isConnected(device);
@@ -200,7 +136,7 @@
      */
     public boolean connect(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null) {
             try {
                 return service.connect(device);
@@ -222,7 +158,7 @@
      */
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "disconnect(" + device + ")");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.disconnect(device);
@@ -242,7 +178,7 @@
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
         if (DBG) Log.d(TAG, "getConnectedDevices()");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -263,7 +199,7 @@
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -284,7 +220,7 @@
     @Override
     public int getConnectionState(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -308,7 +244,7 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (priority != BluetoothProfile.PRIORITY_OFF
                     && priority != BluetoothProfile.PRIORITY_ON) {
@@ -337,7 +273,7 @@
      */
     public int getPriority(BluetoothDevice device) {
         if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getPriority(device);
@@ -366,7 +302,7 @@
     public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
             PendingIntent sentIntent, PendingIntent deliveredIntent) {
         if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
@@ -386,7 +322,7 @@
      */
     public boolean getUnreadMessages(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
-        final IBluetoothMapClient service = mService;
+        final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getUnreadMessages(device);
@@ -406,34 +342,16 @@
      *         MapSupportedFeatures field is set. False is returned otherwise.
      */
     public boolean isUploadingSupported(BluetoothDevice device) {
+        final IBluetoothMapClient service = getService();
         try {
-            return (mService != null && isEnabled() && isValidDevice(device))
-                && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
+            return (service != null && isEnabled() && isValidDevice(device))
+                && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
         } catch (RemoteException e) {
             Log.e(TAG, e.getMessage());
         }
         return false;
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothMapClient.Stub.asInterface(service);
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT,
-                        BluetoothMapClient.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT);
-            }
-        }
-    };
-
     private boolean isEnabled() {
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 8923d73..fb78789 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -19,14 +19,10 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -122,108 +118,42 @@
      */
     public static final int PAN_OPERATION_SUCCESS = 1004;
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
     private BluetoothAdapter mAdapter;
-    private volatile IBluetoothPan mPanService;
+    private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.PAN,
+                    "BluetoothPan", IBluetoothPan.class.getName()) {
+                @Override
+                public IBluetoothPan getServiceInterface(IBinder service) {
+                    return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
+                }
+    };
+
 
     /**
      * Create a BluetoothPan proxy object for interacting with the local
      * Bluetooth Service which handles the Pan profile
      */
     @UnsupportedAppUsage
-    /*package*/ BluetoothPan(Context context, ServiceListener l) {
-        mContext = context;
-        mServiceListener = l;
+    /*package*/ BluetoothPan(Context context, ServiceListener listener) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        try {
-            mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback);
-        } catch (RemoteException re) {
-            Log.w(TAG, "Unable to register BluetoothStateChangeCallback", re);
-        }
-        if (VDBG) Log.d(TAG, "BluetoothPan() call bindService");
-        doBind();
-    }
-
-    @UnsupportedAppUsage
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothPan.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     @UnsupportedAppUsage
     /*package*/ void close() {
         if (VDBG) log("close()");
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.w(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
-            }
-        }
-
-        synchronized (mConnection) {
-            if (mPanService != null) {
-                try {
-                    mPanService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
+        mProfileConnector.disconnect();
     }
 
+    private IBluetoothPan getService() {
+        return mProfileConnector.getService();
+    }
+
+
     protected void finalize() {
         close();
     }
 
-    private final IBluetoothStateChangeCallback mStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-
-                @Override
-                public void onBluetoothStateChange(boolean on) {
-                    // Handle enable request to bind again.
-                    Log.d(TAG, "onBluetoothStateChange on: " + on);
-                    if (on) {
-                        try {
-                            if (mPanService == null) {
-                                if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()");
-                                doBind();
-                            }
-
-                        } catch (IllegalStateException e) {
-                            Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ",
-                                    e);
-
-                        } catch (SecurityException e) {
-                            Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ",
-                                    e);
-                        }
-                    } else {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mPanService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
-                }
-            };
-
     /**
      * Initiate connection to a profile of the remote bluetooth device.
      *
@@ -244,7 +174,7 @@
     @UnsupportedAppUsage
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
-        final IBluetoothPan service = mPanService;
+        final IBluetoothPan service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.connect(device);
@@ -285,7 +215,7 @@
     @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothPan service = mPanService;
+        final IBluetoothPan service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.disconnect(device);
@@ -304,7 +234,7 @@
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
-        final IBluetoothPan service = mPanService;
+        final IBluetoothPan service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -323,7 +253,7 @@
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothPan service = mPanService;
+        final IBluetoothPan service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -342,7 +272,7 @@
     @Override
     public int getConnectionState(BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
-        final IBluetoothPan service = mPanService;
+        final IBluetoothPan service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -358,7 +288,7 @@
     @UnsupportedAppUsage
     public void setBluetoothTethering(boolean value) {
         if (DBG) log("setBluetoothTethering(" + value + ")");
-        final IBluetoothPan service = mPanService;
+        final IBluetoothPan service = getService();
         if (service != null && isEnabled()) {
             try {
                 service.setBluetoothTethering(value);
@@ -371,7 +301,7 @@
     @UnsupportedAppUsage
     public boolean isTetheringOn() {
         if (VDBG) log("isTetheringOn()");
-        final IBluetoothPan service = mPanService;
+        final IBluetoothPan service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.isTetheringOn();
@@ -382,25 +312,6 @@
         return false;
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected");
-            mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.PAN,
-                        BluetoothPan.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected");
-            mPanService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.PAN);
-            }
-        }
-    };
-
     @UnsupportedAppUsage
     private boolean isEnabled() {
         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 359bec6..d94c657 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -118,28 +118,9 @@
                 public void onBluetoothStateChange(boolean up) {
                     log("onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        log("Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                if (mService != null) {
-                                    mService = null;
-                                    mContext.unbindService(mConnection);
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
+                        doUnbind();
                     } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    log("Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
+                        doBind();
                     }
                 }
             };
@@ -155,25 +136,51 @@
         if (mgr != null) {
             try {
                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
+            } catch (RemoteException re) {
+                Log.e(TAG, "", re);
             }
         }
         doBind();
     }
 
     boolean doBind() {
-        Intent intent = new Intent(IBluetoothPbap.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
-            return false;
+        synchronized (mConnection) {
+            try {
+                if (mService == null) {
+                    log("Binding service...");
+                    Intent intent = new Intent(IBluetoothPbap.class.getName());
+                    ComponentName comp = intent.resolveSystemService(
+                            mContext.getPackageManager(), 0);
+                    intent.setComponent(comp);
+                    if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                            UserHandle.CURRENT_OR_SELF)) {
+                        Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
+                        return false;
+                    }
+                }
+            } catch (SecurityException se) {
+                Log.e(TAG, "", se);
+                return false;
+            }
         }
         return true;
     }
 
+    private void doUnbind() {
+        synchronized (mConnection) {
+            if (mService != null) {
+                log("Unbinding service...");
+                try {
+                    mContext.unbindService(mConnection);
+                } catch (IllegalArgumentException ie) {
+                    Log.e(TAG, "", ie);
+                } finally {
+                    mService = null;
+                }
+            }
+        }
+    }
+
     protected void finalize() throws Throwable {
         try {
             close();
@@ -193,21 +200,11 @@
         if (mgr != null) {
             try {
                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
+            } catch (RemoteException re) {
+                Log.e(TAG, "", re);
             }
         }
-
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
+        doUnbind();
         mServiceListener = null;
     }
 
@@ -313,7 +310,7 @@
 
         public void onServiceDisconnected(ComponentName className) {
             log("Proxy object disconnected");
-            mService = null;
+            doUnbind();
             if (mServiceListener != null) {
                 mServiceListener.onServiceDisconnected();
             }
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index cbc96c0..d70e1e7 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -16,14 +16,10 @@
 
 package android.bluetooth;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -43,11 +39,6 @@
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
 
-    private volatile IBluetoothPbapClient mService;
-    private final Context mContext;
-    private ServiceListener mServiceListener;
-    private BluetoothAdapter mAdapter;
-
     /** There was an error trying to obtain the state */
     public static final int STATE_ERROR = -1;
 
@@ -56,72 +47,25 @@
     /** Connection canceled before completion. */
     public static final int RESULT_CANCELED = 2;
 
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) {
-                        Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up);
-                    }
-                    if (!up) {
-                        if (VDBG) {
-                            Log.d(TAG, "Unbinding service...");
-                        }
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) {
-                                        Log.d(TAG, "Binding service...");
-                                    }
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+    private BluetoothAdapter mAdapter;
+    private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT,
+                    "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
+                @Override
+                public IBluetoothPbapClient getServiceInterface(IBinder service) {
+                    return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothPbapClient proxy object.
      */
-    BluetoothPbapClient(Context context, ServiceListener l) {
+    BluetoothPbapClient(Context context, ServiceListener listener) {
         if (DBG) {
             Log.d(TAG, "Create BluetoothPbapClient proxy object");
         }
-        mContext = context;
-        mServiceListener = l;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        doBind();
-    }
-
-    private boolean doBind() {
-        Intent intent = new Intent(IBluetoothPbapClient.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     protected void finalize() throws Throwable {
@@ -139,26 +83,11 @@
      * are ok.
      */
     public synchronized void close() {
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
+    private IBluetoothPbapClient getService() {
+        return mProfileConnector.getService();
     }
 
     /**
@@ -174,7 +103,7 @@
         if (DBG) {
             log("connect(" + device + ") for PBAP Client.");
         }
-        final IBluetoothPbapClient service = mService;
+        final IBluetoothPbapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.connect(device);
@@ -199,7 +128,7 @@
         if (DBG) {
             log("disconnect(" + device + ")" + new Exception());
         }
-        final IBluetoothPbapClient service = mService;
+        final IBluetoothPbapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 service.disconnect(device);
@@ -226,7 +155,7 @@
         if (DBG) {
             log("getConnectedDevices()");
         }
-        final IBluetoothPbapClient service = mService;
+        final IBluetoothPbapClient service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -251,7 +180,7 @@
         if (DBG) {
             log("getDevicesMatchingStates()");
         }
-        final IBluetoothPbapClient service = mService;
+        final IBluetoothPbapClient service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -276,7 +205,7 @@
         if (DBG) {
             log("getConnectionState(" + device + ")");
         }
-        final IBluetoothPbapClient service = mService;
+        final IBluetoothPbapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -291,29 +220,6 @@
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) {
-                log("Proxy object connected");
-            }
-            mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT,
-                        BluetoothPbapClient.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) {
-                log("Proxy object disconnected");
-            }
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT);
-            }
-        }
-    };
-
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
@@ -346,7 +252,7 @@
         if (DBG) {
             log("setPriority(" + device + ", " + priority + ")");
         }
-        final IBluetoothPbapClient service = mService;
+        final IBluetoothPbapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (priority != BluetoothProfile.PRIORITY_OFF
                     && priority != BluetoothProfile.PRIORITY_ON) {
@@ -379,7 +285,7 @@
         if (VDBG) {
             log("getPriority(" + device + ")");
         }
-        final IBluetoothPbapClient service = mService;
+        final IBluetoothPbapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getPriority(device);
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
new file mode 100644
index 0000000..d9987249
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * Connector for Bluetooth profile proxies to bind manager service and
+ * profile services
+ * @param <T> The Bluetooth profile interface for this connection.
+ * @hide
+ */
+public abstract class BluetoothProfileConnector<T> {
+    private int mProfileId;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private BluetoothProfile mProfileProxy;
+    private Context mContext;
+    private String mProfileName;
+    private String mServiceName;
+    private volatile T mService;
+
+    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+            new IBluetoothStateChangeCallback.Stub() {
+        public void onBluetoothStateChange(boolean up) {
+            if (up) {
+                doBind();
+            } else {
+                doUnbind();
+            }
+        }
+    };
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            logDebug("Proxy object connected");
+            mService = getServiceInterface(service);
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(mProfileId, mProfileProxy);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            logDebug("Proxy object disconnected");
+            doUnbind();
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
+            }
+        }
+    };
+
+    BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName,
+            String serviceName) {
+        mProfileId = profileId;
+        mProfileProxy = profile;
+        mProfileName = profileName;
+        mServiceName = serviceName;
+    }
+
+    private boolean doBind() {
+        synchronized (mConnection) {
+            if (mService == null) {
+                logDebug("Binding service...");
+                try {
+                    Intent intent = new Intent(mServiceName);
+                    ComponentName comp = intent.resolveSystemService(
+                            mContext.getPackageManager(), 0);
+                    intent.setComponent(comp);
+                    if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                            UserHandle.CURRENT_OR_SELF)) {
+                        logError("Could not bind to Bluetooth Service with " + intent);
+                        return false;
+                    }
+                } catch (SecurityException se) {
+                    logError("Failed to bind service. " + se);
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private void doUnbind() {
+        synchronized (mConnection) {
+            if (mService != null) {
+                logDebug("Unbinding service...");
+                try {
+                    mContext.unbindService(mConnection);
+                } catch (IllegalArgumentException ie) {
+                    logError("Unable to unbind service: " + ie);
+                } finally {
+                    mService = null;
+                }
+            }
+        }
+    }
+
+    void connect(Context context, BluetoothProfile.ServiceListener listener) {
+        mContext = context;
+        mServiceListener = listener;
+        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException re) {
+                logError("Failed to register state change callback. " + re);
+            }
+        }
+        doBind();
+    }
+
+    void disconnect() {
+        mServiceListener = null;
+        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException re) {
+                logError("Failed to unregister state change callback" + re);
+            }
+        }
+        doUnbind();
+    }
+
+    T getService() {
+        return mService;
+    }
+
+    /**
+     * This abstract function is used to implement method to get the
+     * connected Bluetooth service interface.
+     * @param service the connected binder service.
+     * @return T the binder interface of {@code service}.
+     * @hide
+     */
+    public abstract T getServiceInterface(IBinder service);
+
+    private void logDebug(String log) {
+        Log.d(mProfileName, log);
+    }
+
+    private void logError(String log) {
+        Log.e(mProfileName, log);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index ebf6bed..e0610c8 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -17,14 +17,10 @@
 package android.bluetooth;
 
 import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -70,11 +66,6 @@
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
 
-    private volatile IBluetoothSap mService;
-    private final Context mContext;
-    private ServiceListener mServiceListener;
-    private BluetoothAdapter mAdapter;
-
     /**
      * There was an error trying to obtain the state.
      *
@@ -96,64 +87,23 @@
      */
     public static final int RESULT_CANCELED = 2;
 
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG, "Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG, "Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG, "", re);
-                            }
-                        }
-                    }
+    private BluetoothAdapter mAdapter;
+    private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.SAP,
+                    "BluetoothSap", IBluetoothSap.class.getName()) {
+                @Override
+                public IBluetoothSap getServiceInterface(IBinder service) {
+                    return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
                 }
-            };
+    };
 
     /**
      * Create a BluetoothSap proxy object.
      */
-    /*package*/ BluetoothSap(Context context, ServiceListener l) {
+    /*package*/ BluetoothSap(Context context, ServiceListener listener) {
         if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
-        mContext = context;
-        mServiceListener = l;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        doBind();
-    }
-
-    boolean doBind() {
-        Intent intent = new Intent(IBluetoothSap.class.getName());
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                UserHandle.CURRENT_OR_SELF)) {
-            Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
-            return false;
-        }
-        return true;
+        mProfileConnector.connect(context, listener);
     }
 
     protected void finalize() throws Throwable {
@@ -173,26 +123,11 @@
      * @hide
      */
     public synchronized void close() {
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (Exception e) {
-                Log.e(TAG, "", e);
-            }
-        }
+        mProfileConnector.disconnect();
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG, "", re);
-                }
-            }
-        }
-        mServiceListener = null;
+    private IBluetoothSap getService() {
+        return mProfileConnector.getService();
     }
 
     /**
@@ -204,7 +139,7 @@
      */
     public int getState() {
         if (VDBG) log("getState()");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null) {
             try {
                 return service.getState();
@@ -227,7 +162,7 @@
      */
     public BluetoothDevice getClient() {
         if (VDBG) log("getClient()");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null) {
             try {
                 return service.getClient();
@@ -250,7 +185,7 @@
      */
     public boolean isConnected(BluetoothDevice device) {
         if (VDBG) log("isConnected(" + device + ")");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null) {
             try {
                 return service.isConnected(device);
@@ -285,7 +220,7 @@
     @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.disconnect(device);
@@ -306,7 +241,7 @@
      */
     public List<BluetoothDevice> getConnectedDevices() {
         if (DBG) log("getConnectedDevices()");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getConnectedDevices();
@@ -327,7 +262,7 @@
      */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null && isEnabled()) {
             try {
                 return service.getDevicesMatchingConnectionStates(states);
@@ -348,7 +283,7 @@
      */
     public int getConnectionState(BluetoothDevice device) {
         if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getConnectionState(device);
@@ -373,7 +308,7 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (priority != BluetoothProfile.PRIORITY_OFF
                     && priority != BluetoothProfile.PRIORITY_ON) {
@@ -399,7 +334,7 @@
      */
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        final IBluetoothSap service = mService;
+        final IBluetoothSap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
                 return service.getPriority(device);
@@ -412,24 +347,6 @@
         return PRIORITY_OFF;
     }
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) log("Proxy object connected");
-            mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) log("Proxy object disconnected");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
-            }
-        }
-    };
-
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5f34f1b..8046776 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -16,6 +16,8 @@
 
 package android.content;
 
+import static android.provider.DocumentsContract.EXTRA_ORIENTATION;
+
 import android.accounts.Account;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -40,6 +42,7 @@
 import android.graphics.ImageDecoder;
 import android.graphics.ImageDecoder.ImageInfo;
 import android.graphics.ImageDecoder.Source;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
@@ -56,6 +59,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
+import android.system.Int32Ref;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -3563,9 +3567,14 @@
         // Convert to Point, since that's what the API is defined as
         final Bundle opts = new Bundle();
         opts.putParcelable(EXTRA_SIZE, Point.convert(size));
+        final Int32Ref orientation = new Int32Ref(0);
 
-        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
-            return content.openTypedAssetFile(uri, "image/*", opts, signal);
+        Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+            final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts,
+                    signal);
+            final Bundle extras = afd.getExtras();
+            orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
+            return afd;
         }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
             decoder.setAllocator(allocator);
 
@@ -3581,6 +3590,20 @@
                 decoder.setTargetSampleSize(sample);
             }
         });
+
+        // Transform the bitmap if requested. We use a side-channel to
+        // communicate the orientation, since EXIF thumbnails don't contain
+        // the rotation flags of the original image.
+        if (orientation.value != 0) {
+            final int width = bitmap.getWidth();
+            final int height = bitmap.getHeight();
+
+            final Matrix m = new Matrix();
+            m.setRotate(orientation.value, width / 2, height / 2);
+            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
+        }
+
+        return bitmap;
     }
 
     /** {@hide} */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3dd510c..0112396 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3009,7 +3009,14 @@
     /**
      * Variation of {@link #bindService} that, in the specific case of isolated
      * services, allows the caller to generate multiple instances of a service
-     * from a single component declaration.
+     * from a single component declaration.  In other words, you can use this to bind
+     * to a service that has specified {@link android.R.attr#isolatedProcess} and, in
+     * addition to the existing behavior of running in an isolated process, you can
+     * also through the arguments here have the system bring up multiple concurrent
+     * processes hosting their own instances of that service.  The <var>instanceName</var>
+     * you provide here identifies the different instances, and you can use
+     * {@link #updateServiceGroup(ServiceConnection, int, int)} to tell the system how it
+     * should manage each of these instances.
      *
      * @param service Identifies the service to connect to.  The Intent must
      *      specify an explicit component name.
@@ -3027,6 +3034,8 @@
      * @throws IllegalArgumentException If the instanceName is invalid.
      *
      * @see #bindService
+     * @see #updateServiceGroup
+     * @see android.R.attr#isolatedProcess
      */
     public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service,
             @BindServiceFlags int flags, @NonNull String instanceName,
@@ -3082,10 +3091,16 @@
      *              are considered to be related.  Supplying 0 reverts to the default behavior
      *              of not grouping.
      * @param importance Additional importance of the processes within a group.  Upon calling
-     *                   here, this will override any previous group that was set for that
-     *                   process.  This fine-tunes process killing of all processes within
-     *                   a related groups -- higher importance values will be killed before
-     *                   lower ones.
+     *                   here, this will override any previous importance that was set for that
+     *                   process.  The most important process is 0, and higher values are
+     *                   successively less important.  You can view this as describing how
+     *                   to order the processes in an array, with the processes at the end of
+     *                   the array being the least important.  This value has no meaning besides
+     *                   indicating how processes should be ordered in that array one after the
+     *                   other.  This provides a way to fine-tune the system's process killing,
+     *                   guiding it to kill processes at the end of the array first.
+     *
+     * @see #bindIsolatedService
      */
     public void updateServiceGroup(@NonNull ServiceConnection conn, int group,
             int importance) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d2f0fb3..a8815ec 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1318,6 +1318,8 @@
         public boolean isMultiPackage;
         /** {@hide} */
         public boolean isStaged;
+        /** {@hide} */
+        public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
 
         /**
          * Construct parameters for a new package install session.
@@ -1350,6 +1352,7 @@
             installerPackageName = source.readString();
             isMultiPackage = source.readBoolean();
             isStaged = source.readBoolean();
+            requiredInstalledVersionCode = source.readLong();
         }
 
         /** {@hide} */
@@ -1372,6 +1375,7 @@
             ret.installerPackageName = installerPackageName;
             ret.isMultiPackage = isMultiPackage;
             ret.isStaged = isStaged;
+            ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
             return ret;
         }
 
@@ -1509,11 +1513,6 @@
          * state of the permission can be determined only at install time and cannot be
          * changed on updated or at a later point via the package manager APIs.
          *
-         * <p>The whitelisted non-immutably restricted permissions would be added to
-         * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}
-         * while the immutably restricted permissions would be added to the {@link
-         * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist}
-         *
          * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int)
          * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int)
          */
@@ -1567,6 +1566,19 @@
             }
         }
 
+        /**
+         * Require the given version of the package be installed.
+         * The install will only be allowed if the existing version code of
+         * the package installed on the device matches the given version code.
+         * Use {@link * PackageManager#VERSION_CODE_HIGHEST} to allow
+         * installation regardless of the currently installed package version.
+         *
+         * @hide
+         */
+        public void setRequiredInstalledVersionCode(long versionCode) {
+            requiredInstalledVersionCode = versionCode;
+        }
+
         /** {@hide} */
         public void setInstallFlagsForcePermissionPrompt() {
             installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
@@ -1708,6 +1720,7 @@
             pw.printPair("installerPackageName", installerPackageName);
             pw.printPair("isMultiPackage", isMultiPackage);
             pw.printPair("isStaged", isStaged);
+            pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
             pw.println();
         }
 
@@ -1736,6 +1749,7 @@
             dest.writeString(installerPackageName);
             dest.writeBoolean(isMultiPackage);
             dest.writeBoolean(isStaged);
+            dest.writeLong(requiredInstalledVersionCode);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 33c0bb9..dd5ca67 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1423,6 +1423,14 @@
      */
     public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;
 
+    /**
+     * Installation failed return code: the required installed version code
+     * does not match the currently installed package version code.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -6918,6 +6926,7 @@
             case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
             case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
             case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
+            case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
             default: return Integer.toString(status);
         }
     }
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index b44458a..cc5d3b1 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -419,26 +419,36 @@
         Preconditions.checkNotNull(cr);
         Preconditions.checkNotNull(notifyUris);
 
-        setNotificationUris(cr, notifyUris, cr.getUserId());
+        setNotificationUris(cr, notifyUris, cr.getUserId(), true);
     }
 
-    /** @hide - set the notification uri but with an observer for a particular user's view */
-    public void setNotificationUris(ContentResolver cr, List<Uri> notifyUris, int userHandle) {
+    /**
+     * Set the notification uri but with an observer for a particular user's view. Also allows
+     * disabling the use of a self observer, which is sensible if either
+     * a) the cursor's owner calls {@link #onChange(boolean)} whenever the content changes, or
+     * b) the cursor is known not to have any content observers.
+     * @hide
+     */
+    public void setNotificationUris(ContentResolver cr, List<Uri> notifyUris, int userHandle,
+            boolean registerSelfObserver) {
         synchronized (mSelfObserverLock) {
             mNotifyUris = notifyUris;
             mNotifyUri = mNotifyUris.get(0);
             mContentResolver = cr;
             if (mSelfObserver != null) {
                 mContentResolver.unregisterContentObserver(mSelfObserver);
+                mSelfObserverRegistered = false;
             }
-            mSelfObserver = new SelfContentObserver(this);
-            final int size = mNotifyUris.size();
-            for (int i = 0; i < size; ++i) {
-                final Uri notifyUri = mNotifyUris.get(i);
-                mContentResolver.registerContentObserver(
-                        notifyUri, true, mSelfObserver, userHandle);
+            if (registerSelfObserver) {
+                mSelfObserver = new SelfContentObserver(this);
+                final int size = mNotifyUris.size();
+                for (int i = 0; i < size; ++i) {
+                    final Uri notifyUri = mNotifyUris.get(i);
+                    mContentResolver.registerContentObserver(
+                            notifyUri, true, mSelfObserver, userHandle);
+                }
+                mSelfObserverRegistered = true;
             }
-            mSelfObserverRegistered = true;
         }
     }
 
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 4aa6fab..099ae29 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -364,6 +364,8 @@
      * count steps if it is not activated. This sensor is ideal for fitness tracking applications.
      * It is defined as an {@link Sensor#REPORTING_MODE_ON_CHANGE} sensor.
      * <p>
+     * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
+     * <p>
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
      */
     public static final int TYPE_STEP_COUNTER = 19;
@@ -382,6 +384,8 @@
      * gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't
      * use the gyroscope. However, it is more noisy and will work best outdoors.
      * <p>
+     * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
+     * <p>
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
      */
     public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index d7db1f5..226b8e5 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -36,6 +36,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A class for describing camera output, which contains a {@link Surface} and its specific
@@ -692,7 +693,8 @@
                     mIsShared != other.mIsShared ||
                     mConfiguredFormat != other.mConfiguredFormat ||
                     mConfiguredDataspace != other.mConfiguredDataspace ||
-                    mConfiguredGenerationId != other.mConfiguredGenerationId)
+                    mConfiguredGenerationId != other.mConfiguredGenerationId ||
+                    !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId))
                 return false;
 
             int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 83e1980..3cdebac 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -26,12 +26,14 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
 import libcore.io.IoUtils;
 
-import java.io.FileDescriptor;
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.Executor;
@@ -45,6 +47,9 @@
 @TestApi
 @SystemService(Context.BUGREPORT_SERVICE)
 public final class BugreportManager {
+
+    private static final String TAG = "BugreportManager";
+
     private final Context mContext;
     private final IDumpstate mBinder;
 
@@ -149,16 +154,22 @@
             Preconditions.checkNotNull(executor);
             Preconditions.checkNotNull(callback);
 
+            if (screenshotFd == null) {
+                // Binder needs a valid File Descriptor to be passed
+                screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"),
+                        ParcelFileDescriptor.MODE_READ_ONLY);
+            }
             DumpstateListener dsListener = new DumpstateListener(executor, callback);
             // Note: mBinder can get callingUid from the binder transaction.
             mBinder.startBugreport(-1 /* callingUid */,
                     mContext.getOpPackageName(),
                     bugreportFd.getFileDescriptor(),
-                    (screenshotFd != null
-                            ? screenshotFd.getFileDescriptor() : new FileDescriptor()),
+                    screenshotFd.getFileDescriptor(),
                     params.getMode(), dsListener);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } catch (FileNotFoundException e) {
+            Log.wtf(TAG, "Not able to find /dev/null file: ", e);
         } finally {
             // We can close the file descriptors here because binder would have duped them.
             IoUtils.closeQuietly(bugreportFd);
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index 08afe31..1f61a3c 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -519,6 +519,13 @@
             android.Manifest.permission.PACKAGE_USAGE_STATS
     })
     public @Nullable IncidentReport getIncidentReport(Uri uri) {
+        final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID);
+        if (id == null) {
+            // If there's no report id, it's a bug report, so we can't return the incident
+            // report.
+            return null;
+        }
+
         final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE);
         if (pkg == null) {
             throw new RuntimeException("Invalid URI: No "
@@ -531,13 +538,6 @@
                     + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri);
         }
 
-        final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID);
-        if (cls == null) {
-            // If there's no report id, it's a bug report, so we can't return the incident
-            // report.
-            return null;
-        }
-    
         try {
             return getCompanionServiceLocked().getIncidentReport(pkg, cls, id);
         } catch (RemoteException ex) {
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 7cc7ccd..1b41694 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -866,8 +866,8 @@
             if (storageManager.needsCheckpoint()) {
                 Log.i(TAG, "Rescue Party requested wipe. Aborting update instead.");
                 storageManager.abortChanges("rescueparty", false);
+                return;
             }
-            return;
         } catch (RemoteException e) {
             Log.i(TAG, "Failed to handle with checkpointing. Continuing with wipe.");
         }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 6478de2..8536158 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -881,6 +881,7 @@
 
             maybeSetApiBlacklistExemptions(primaryZygoteState, false);
             maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+            maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState);
         }
     }
 
@@ -896,6 +897,7 @@
 
             maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
             maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+            maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState);
         }
     }
 
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 774d4ae..9a11104 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -263,6 +263,7 @@
      * Namespace for TextClassifier related features.
      *
      * @hide
+     * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
      */
     @SystemApi
     public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4632b75..4ac4850 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -36,6 +36,7 @@
 import android.graphics.ImageDecoder;
 import android.graphics.Point;
 import android.media.ExifInterface;
+import android.media.MediaFile;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -1698,6 +1699,18 @@
         } catch (IOException e) {
         }
 
+        // Use ImageDecoder to do full image decode of heif format file
+        // will have right orientation. So, we don't need to add orientation
+        // information into extras.
+        final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
+        if (mimeType.equals("image/heif")
+                || mimeType.equals("image/heif-sequence")
+                || mimeType.equals("image/heic")
+                || mimeType.equals("image/heic-sequence")) {
+            return new AssetFileDescriptor(pfd, 0 /* startOffset */,
+                    AssetFileDescriptor.UNKNOWN_LENGTH, null /* extras */);
+        }
+
         return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b0e980e..7c5a1fb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7762,6 +7762,9 @@
          */
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
+        private static final Validator UI_NIGHT_MODE_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+
         /**
          * Whether screensavers are enabled.
          * @hide
@@ -8908,6 +8911,7 @@
             ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
             NOTIFICATION_NEW_INTERRUPTION_MODEL,
             TRUST_AGENTS_EXTEND_UNLOCK,
+            UI_NIGHT_MODE,
             LOCK_SCREEN_WHEN_TRUST_LOST,
             SKIP_GESTURE,
             SILENCE_GESTURE,
@@ -9101,6 +9105,7 @@
             VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
             VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR);
             VALIDATORS.put(DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR);
         }
 
         /**
@@ -11866,6 +11871,7 @@
          * sync_adapter_duration            (long)
          * exempted_sync_duration           (long)
          * system_interaction_duration      (long)
+         * initial_foreground_service_start_duration (long)
          * stable_charging_threshold        (long)
          *
          * idle_duration        (long) // This is deprecated and used to circumvent b/26355386.
@@ -12038,26 +12044,27 @@
          * entity_list_default use ":" as delimiter for values. Ex:
          *
          * <pre>
-         * smart_linkify_enabled                            (boolean)
-         * system_textclassifier_enabled                    (boolean)
+         * classify_text_max_range_length                   (int)
+         * detect_language_from_text_enabled                (boolean)
+         * entity_list_default                              (String[])
+         * entity_list_editable                             (String[])
+         * entity_list_not_editable                         (String[])
+         * generate_links_log_sample_rate                   (int)
+         * generate_links_max_text_length                   (int)
+         * in_app_conversation_action_types_default         (String[])
+         * lang_id_context_settings                         (float[])
+         * lang_id_threshold_override                       (float)
+         * local_textclassifier_enabled                     (boolean)
          * model_dark_launch_enabled                        (boolean)
-         * smart_selection_enabled                          (boolean)
-         * smart_text_share_enabled                         (boolean)
+         * notification_conversation_action_types_default   (String[])
          * smart_linkify_enabled                            (boolean)
          * smart_select_animation_enabled                   (boolean)
+         * smart_selection_enabled                          (boolean)
+         * smart_text_share_enabled                         (boolean)
          * suggest_selection_max_range_length               (int)
-         * classify_text_max_range_length                   (int)
-         * generate_links_max_text_length                   (int)
-         * generate_links_log_sample_rate                   (int)
-         * entity_list_default                              (String[])
-         * entity_list_not_editable                         (String[])
-         * entity_list_editable                             (String[])
-         * in_app_conversation_action_types_default         (String[])
-         * notification_conversation_action_types_default   (String[])
-         * lang_id_threshold_override                       (float)
+         * system_textclassifier_enabled                    (boolean)
          * template_intent_factory_enabled                  (boolean)
          * translate_in_classification_enabled              (boolean)
-         * detect_language_from_text_enabled                (boolean)
          * </pre>
          *
          * <p>
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 55e6141..516d336 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -30,6 +30,7 @@
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.os.Bundle;
 import android.os.Handler;
@@ -62,11 +63,15 @@
     private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() {
         @Override
         public void provideContextImage(int taskId, GraphicBuffer contextImage,
-                Bundle imageContextRequestExtras) {
+                int colorSpaceId, Bundle imageContextRequestExtras) {
 
             Bitmap wrappedBuffer = null;
             if (contextImage != null) {
-                wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, null);
+                ColorSpace colorSpace = null;
+                if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+                    colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+                }
+                wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
             }
 
             mHandler.sendMessage(
diff --git a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
index 1926478..6240e00 100644
--- a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
+++ b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
@@ -32,6 +32,7 @@
     void provideContextImage(
             int taskId,
             in GraphicBuffer contextImage,
+            int colorSpaceId,
             in Bundle imageContextRequestExtras);
     void suggestContentSelections(
             in SelectionsRequest request,
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index cd8c7ae..c9d37bf 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1995,7 +1995,8 @@
         }
 
         /**
-         * @return the index of the activity that this state is for.
+         * @return the index of the activity that this state is for or -1
+         *     if there was no assist data captured.
          */
         public @IntRange(from = -1) int getIndex() {
             return mIndex;
@@ -2048,7 +2049,8 @@
     }
 
     /**
-     * Represents the id of an assist source activity.
+     * Represents the id of an assist source activity. You can use
+     * {@link #equals(Object)} to compare instances of this class.
      */
     public static class ActivityId {
         private final int mTaskId;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index e8b0d92..ae36e4e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -36,7 +36,6 @@
     public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
     public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
     public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
-    public static final String SAFETY_HUB = "settings_safety_hub";
     public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     public static final String FORCE_GLOBAL_ACTIONS_GRID_ENABLED =
             "settings_global_actions_force_grid_enabled";
@@ -57,7 +56,6 @@
         DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
         DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
-        DEFAULT_FLAGS.put(SAFETY_HUB, "true");
         DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
         DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false");
         DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c730fe2..b347a78 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -621,18 +621,6 @@
      */
     void setShouldShowIme(int displayId, boolean shouldShow);
 
-     /**
-     * Reparent the top layers for a display to the requested surfaceControl. The display that
-     * is going to be re-parented (the displayId passed in) needs to have been created by the same
-     * process that is requesting the re-parent. This is to ensure clients can't just re-parent
-     * display content info to any SurfaceControl, as this would be a security issue.
-     *
-     * @param displayId The id of the display.
-     * @param surfaceControlHandle The SurfaceControl that the top level layers for the
-     *        display should be re-parented to.
-     */
-    void reparentDisplayContent(int displayId, in SurfaceControl sc);
-
     /**
      * Waits for transactions to get applied before injecting input.
      * This includes waiting for the input windows to get sent to InputManager.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index b52fdb8..d269323 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -257,6 +257,31 @@
     void updatePointerIcon(IWindow window);
 
     /**
+     * Reparent the top layers for a display to the requested SurfaceControl. The display that is
+     * going to be re-parented (the displayId passed in) needs to have been created by the same
+     * process that is requesting the re-parent. This is to ensure clients can't just re-parent
+     * display content info to any SurfaceControl, as this would be a security issue.
+     *
+     * @param window The window which owns the SurfaceControl. This indicates the z-order of the
+     *               windows of this display against the windows on the parent display.
+     * @param sc The SurfaceControl that the top level layers for the display should be re-parented
+     *           to.
+     * @param displayId The id of the display to be re-parented.
+     */
+    void reparentDisplayContent(IWindow window, in SurfaceControl sc, int displayId);
+
+    /**
+     * Update the location of a child display in its parent window. This enables windows in the
+     * child display to compute the global transformation matrix.
+     *
+     * @param window The parent window of the display.
+     * @param x The x coordinate in the parent window.
+     * @param y The y coordinate in the parent window.
+     * @param displayId The id of the display to be notified.
+     */
+    void updateDisplayContentLocation(IWindow window, int x, int y, int displayId);
+
+    /**
      * Update a tap exclude region identified by provided id in the window. Touches on this region
      * will neither be dispatched to this window nor change the focus to this window. Passing an
      * invalid region will remove the area from the exclude region of this window.
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index d415387..83abf1a 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -173,6 +173,6 @@
     }
 
     private InputMethodManager getImm() {
-        return mController.getViewRoot().mDisplayContext.getSystemService(InputMethodManager.class);
+        return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class);
     }
 }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 8070e76..bbb9053 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -848,6 +848,10 @@
                 position.width() / (float) mSurfaceWidth,
                 0.0f, 0.0f,
                 position.height() / (float) mSurfaceHeight);
+        if (mViewVisibility) {
+            mRtTransaction.show(surface);
+        }
+
     }
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
@@ -914,27 +918,15 @@
             if (mSurfaceControl == null) {
                 return;
             }
-            if (mRtHandlingPositionUpdates) {
-                mRtHandlingPositionUpdates = false;
-                // This callback will happen while the UI thread is blocked, so we can
-                // safely access other member variables at this time.
-                // So do what the UI thread would have done if RT wasn't handling position
-                // updates.
-                if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, String.format("%d updateSurfacePosition, "
-                                            + "postion = [%d, %d, %d, %d]",
-                                    System.identityHashCode(this),
-                                    mScreenRect.left, mScreenRect.top,
-                                    mScreenRect.right, mScreenRect.bottom));
-                        }
-                        setParentSpaceRectangle(mScreenRect, frameNumber);
-                    } catch (Exception ex) {
-                        Log.e(TAG, "Exception configuring surface", ex);
-                    }
-                }
+
+            if (frameNumber > 0) {
+                final ViewRootImpl viewRoot = getViewRootImpl();
+
+                mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface,
+                        frameNumber);
             }
+            mRtTransaction.hide(mSurfaceControl);
+            mRtTransaction.apply();
         }
     };
 
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 3bae4b8..3d3d5dc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -177,7 +177,7 @@
      * Forces smart-dark to be always on.
      * @hide
      */
-    public static final String DEBUG_FORCE_DARK = "persist.hwui.force_dark";
+    public static final String DEBUG_FORCE_DARK = "debug.hwui.force_dark";
 
     public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
     public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5929c1b..921294a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7054,10 +7054,12 @@
 
     private FrameMetricsObserver findFrameMetricsObserver(
             Window.OnFrameMetricsAvailableListener listener) {
-        for (int i = 0; i < mFrameMetricsObservers.size(); i++) {
-            FrameMetricsObserver observer = mFrameMetricsObservers.get(i);
-            if (observer.mListener == listener) {
-                return observer;
+        if (mFrameMetricsObservers != null) {
+            for (int i = 0; i < mFrameMetricsObservers.size(); i++) {
+                FrameMetricsObserver observer = mFrameMetricsObservers.get(i);
+                if (observer.mListener == listener) {
+                    return observer;
+                }
             }
         }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b813bc3..4463e13 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -283,13 +283,7 @@
     @GuardedBy("mWindowCallbacks")
     final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
     @UnsupportedAppUsage
-    final Context mContext;
-    /**
-     * TODO(b/116349163): Check if we can merge this into {@link #mContext}.
-     * @hide
-     */
-    @NonNull
-    public Context mDisplayContext;
+    public final Context mContext;
 
     @UnsupportedAppUsage
     final IWindowSession mWindowSession;
@@ -595,7 +589,6 @@
 
     public ViewRootImpl(Context context, Display display) {
         mContext = context;
-        mDisplayContext = context.createDisplayContext(display);
         mWindowSession = WindowManagerGlobal.getWindowSession();
         mDisplay = display;
         mBasePackageName = context.getBasePackageName();
@@ -1379,7 +1372,7 @@
         } else {
             mDisplay = preferredDisplay;
         }
-        mDisplayContext = mContext.createDisplayContext(mDisplay);
+        mContext.updateDisplay(mDisplay.getDisplayId());
     }
 
     void pokeDrawLockIfNeeded() {
@@ -2725,7 +2718,7 @@
                     .mayUseInputMethod(mWindowAttributes.flags);
             if (imTarget != mLastWasImTarget) {
                 mLastWasImTarget = imTarget;
-                InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
+                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
                 if (imm != null && imTarget) {
                     imm.onPreWindowFocus(mView, hasWindowFocus);
                     imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2859,7 +2852,7 @@
             mLastWasImTarget = WindowManager.LayoutParams
                     .mayUseInputMethod(mWindowAttributes.flags);
 
-            InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
+            InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
             if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
                 imm.onPreWindowFocus(mView, hasWindowFocus);
             }
@@ -4564,8 +4557,7 @@
                     enqueueInputEvent(event, null, 0, true);
                 } break;
                 case MSG_CHECK_FOCUS: {
-                    InputMethodManager imm =
-                            mDisplayContext.getSystemService(InputMethodManager.class);
+                    InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
                     if (imm != null) {
                         imm.checkFocus();
                     }
@@ -5110,7 +5102,7 @@
         @Override
         protected int onProcess(QueuedInputEvent q) {
             if (mLastWasImTarget && !isInLocalFocusMode()) {
-                InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
+                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
                 if (imm != null) {
                     final InputEvent event = q.mEvent;
                     if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 2c79299..b387a68 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -16,6 +16,7 @@
 package android.view.autofill;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,10 +35,10 @@
     private static final int FLAG_HAS_SESSION = 0x4;
 
     private final int mViewId;
-    private final int mFlags;
+    private int mFlags;
     private final int mVirtualIntId;
     private final long mVirtualLongId;
-    private final int mSessionId;
+    private int mSessionId;
 
     /** @hide */
     @TestApi
@@ -72,6 +73,12 @@
     }
 
     /** @hide */
+    public static AutofillId withoutSession(@NonNull AutofillId id) {
+        final int flags = id.mFlags & ~FLAG_HAS_SESSION;
+        return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION);
+    }
+
+    /** @hide */
     public int getViewId() {
         return mViewId;
     }
@@ -135,7 +142,8 @@
         return !isVirtualInt() && !isVirtualLong();
     }
 
-    private boolean hasSession() {
+    /** @hide */
+    public boolean hasSession() {
         return (mFlags & FLAG_HAS_SESSION) != 0;
     }
 
@@ -144,6 +152,18 @@
         return mSessionId;
     }
 
+    /** @hide */
+    public void setSessionId(int sessionId) {
+        mFlags |= FLAG_HAS_SESSION;
+        mSessionId = sessionId;
+    }
+
+    /** @hide */
+    public void resetSessionId() {
+        mFlags &= ~FLAG_HAS_SESSION;
+        mSessionId = NO_SESSION;
+    }
+
     /////////////////////////////////
     //  Object "contract" methods. //
     /////////////////////////////////
@@ -172,6 +192,17 @@
         return true;
     }
 
+    /** @hide */
+    @TestApi
+    public boolean equalsIgnoreSession(@Nullable AutofillId other) {
+        if (this == other) return true;
+        if (other == null) return false;
+        if (mViewId != other.mViewId) return false;
+        if (mVirtualIntId != other.mVirtualIntId) return false;
+        if (mVirtualLongId != other.mVirtualLongId) return false;
+        return true;
+    }
+
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder().append(mViewId);
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 6503a80..5872d3f 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1139,6 +1139,7 @@
         if (mEnteredIds == null) {
             mEnteredIds = new ArraySet<>(1);
         }
+        id.resetSessionId();
         mEnteredIds.add(id);
     }
 
@@ -2177,6 +2178,9 @@
     private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
             boolean saveOnAllViewsInvisible, boolean saveOnFinish,
             @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
+        if (saveTriggerId != null) {
+            saveTriggerId.resetSessionId();
+        }
         synchronized (mLock) {
             if (sVerbose) {
                 Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
@@ -2202,6 +2206,7 @@
                         mFillableIds = new ArraySet<>(fillableIds.length);
                     }
                     for (AutofillId id : fillableIds) {
+                        id.resetSessionId();
                         mFillableIds.add(id);
                     }
                 }
@@ -2264,6 +2269,11 @@
      *  session when they're entered.
      */
     private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
+        if (autofillableIds != null) {
+            for (int i = 0; i < autofillableIds.size(); i++) {
+                autofillableIds.get(i).resetSessionId();
+            }
+        }
         synchronized (mLock) {
             if (sVerbose) {
                 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
@@ -2879,6 +2889,7 @@
                 final int numIds = trackedIds.length;
                 for (int i = 0; i < numIds; i++) {
                     final AutofillId id = trackedIds[i];
+                    id.resetSessionId();
 
                     if (isVisible[i]) {
                         mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index c2ad82f..e3c2bd1 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -37,6 +37,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewStructure;
+import android.view.WindowManager;
 import android.view.contentcapture.ContentCaptureSession.FlushReason;
 
 import com.android.internal.annotations.GuardedBy;
@@ -343,10 +344,9 @@
     /** @hide */
     @UiThread
     public void onActivityCreated(@NonNull IBinder applicationToken,
-            @NonNull ComponentName activityComponent, int flags) {
+            @NonNull ComponentName activityComponent) {
         if (mOptions.lite) return;
         synchronized (mLock) {
-            mFlags |= flags;
             getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags);
         }
     }
@@ -484,8 +484,43 @@
             Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
         }
 
+        MainContentCaptureSession mainSession;
         synchronized (mLock) {
-            mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP;
+            if (enabled) {
+                mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_APP;
+            } else {
+                mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_APP;
+            }
+            mainSession = mMainSession;
+        }
+        if (mainSession != null) {
+            mainSession.setDisabled(!enabled);
+        }
+    }
+
+    /**
+     * Called by apps to update flag secure when window attributes change.
+     *
+     * @hide
+     */
+    public void updateWindowAttributes(@NonNull WindowManager.LayoutParams params) {
+        if (sDebug) {
+            Log.d(TAG, "updateWindowAttributes(): window flags=" + params.flags);
+        }
+        final boolean flagSecureEnabled =
+                (params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
+
+        MainContentCaptureSession mainSession;
+        synchronized (mLock) {
+            if (flagSecureEnabled) {
+                mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+            } else {
+                mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+            }
+            mainSession = mMainSession;
+        }
+        if (mainSession != null) {
+            mainSession.setDisabled(flagSecureEnabled);
         }
     }
 
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 784cf9c..7241664 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -28,6 +28,7 @@
 import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -80,6 +81,12 @@
      */
     public static final String EXTRA_BINDER = "binder";
 
+    /**
+     * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
+     * @hide
+     */
+    public static final String EXTRA_ENABLED_STATE = "enabled";
+
     @NonNull
     private final AtomicBoolean mDisabled = new AtomicBoolean(false);
 
@@ -155,6 +162,13 @@
             public void send(int resultCode, Bundle resultData) {
                 final IBinder binder;
                 if (resultData != null) {
+                    // Change in content capture enabled.
+                    final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
+                    if (hasEnabled) {
+                        final boolean disabled = (resultCode == RESULT_CODE_FALSE);
+                        mDisabled.set(disabled);
+                        return;
+                    }
                     binder = resultData.getBinder(EXTRA_BINDER);
                     if (binder == null) {
                         Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
@@ -578,6 +592,15 @@
         return mDisabled.get();
     }
 
+    /**
+     * Sets the disabled state of content capture.
+     *
+     * @return whether disabled state was changed.
+     */
+    boolean setDisabled(boolean disabled) {
+        return mDisabled.compareAndSet(!disabled, disabled);
+    }
+
     // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
     // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
     // change should also get get rid of the "internalNotifyXXXX" methods above
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index ee5b3ec..21ead4c 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -17,6 +17,7 @@
 package android.view.inputmethod;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.os.Parcel;
@@ -388,7 +389,7 @@
                             "required when positional parameters are specified.");
                 }
             }
-            return new CursorAnchorInfo(this);
+            return CursorAnchorInfo.create(this);
         }
 
         /**
@@ -412,30 +413,90 @@
         }
     }
 
-    private CursorAnchorInfo(final Builder builder) {
-        mSelectionStart = builder.mSelectionStart;
-        mSelectionEnd = builder.mSelectionEnd;
-        mComposingTextStart = builder.mComposingTextStart;
-        mComposingText = builder.mComposingText;
-        mInsertionMarkerFlags = builder.mInsertionMarkerFlags;
-        mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal;
-        mInsertionMarkerTop = builder.mInsertionMarkerTop;
-        mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
-        mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
-        mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
-                builder.mCharacterBoundsArrayBuilder.build() : null;
-        mMatrixValues = new float[9];
+    private static CursorAnchorInfo create(Builder builder) {
+        final SparseRectFArray characterBoundsArray =
+                builder.mCharacterBoundsArrayBuilder != null
+                        ? builder.mCharacterBoundsArrayBuilder.build()
+                        : null;
+        final float[] matrixValues = new float[9];
         if (builder.mMatrixInitialized) {
-            System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
+            System.arraycopy(builder.mMatrixValues, 0, matrixValues, 0, 9);
         } else {
-            Matrix.IDENTITY_MATRIX.getValues(mMatrixValues);
+            Matrix.IDENTITY_MATRIX.getValues(matrixValues);
         }
 
+        return new CursorAnchorInfo(builder.mSelectionStart, builder.mSelectionEnd,
+                builder.mComposingTextStart, builder.mComposingText, builder.mInsertionMarkerFlags,
+                builder.mInsertionMarkerHorizontal, builder.mInsertionMarkerTop,
+                builder.mInsertionMarkerBaseline, builder.mInsertionMarkerBottom,
+                characterBoundsArray, matrixValues);
+    }
+
+    private CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart,
+            @Nullable CharSequence composingText, int insertionMarkerFlags,
+            float insertionMarkerHorizontal, float insertionMarkerTop,
+            float insertionMarkerBaseline, float insertionMarkerBottom,
+            @Nullable  SparseRectFArray characterBoundsArray, @NonNull float[] matrixValues) {
+        mSelectionStart = selectionStart;
+        mSelectionEnd = selectionEnd;
+        mComposingTextStart = composingTextStart;
+        mComposingText = composingText;
+        mInsertionMarkerFlags = insertionMarkerFlags;
+        mInsertionMarkerHorizontal = insertionMarkerHorizontal;
+        mInsertionMarkerTop = insertionMarkerTop;
+        mInsertionMarkerBaseline = insertionMarkerBaseline;
+        mInsertionMarkerBottom = insertionMarkerBottom;
+        mCharacterBoundsArray = characterBoundsArray;
+        mMatrixValues = matrixValues;
+
         // To keep hash function simple, we only use some complex objects for hash.
-        int hash = Objects.hashCode(mComposingText);
-        hash *= 31;
-        hash += Arrays.hashCode(mMatrixValues);
-        mHashCode = hash;
+        int hashCode = Objects.hashCode(mComposingText);
+        hashCode *= 31;
+        hashCode += Arrays.hashCode(matrixValues);
+        mHashCode = hashCode;
+    }
+
+    /**
+     * Creates a new instance of {@link CursorAnchorInfo} by applying {@code parentMatrix} to
+     * the coordinate transformation matrix.
+     *
+     * @param original {@link CursorAnchorInfo} to be cloned from.
+     * @param parentMatrix {@link Matrix} to be applied to {@code original.getMatrix()}
+     * @return A new instance of {@link CursorAnchorInfo} whose {@link CursorAnchorInfo#getMatrix()}
+     *         returns {@code parentMatrix * original.getMatrix()}.
+     * @hide
+     */
+    public static CursorAnchorInfo createForAdditionalParentMatrix(CursorAnchorInfo original,
+            @NonNull Matrix parentMatrix) {
+        return new CursorAnchorInfo(original.mSelectionStart, original.mSelectionEnd,
+                original.mComposingTextStart, original.mComposingText,
+                original.mInsertionMarkerFlags, original.mInsertionMarkerHorizontal,
+                original.mInsertionMarkerTop, original.mInsertionMarkerBaseline,
+                original.mInsertionMarkerBottom, original.mCharacterBoundsArray,
+                computeMatrixValues(parentMatrix, original));
+    }
+
+    /**
+     * Returns a float array that represents {@link Matrix} elements for
+     * {@code parentMatrix * info.getMatrix()}.
+     *
+     * @param parentMatrix {@link Matrix} to be multiplied.
+     * @param info {@link CursorAnchorInfo} to provide {@link Matrix} to be multiplied.
+     * @return {@code parentMatrix * info.getMatrix()}.
+     */
+    private static float[] computeMatrixValues(@NonNull Matrix parentMatrix,
+            @NonNull CursorAnchorInfo info) {
+        if (parentMatrix.isIdentity()) {
+            return info.mMatrixValues;
+        }
+
+        final Matrix newMatrix = new Matrix();
+        newMatrix.setValues(info.mMatrixValues);
+        newMatrix.postConcat(parentMatrix);
+
+        final float[] matrixValues = new float[9];
+        newMatrix.getValues(matrixValues);
+        return matrixValues;
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fd73856..d302c2b 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -33,6 +33,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
 import android.os.Binder;
@@ -425,6 +426,17 @@
      */
     private CursorAnchorInfo mCursorAnchorInfo = null;
 
+    /**
+     * A special {@link Matrix} that can be provided by the system when this instance is running
+     * inside a virtual display that is managed by {@link android.app.ActivityView}.
+     *
+     * <p>If this is non-{@code null}, {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}
+     * should be adjusted with this {@link Matrix}.</p>
+     *
+     * <p>{@code null} when not used.</p>
+     */
+    private Matrix mActivityViewToScreenMatrix = null;
+
     // -----------------------------------------------------------
 
     /**
@@ -473,6 +485,7 @@
     static final int MSG_REPORT_FULLSCREEN_MODE = 10;
     static final int MSG_REPORT_PRE_RENDERED = 15;
     static final int MSG_APPLY_IME_VISIBILITY = 20;
+    static final int MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX = 30;
 
     private static boolean isAutofillUIShowing(View servedView) {
         AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class);
@@ -510,7 +523,7 @@
             return null;
         }
         final InputMethodManager fallbackImm =
-                viewRootImpl.mDisplayContext.getSystemService(InputMethodManager.class);
+                viewRootImpl.mContext.getSystemService(InputMethodManager.class);
         if (fallbackImm == null) {
             Log.e(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view);
             return null;
@@ -579,6 +592,7 @@
                         mCurMethod = res.method;
                         mCurId = res.id;
                         mBindSequence = res.sequence;
+                        mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
                     }
                     startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
                     return;
@@ -686,6 +700,48 @@
                     }
                     return;
                 }
+                case MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX: {
+                    final float[] matrixValues = (float[]) msg.obj;
+                    final int bindSequence = msg.arg1;
+                    synchronized (mH) {
+                        if (mBindSequence != bindSequence) {
+                            return;
+                        }
+                        if (matrixValues == null) {
+                            // That this app is unbound from the parent ActivityView. In this case,
+                            // calling updateCursorAnchorInfo() isn't safe.  Only clear the matrix.
+                            mActivityViewToScreenMatrix = null;
+                            return;
+                        }
+
+                        final float[] currentValues = new float[9];
+                        mActivityViewToScreenMatrix.getValues(currentValues);
+                        if (Arrays.equals(currentValues, matrixValues)) {
+                            return;
+                        }
+                        mActivityViewToScreenMatrix.setValues(matrixValues);
+
+                        if (mCursorAnchorInfo == null || mCurMethod == null
+                                || mServedInputConnectionWrapper == null) {
+                            return;
+                        }
+                        final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode
+                                & InputConnection.CURSOR_UPDATE_MONITOR) != 0;
+                        if (!isMonitoring) {
+                            return;
+                        }
+                        // Since the host ActivityView is moved, we need to issue
+                        // IMS#updateCursorAnchorInfo() again.
+                        try {
+                            mCurMethod.updateCursorAnchorInfo(
+                                    CursorAnchorInfo.createForAdditionalParentMatrix(
+                                            mCursorAnchorInfo, mActivityViewToScreenMatrix));
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "IME died: " + mCurId, e);
+                        }
+                    }
+                    return;
+                }
             }
         }
     }
@@ -777,6 +833,11 @@
                     .sendToTarget();
         }
 
+        @Override
+        public void updateActivityViewToScreenMatrix(int bindSequence, float[] matrixValues) {
+            mH.obtainMessage(MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX, bindSequence, 0,
+                    matrixValues).sendToTarget();
+        }
     };
 
     final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -1192,6 +1253,7 @@
     @UnsupportedAppUsage
     void finishInputLocked() {
         mNextServedView = null;
+        mActivityViewToScreenMatrix = null;
         if (mServedView != null) {
             if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
             mServedView = null;
@@ -1668,6 +1730,7 @@
                             + InputMethodDebug.startInputFlagsToString(startInputFlags));
                     return false;
                 }
+                mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
                 if (res.id != null) {
                     setInputChannelLocked(res.channel);
                     mBindSequence = res.sequence;
@@ -2200,7 +2263,13 @@
             }
             if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
             try {
-                mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+                if (mActivityViewToScreenMatrix != null) {
+                    mCurMethod.updateCursorAnchorInfo(
+                            CursorAnchorInfo.createForAdditionalParentMatrix(
+                                    cursorAnchorInfo, mActivityViewToScreenMatrix));
+                } else {
+                    mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+                }
                 mCursorAnchorInfo = cursorAnchorInfo;
                 // Clear immediate bit (if any).
                 mRequestUpdateCursorAnchorInfoMonitorMode &=
@@ -2779,6 +2848,30 @@
     }
 
     /**
+     * An internal API for {@link android.app.ActivityView} to report where its embedded virtual
+     * display is placed.
+     *
+     * @param childDisplayId Display ID of the embedded virtual display.
+     * @param matrix {@link Matrix} to convert virtual display screen coordinates to
+     *               the host screen coordinates. {@code null} to clear the relationship.
+     * @hide
+     */
+    public void reportActivityView(int childDisplayId, @Nullable Matrix matrix) {
+        try {
+            final float[] matrixValues;
+            if (matrix == null) {
+                matrixValues = null;
+            } else {
+                matrixValues = new float[9];
+                matrix.getValues(matrixValues);
+            }
+            mService.reportActivityView(mClient, childDisplayId, matrixValues);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Force switch to the last used input method and subtype. If the last input method didn't have
      * any subtypes, the framework will simply switch to the last input method with no subtype
      * specified.
@@ -2887,6 +2980,16 @@
         }
     }
 
+    /**
+     * <p>This is used for CTS test only. Do not use this method outside of CTS package.<p/>
+     * @return the ID of this display which this {@link InputMethodManager} resides
+     * @hide
+     */
+    @TestApi
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
     void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
         final Printer p = new PrintWriterPrinter(fout);
         p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java
index b475412..63de059 100644
--- a/core/java/android/view/textclassifier/ConfigParser.java
+++ b/core/java/android/view/textclassifier/ConfigParser.java
@@ -17,9 +17,19 @@
 
 import android.annotation.Nullable;
 import android.provider.DeviceConfig;
+import android.util.ArrayMap;
 import android.util.KeyValueListParser;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 /**
  * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}.
@@ -27,80 +37,228 @@
  *
  * @hide
  */
-@VisibleForTesting
+@VisibleForTesting(visibility = Visibility.PACKAGE)
 public final class ConfigParser {
     private static final String TAG = "ConfigParser";
 
-    private final KeyValueListParser mParser;
+    static final boolean ENABLE_DEVICE_CONFIG = true;
 
-    // TODO: Re-enable DeviceConfig when it has reasonable performance or just delete the
-    // option of using DeviceConfig entirely.
-    static final boolean ENABLE_DEVICE_CONFIG = false;
+    private static final String STRING_LIST_DELIMITER = ":";
 
-    public ConfigParser(@Nullable String textClassifierConstants) {
-        final KeyValueListParser parser = new KeyValueListParser(',');
-        try {
-            parser.setString(textClassifierConstants);
-        } catch (IllegalArgumentException e) {
-            // Failed to parse the settings string, log this and move on with defaults.
-            Log.w(TAG, "Bad text_classifier_constants: " + textClassifierConstants);
+    private final Supplier<String> mLegacySettingsSupplier;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private final Map<String, Object> mCache = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private @Nullable KeyValueListParser mSettingsParser;  // Call getLegacySettings() instead.
+
+    public ConfigParser(Supplier<String> legacySettingsSupplier) {
+        mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier);
+    }
+
+    private KeyValueListParser getLegacySettings() {
+        synchronized (mLock) {
+            if (mSettingsParser == null) {
+                final String legacySettings = mLegacySettingsSupplier.get();
+                try {
+                    mSettingsParser = new KeyValueListParser(',');
+                    mSettingsParser.setString(legacySettings);
+                } catch (IllegalArgumentException e) {
+                    // Failed to parse the settings string, log this and move on with defaults.
+                    Log.w(TAG, "Bad text_classifier_constants: " + legacySettings);
+                }
+            }
+            return mSettingsParser;
         }
-        mParser = parser;
     }
 
     /**
-     * Reads a boolean flag.
+     * Reads a boolean setting through the cache.
      */
     public boolean getBoolean(String key, boolean defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getBoolean(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getBoolean(key, defaultValue));
-        } else {
-            return mParser.getBoolean(key, defaultValue);
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof Boolean) {
+                return (boolean) cached;
+            }
+            final boolean value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getBoolean(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getBoolean(key, defaultValue));
+            } else {
+                value = getLegacySettings().getBoolean(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
         }
     }
 
     /**
-     * Reads an integer flag.
+     * Reads an integer setting through the cache.
      */
     public int getInt(String key, int defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getInt(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getInt(key, defaultValue));
-        } else {
-            return mParser.getInt(key, defaultValue);
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof Integer) {
+                return (int) cached;
+            }
+            final int value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getInt(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getInt(key, defaultValue));
+            } else {
+                value = getLegacySettings().getInt(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
         }
     }
 
     /**
-     * Reads a float flag.
+     * Reads a float setting through the cache.
      */
     public float getFloat(String key, float defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getFloat(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getFloat(key, defaultValue));
-        } else {
-            return mParser.getFloat(key, defaultValue);
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof Float) {
+                return (float) cached;
+            }
+            final float value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getFloat(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getFloat(key, defaultValue));
+            } else {
+                value = getLegacySettings().getFloat(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
         }
     }
 
     /**
-     * Reads a string flag.
+     * Reads a string setting through the cache.
      */
     public String getString(String key, String defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getString(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getString(key, defaultValue));
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof String) {
+                return (String) cached;
+            }
+            final String value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getString(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getString(key, defaultValue));
+            } else {
+                value = getLegacySettings().getString(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
+        }
+    }
+
+    /**
+     * Reads a string list setting through the cache.
+     */
+    public List<String> getStringList(String key, List<String> defaultValue) {
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof List) {
+                final List asList = (List) cached;
+                if (asList.isEmpty()) {
+                    return Collections.emptyList();
+                } else if (asList.get(0) instanceof String) {
+                    return (List<String>) cached;
+                }
+            }
+            final List<String> value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = getDeviceConfigStringList(
+                        key,
+                        getSettingsStringList(key, defaultValue));
+            } else {
+                value = getSettingsStringList(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
+        }
+    }
+
+    /**
+     * Reads a float array through the cache. The returned array should be expected to be of the
+     * same length as that of the defaultValue.
+     */
+    public float[] getFloatArray(String key, float[] defaultValue) {
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof float[]) {
+                return (float[]) cached;
+            }
+            final float[] value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = getDeviceConfigFloatArray(
+                        key,
+                        getSettingsFloatArray(key, defaultValue));
+            } else {
+                value = getSettingsFloatArray(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
+        }
+    }
+
+    private List<String> getSettingsStringList(String key, List<String> defaultValue) {
+        return parse(mSettingsParser.getString(key, null), defaultValue);
+    }
+
+    private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
+        return parse(
+                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+                defaultValue);
+    }
+
+    private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
+        return parse(
+                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+                defaultValue);
+    }
+
+    private float[] getSettingsFloatArray(String key, float[] defaultValue) {
+        return parse(mSettingsParser.getString(key, null), defaultValue);
+    }
+
+    private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
+        if (listStr != null) {
+            return Collections.unmodifiableList(
+                    Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
+        }
+        return defaultValue;
+    }
+
+    private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
+        if (arrayStr != null) {
+            final String[] split = arrayStr.split(STRING_LIST_DELIMITER);
+            if (split.length != defaultValue.length) {
+                return defaultValue;
+            }
+            final float[] result = new float[split.length];
+            for (int i = 0; i < split.length; i++) {
+                try {
+                    result[i] = Float.parseFloat(split[i]);
+                } catch (NumberFormatException e) {
+                    return defaultValue;
+                }
+            }
+            return result;
         } else {
-            return mParser.getString(key, defaultValue);
+            return defaultValue;
         }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 876e5cc..2964f51 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -16,58 +16,39 @@
 
 package android.view.textclassifier;
 
-import android.annotation.Nullable;
-
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
-import java.util.StringJoiner;
+import java.util.function.Supplier;
 
 /**
  * TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * <pre>
- * smart_linkify_enabled                            (boolean)
- * system_textclassifier_enabled                    (boolean)
- * model_dark_launch_enabled                        (boolean)
- * smart_selection_enabled                          (boolean)
- * smart_text_share_enabled                         (boolean)
- * smart_linkify_enabled                            (boolean)
- * smart_select_animation_enabled                   (boolean)
- * suggest_selection_max_range_length               (int)
- * classify_text_max_range_length                   (int)
- * generate_links_max_text_length                   (int)
- * generate_links_log_sample_rate                   (int)
- * entity_list_default                              (String[])
- * entity_list_not_editable                         (String[])
- * entity_list_editable                             (String[])
- * in_app_conversation_action_types_default         (String[])
- * notification_conversation_action_types_default   (String[])
- * lang_id_threshold_override                       (float)
- * template_intent_factory_enabled                  (boolean)
- * translate_in_classification_enabled              (boolean)
- * detect_languages_from_text_enabled               (boolean)
- * lang_id_context_settings                         (float[])
- * </pre>
- *
+ * This is encoded as a key=value list, separated by commas.
  * <p>
- * Type: string
- * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- *
  * Example of setting the values for testing.
+ * <p>
+ * <pre>
  * adb shell settings put global text_classifier_constants \
  *      model_dark_launch_enabled=true,smart_selection_enabled=true, \
  *      entity_list_default=phone:address, \
  *      lang_id_context_settings=20:1.0:0.4
+ * </pre>
+ * <p>
+ * Settings are also available in device config. These take precedence over those in settings
+ * global.
+ * <p>
+ * <pre>
+ * adb shell cmd device_config put textclassifier system_textclassifier_enabled true
+ * </pre>
+ *
+ * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
+ * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER
  * @hide
  */
+// TODO: Rename to TextClassifierSettings.
 public final class TextClassificationConstants {
 
-    private static final String LOG_TAG = TextClassifier.DEFAULT_LOG_TAG;
-
     /**
      * Whether the smart linkify feature is enabled.
      */
@@ -188,29 +169,26 @@
     private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
     private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
     private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
-    private static final String STRING_LIST_DELIMITER = ":";
-    private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(STRING_LIST_DELIMITER)
-            .add(TextClassifier.TYPE_ADDRESS)
-            .add(TextClassifier.TYPE_EMAIL)
-            .add(TextClassifier.TYPE_PHONE)
-            .add(TextClassifier.TYPE_URL)
-            .add(TextClassifier.TYPE_DATE)
-            .add(TextClassifier.TYPE_DATE_TIME)
-            .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
-    private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
-            new StringJoiner(STRING_LIST_DELIMITER)
-                    .add(ConversationAction.TYPE_TEXT_REPLY)
-                    .add(ConversationAction.TYPE_CREATE_REMINDER)
-                    .add(ConversationAction.TYPE_CALL_PHONE)
-                    .add(ConversationAction.TYPE_OPEN_URL)
-                    .add(ConversationAction.TYPE_SEND_EMAIL)
-                    .add(ConversationAction.TYPE_SEND_SMS)
-                    .add(ConversationAction.TYPE_TRACK_FLIGHT)
-                    .add(ConversationAction.TYPE_VIEW_CALENDAR)
-                    .add(ConversationAction.TYPE_VIEW_MAP)
-                    .add(ConversationAction.TYPE_ADD_CONTACT)
-                    .add(ConversationAction.TYPE_COPY)
-                    .toString();
+    private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList(
+            TextClassifier.TYPE_ADDRESS,
+            TextClassifier.TYPE_EMAIL,
+            TextClassifier.TYPE_PHONE,
+            TextClassifier.TYPE_URL,
+            TextClassifier.TYPE_DATE,
+            TextClassifier.TYPE_DATE_TIME,
+            TextClassifier.TYPE_FLIGHT_NUMBER);
+    private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList(
+            ConversationAction.TYPE_TEXT_REPLY,
+            ConversationAction.TYPE_CREATE_REMINDER,
+            ConversationAction.TYPE_CALL_PHONE,
+            ConversationAction.TYPE_OPEN_URL,
+            ConversationAction.TYPE_SEND_EMAIL,
+            ConversationAction.TYPE_SEND_SMS,
+            ConversationAction.TYPE_TRACK_FLIGHT,
+            ConversationAction.TYPE_VIEW_CALENDAR,
+            ConversationAction.TYPE_VIEW_MAP,
+            ConversationAction.TYPE_ADD_CONTACT,
+            ConversationAction.TYPE_COPY);
     /**
      * < 0  : Not set. Use value from LangId model.
      * 0 - 1: Override value in LangId model.
@@ -221,259 +199,185 @@
     private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true;
     private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true;
     private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
-    private static final String LANG_ID_CONTEXT_SETTINGS_DEFAULT =
-            new StringJoiner(STRING_LIST_DELIMITER).add("20").add("1.0").add("0.4").toString();
+    private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f};
 
-    private final boolean mSystemTextClassifierEnabled;
-    private final boolean mLocalTextClassifierEnabled;
-    private final boolean mModelDarkLaunchEnabled;
-    private final boolean mSmartSelectionEnabled;
-    private final boolean mSmartTextShareEnabled;
-    private final boolean mSmartLinkifyEnabled;
-    private final boolean mSmartSelectionAnimationEnabled;
-    private final int mSuggestSelectionMaxRangeLength;
-    private final int mClassifyTextMaxRangeLength;
-    private final int mGenerateLinksMaxTextLength;
-    private final int mGenerateLinksLogSampleRate;
-    private final List<String> mEntityListDefault;
-    private final List<String> mEntityListNotEditable;
-    private final List<String> mEntityListEditable;
-    private final List<String> mInAppConversationActionTypesDefault;
-    private final List<String> mNotificationConversationActionTypesDefault;
-    private final float mLangIdThresholdOverride;
-    private final boolean mTemplateIntentFactoryEnabled;
-    private final boolean mTranslateInClassificationEnabled;
-    private final boolean mDetectLanguagesFromTextEnabled;
-    private final float[] mLangIdContextSettings;
+    private final ConfigParser mConfigParser;
 
-    private TextClassificationConstants(@Nullable String settings) {
-        ConfigParser configParser = new ConfigParser(settings);
-        mSystemTextClassifierEnabled =
-                configParser.getBoolean(
-                        SYSTEM_TEXT_CLASSIFIER_ENABLED,
-                        SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
-        mLocalTextClassifierEnabled =
-                configParser.getBoolean(
-                        LOCAL_TEXT_CLASSIFIER_ENABLED,
-                        LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
-        mModelDarkLaunchEnabled =
-                configParser.getBoolean(
-                        MODEL_DARK_LAUNCH_ENABLED,
-                        MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
-        mSmartSelectionEnabled =
-                configParser.getBoolean(
-                        SMART_SELECTION_ENABLED,
-                        SMART_SELECTION_ENABLED_DEFAULT);
-        mSmartTextShareEnabled =
-                configParser.getBoolean(
-                        SMART_TEXT_SHARE_ENABLED,
-                        SMART_TEXT_SHARE_ENABLED_DEFAULT);
-        mSmartLinkifyEnabled =
-                configParser.getBoolean(
-                        SMART_LINKIFY_ENABLED,
-                        SMART_LINKIFY_ENABLED_DEFAULT);
-        mSmartSelectionAnimationEnabled =
-                configParser.getBoolean(
-                        SMART_SELECT_ANIMATION_ENABLED,
-                        SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
-        mSuggestSelectionMaxRangeLength =
-                configParser.getInt(
-                        SUGGEST_SELECTION_MAX_RANGE_LENGTH,
-                        SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
-        mClassifyTextMaxRangeLength =
-                configParser.getInt(
-                        CLASSIFY_TEXT_MAX_RANGE_LENGTH,
-                        CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
-        mGenerateLinksMaxTextLength =
-                configParser.getInt(
-                        GENERATE_LINKS_MAX_TEXT_LENGTH,
-                        GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
-        mGenerateLinksLogSampleRate =
-                configParser.getInt(
-                        GENERATE_LINKS_LOG_SAMPLE_RATE,
-                        GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
-        mEntityListDefault = parseStringList(
-                configParser.getString(
-                        ENTITY_LIST_DEFAULT,
-                        ENTITY_LIST_DEFAULT_VALUE));
-        mEntityListNotEditable = parseStringList(
-                configParser.getString(
-                        ENTITY_LIST_NOT_EDITABLE,
-                        ENTITY_LIST_DEFAULT_VALUE));
-        mEntityListEditable = parseStringList(
-                configParser.getString(
-                        ENTITY_LIST_EDITABLE,
-                        ENTITY_LIST_DEFAULT_VALUE));
-        mInAppConversationActionTypesDefault = parseStringList(
-                configParser.getString(
-                        IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
-                        CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
-        mNotificationConversationActionTypesDefault = parseStringList(
-                configParser.getString(
-                        NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
-                        CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
-        mLangIdThresholdOverride =
-                configParser.getFloat(
-                        LANG_ID_THRESHOLD_OVERRIDE,
-                        LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
-        mTemplateIntentFactoryEnabled =
-                configParser.getBoolean(
-                        TEMPLATE_INTENT_FACTORY_ENABLED,
-                        TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
-        mTranslateInClassificationEnabled =
-                configParser.getBoolean(
-                        TRANSLATE_IN_CLASSIFICATION_ENABLED,
-                        TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
-        mDetectLanguagesFromTextEnabled =
-                configParser.getBoolean(
-                        DETECT_LANGUAGES_FROM_TEXT_ENABLED,
-                        DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
-        mLangIdContextSettings = parseFloatArray(
-                configParser,
-                LANG_ID_CONTEXT_SETTINGS,
-                LANG_ID_CONTEXT_SETTINGS_DEFAULT);
-    }
-
-    /** Load from a settings string. */
-    public static TextClassificationConstants loadFromString(String settings) {
-        return new TextClassificationConstants(settings);
+    public TextClassificationConstants(Supplier<String> legacySettingsSupplier) {
+        mConfigParser = new ConfigParser(legacySettingsSupplier);
     }
 
     public boolean isLocalTextClassifierEnabled() {
-        return mLocalTextClassifierEnabled;
+        return mConfigParser.getBoolean(
+                LOCAL_TEXT_CLASSIFIER_ENABLED,
+                LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
     }
 
     public boolean isSystemTextClassifierEnabled() {
-        return mSystemTextClassifierEnabled;
+        return mConfigParser.getBoolean(
+                SYSTEM_TEXT_CLASSIFIER_ENABLED,
+                SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
     }
 
     public boolean isModelDarkLaunchEnabled() {
-        return mModelDarkLaunchEnabled;
+        return mConfigParser.getBoolean(
+                MODEL_DARK_LAUNCH_ENABLED,
+                MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
     }
 
     public boolean isSmartSelectionEnabled() {
-        return mSmartSelectionEnabled;
+        return mConfigParser.getBoolean(
+                SMART_SELECTION_ENABLED,
+                SMART_SELECTION_ENABLED_DEFAULT);
     }
 
     public boolean isSmartTextShareEnabled() {
-        return mSmartTextShareEnabled;
+        return mConfigParser.getBoolean(
+                SMART_TEXT_SHARE_ENABLED,
+                SMART_TEXT_SHARE_ENABLED_DEFAULT);
     }
 
     public boolean isSmartLinkifyEnabled() {
-        return mSmartLinkifyEnabled;
+        return mConfigParser.getBoolean(
+                SMART_LINKIFY_ENABLED,
+                SMART_LINKIFY_ENABLED_DEFAULT);
     }
 
     public boolean isSmartSelectionAnimationEnabled() {
-        return mSmartSelectionAnimationEnabled;
+        return mConfigParser.getBoolean(
+                SMART_SELECT_ANIMATION_ENABLED,
+                SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
     }
 
     public int getSuggestSelectionMaxRangeLength() {
-        return mSuggestSelectionMaxRangeLength;
+        return mConfigParser.getInt(
+                SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+                SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
     }
 
     public int getClassifyTextMaxRangeLength() {
-        return mClassifyTextMaxRangeLength;
+        return mConfigParser.getInt(
+                CLASSIFY_TEXT_MAX_RANGE_LENGTH,
+                CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
     }
 
     public int getGenerateLinksMaxTextLength() {
-        return mGenerateLinksMaxTextLength;
+        return mConfigParser.getInt(
+                GENERATE_LINKS_MAX_TEXT_LENGTH,
+                GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
     }
 
     public int getGenerateLinksLogSampleRate() {
-        return mGenerateLinksLogSampleRate;
+        return mConfigParser.getInt(
+                GENERATE_LINKS_LOG_SAMPLE_RATE,
+                GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
     }
 
     public List<String> getEntityListDefault() {
-        return mEntityListDefault;
+        return mConfigParser.getStringList(
+                ENTITY_LIST_DEFAULT,
+                ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getEntityListNotEditable() {
-        return mEntityListNotEditable;
+        return mConfigParser.getStringList(
+                ENTITY_LIST_NOT_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getEntityListEditable() {
-        return mEntityListEditable;
+        return mConfigParser.getStringList(
+                ENTITY_LIST_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getInAppConversationActionTypes() {
-        return mInAppConversationActionTypesDefault;
+        return mConfigParser.getStringList(
+                IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
+                CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
     }
 
     public List<String> getNotificationConversationActionTypes() {
-        return mNotificationConversationActionTypesDefault;
+        return mConfigParser.getStringList(
+                NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
+                CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
     }
 
     public float getLangIdThresholdOverride() {
-        return mLangIdThresholdOverride;
+        return mConfigParser.getFloat(
+                LANG_ID_THRESHOLD_OVERRIDE,
+                LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
     }
 
     public boolean isTemplateIntentFactoryEnabled() {
-        return mTemplateIntentFactoryEnabled;
+        return mConfigParser.getBoolean(
+                TEMPLATE_INTENT_FACTORY_ENABLED,
+                TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
     }
 
     public boolean isTranslateInClassificationEnabled() {
-        return mTranslateInClassificationEnabled;
+        return mConfigParser.getBoolean(
+                TRANSLATE_IN_CLASSIFICATION_ENABLED,
+                TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
     }
 
     public boolean isDetectLanguagesFromTextEnabled() {
-        return mDetectLanguagesFromTextEnabled;
+        return mConfigParser.getBoolean(
+                DETECT_LANGUAGES_FROM_TEXT_ENABLED,
+                DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
     }
 
     public float[] getLangIdContextSettings() {
-        return mLangIdContextSettings;
-    }
-
-    private static List<String> parseStringList(String listStr) {
-        return Collections.unmodifiableList(Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
-    }
-
-    private static float[] parseFloatArray(
-            ConfigParser configParser, String key, String defaultStr) {
-        final String str = configParser.getString(key, defaultStr);
-        final String[] defaultSplit = defaultStr.split(STRING_LIST_DELIMITER);
-        String[] split = str.split(STRING_LIST_DELIMITER);
-        if (split.length != defaultSplit.length) {
-            Log.v(LOG_TAG, "Error parsing " + key + " flag. Using defaults.");
-            split = defaultSplit;
-        }
-        final float[] result = new float[split.length];
-        for (int i = 0; i < split.length; i++) {
-            try {
-                result[i] = Float.parseFloat(split[i]);
-            } catch (NumberFormatException e) {
-                Log.v(LOG_TAG, "Error parsing part of " + key + " flag. Using defaults.");
-                result[i] = Float.parseFloat(defaultSplit[i]);
-            }
-        }
-        return result;
+        return mConfigParser.getFloatArray(
+                LANG_ID_CONTEXT_SETTINGS,
+                LANG_ID_CONTEXT_SETTINGS_DEFAULT);
     }
 
     void dump(IndentingPrintWriter pw) {
         pw.println("TextClassificationConstants:");
         pw.increaseIndent();
-        pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled);
-        pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled);
-        pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled);
-        pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled);
-        pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled);
-        pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled);
-        pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled);
-        pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength);
-        pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength);
-        pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength);
-        pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate);
-        pw.printPair("getEntityListDefault", mEntityListDefault);
-        pw.printPair("getEntityListNotEditable", mEntityListNotEditable);
-        pw.printPair("getEntityListEditable", mEntityListEditable);
-        pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault);
-        pw.printPair("getNotificationConversationActionTypes",
-                mNotificationConversationActionTypesDefault);
-        pw.printPair("getLangIdThresholdOverride", mLangIdThresholdOverride);
-        pw.printPair("isTemplateIntentFactoryEnabled", mTemplateIntentFactoryEnabled);
-        pw.printPair("isTranslateInClassificationEnabled", mTranslateInClassificationEnabled);
-        pw.printPair("isDetectLanguageFromTextEnabled", mDetectLanguagesFromTextEnabled);
-        pw.printPair("getLangIdContextSettings", Arrays.toString(mLangIdContextSettings));
+        pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
+                .println();
+        pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled())
+                .println();
+        pw.printPair("entity_list_default", getEntityListDefault())
+                .println();
+        pw.printPair("entity_list_editable", getEntityListEditable())
+                .println();
+        pw.printPair("entity_list_not_editable", getEntityListNotEditable())
+                .println();
+        pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate())
+                .println();
+        pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
+                .println();
+        pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes())
+                .println();
+        pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings()))
+                .println();
+        pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride())
+                .println();
+        pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
+                .println();
+        pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
+                .println();
+        pw.printPair("notification_conversation_action_types_default",
+                getNotificationConversationActionTypes()).println();
+        pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
+                .println();
+        pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
+                .println();
+        pw.printPair("smart_selection_enabled", isSmartSelectionEnabled())
+                .println();
+        pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
+                .println();
+        pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength())
+                .println();
+        pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
+                .println();
+        pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled())
+                .println();
+        pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
+                .println();
         pw.decreaseIndent();
-        pw.println();
     }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 042b943..95ca9de 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -45,6 +45,9 @@
 
     private static final String LOG_TAG = "TextClassificationManager";
 
+    private static final TextClassificationConstants sDefaultSettings =
+            new TextClassificationConstants(() ->  null);
+
     private final Object mLock = new Object();
     private final TextClassificationSessionFactory mDefaultSessionFactory =
             classificationContext -> new TextClassificationSession(
@@ -129,9 +132,10 @@
     private TextClassificationConstants getSettings() {
         synchronized (mLock) {
             if (mSettings == null) {
-                mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
-                        getApplicationContext().getContentResolver(),
-                        Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+                mSettings = new TextClassificationConstants(
+                        () ->  Settings.Global.getString(
+                                getApplicationContext().getContentResolver(),
+                                Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
             }
             return mSettings;
         }
@@ -251,7 +255,11 @@
 
     /** @hide */
     @VisibleForTesting
-    public void invalidate() {
+    public void invalidateForTesting() {
+        invalidate();
+    }
+
+    private void invalidate() {
         synchronized (mLock) {
             mSettings = null;
             mLocalTextClassifier = null;
@@ -280,9 +288,8 @@
         if (tcm != null) {
             return tcm.getSettings();
         } else {
-            return TextClassificationConstants.loadFromString(Settings.Global.getString(
-                    context.getApplicationContext().getContentResolver(),
-                    Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+            // Use default settings if there is no tcm.
+            return sDefaultSettings;
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 3297523..3e95f1b 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -301,7 +301,7 @@
             final ZonedDateTime refTime = ZonedDateTime.now();
             final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
                     ? request.getEntityConfig().resolveEntityListModifications(
-                            getEntitiesForHints(request.getEntityConfig().getHints()))
+                    getEntitiesForHints(request.getEntityConfig().getHints()))
                     : mSettings.getEntityListDefault();
             final String localesString = concatenateLocales(request.getDefaultLocales());
             final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
@@ -779,8 +779,8 @@
         final float moreTextScoreRatio = 1f - subjectTextScoreRatio;
         Log.v(LOG_TAG,
                 String.format(Locale.US, "LangIdContextSettings: "
-                        + "minimumTextSize=%d, penalizeRatio=%.2f, "
-                        + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
+                                + "minimumTextSize=%d, penalizeRatio=%.2f, "
+                                + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
                         minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio));
 
         if (end - start < minimumTextSize && penalizeRatio <= 0) {
@@ -903,4 +903,3 @@
         }
     }
 }
-
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index 87d80d4..9ac979b 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -43,7 +43,7 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "AbstractResolverComp";
 
-    private AfterCompute mAfterCompute;
+    protected AfterCompute mAfterCompute;
     protected final PackageManager mPm;
     protected final UsageStatsManager mUsm;
     protected String[] mAnnotations;
@@ -70,11 +70,7 @@
                         Log.d(TAG, "RANKER_SERVICE_RESULT");
                     }
                     if (mHandler.hasMessages(RANKER_RESULT_TIMEOUT)) {
-                        if (msg.obj != null) {
-                            handleResultMessage(msg);
-                        } else {
-                            Log.e(TAG, "Receiving null prediction results.");
-                        }
+                        handleResultMessage(msg);
                         mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
                         afterCompute();
                     }
@@ -99,7 +95,6 @@
         mHttp = "http".equals(scheme) || "https".equals(scheme);
         mContentType = intent.getType();
         getContentAnnotations(intent);
-
         mPm = context.getPackageManager();
         mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
         mDefaultBrowserPackageName = mHttp
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index 3b4e1a0..3fddfc8 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -28,7 +28,7 @@
 import android.content.pm.ResolveInfo;
 import android.os.Message;
 import android.os.UserHandle;
-import android.view.textclassifier.Log;
+import android.util.Log;
 
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
@@ -36,29 +36,47 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executors;
 
 /**
- * Uses an {@link AppPredictor} to sort Resolver targets.
+ * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
+ * disabled by returning an empty sorted target list, {@link AppPredictionServiceResolverComparator}
+ * will fallback to using a {@link ResolverRankerServiceResolverComparator}.
  */
 class AppPredictionServiceResolverComparator extends AbstractResolverComparator {
 
     private static final String TAG = "APSResolverComparator";
+    private static final boolean DEBUG = false;
 
     private final AppPredictor mAppPredictor;
     private final Context mContext;
     private final Map<ComponentName, Integer> mTargetRanks = new HashMap<>();
     private final UserHandle mUser;
+    private final Intent mIntent;
+    private final String mReferrerPackage;
+    // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall
+    // back to using the ResolverRankerService.
+    private ResolverRankerServiceResolverComparator mResolverRankerService;
 
     AppPredictionServiceResolverComparator(
-                Context context, Intent intent, AppPredictor appPredictor, UserHandle user) {
+                Context context,
+                Intent intent,
+                String referrerPackage,
+                AppPredictor appPredictor,
+                UserHandle user) {
         super(context, intent);
         mContext = context;
+        mIntent = intent;
         mAppPredictor = appPredictor;
         mUser = user;
+        mReferrerPackage = referrerPackage;
     }
 
     @Override
     int compare(ResolveInfo lhs, ResolveInfo rhs) {
+        if (mResolverRankerService != null) {
+            return mResolverRankerService.compare(lhs, rhs);
+        }
         Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
                 lhs.activityInfo.name));
         Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
@@ -75,33 +93,59 @@
 
     @Override
     void doCompute(List<ResolvedComponentInfo> targets) {
+        if (targets.isEmpty()) {
+            mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT);
+            return;
+        }
         List<AppTarget> appTargets = new ArrayList<>();
         for (ResolvedComponentInfo target : targets) {
             appTargets.add(new AppTarget.Builder(new AppTargetId(target.name.flattenToString()))
                     .setTarget(target.name.getPackageName(), mUser)
                     .setClassName(target.name.getClassName()).build());
         }
-        mAppPredictor.sortTargets(appTargets, mContext.getMainExecutor(),
+        mAppPredictor.sortTargets(appTargets, Executors.newSingleThreadExecutor(),
                 sortedAppTargets -> {
-                    Message msg =
+                    if (sortedAppTargets.isEmpty()) {
+                        if (DEBUG) {
+                            Log.d(TAG, "AppPredictionService disabled. Using resolver.");
+                        }
+                        // APS for chooser is disabled. Fallback to resolver.
+                        mResolverRankerService =
+                                new ResolverRankerServiceResolverComparator(
+                                    mContext, mIntent, mReferrerPackage,
+                                        () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT));
+                        mResolverRankerService.compute(targets);
+                    } else {
+                        if (DEBUG) {
+                            Log.d(TAG, "AppPredictionService response received");
+                        }
+                        Message msg =
                             Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets);
-                    msg.sendToTarget();
-                });
+                        msg.sendToTarget();
+                    }
+                }
+        );
     }
 
     @Override
     void handleResultMessage(Message msg) {
-        if (msg.what == RANKER_SERVICE_RESULT) {
+        // Null value is okay if we have defaulted to the ResolverRankerService.
+        if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) {
             final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj;
             for (int i = 0; i < sortedAppTargets.size(); i++) {
                 mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(),
                         sortedAppTargets.get(i).getClassName()), i);
             }
+        } else if (msg.obj == null && mResolverRankerService == null) {
+            Log.e(TAG, "Unexpected null result");
         }
     }
 
     @Override
     float getScore(ComponentName name) {
+        if (mResolverRankerService != null) {
+            return mResolverRankerService.getScore(name);
+        }
         Integer rank = mTargetRanks.get(name);
         if (rank == null) {
             Log.w(TAG, "Score requested for unknown component.");
@@ -113,6 +157,10 @@
 
     @Override
     void updateModel(ComponentName componentName) {
+        if (mResolverRankerService != null) {
+            mResolverRankerService.updateModel(componentName);
+            return;
+        }
         mAppPredictor.notifyAppTargetEvent(
                 new AppTargetEvent.Builder(
                     new AppTarget.Builder(
@@ -121,4 +169,12 @@
                         .setClassName(componentName.getClassName()).build(),
                     ACTION_LAUNCH).build());
     }
+
+    @Override
+    void destroy() {
+        if (mResolverRankerService != null) {
+            mResolverRankerService.destroy();
+            mResolverRankerService = null;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1eabbd8..b605eaf 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -153,8 +153,8 @@
      * {@link AppPredictionManager} will be queried for direct share targets.
      */
     // TODO(b/123089490): Replace with system flag
-    private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = false;
-    private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = false;
+    private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = true;
+    private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true;
     // TODO(b/123088566) Share these in a better way.
     private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
     public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
@@ -177,7 +177,7 @@
      */
     private static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200;
 
-    private static final float DIRECT_SHARE_EXPANSION_RATE = 0.7f;
+    private static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f;
 
     // TODO(b/121287224): Re-evaluate this limit
     private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
@@ -453,6 +453,11 @@
                 if (mChooserListAdapter == null) {
                     return;
                 }
+                if (resultList.isEmpty()) {
+                    // APS may be disabled, so try querying targets ourselves.
+                    queryDirectShareTargets(mChooserListAdapter, true);
+                    return;
+                }
                 final List<DisplayResolveInfo> driList =
                         getDisplayResolveInfos(mChooserListAdapter);
                 final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
@@ -919,6 +924,8 @@
         mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE);
         mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
         mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
+        mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT);
+        mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED);
         if (mAppPredictor != null) {
             mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback);
             mAppPredictor.destroy();
@@ -1272,11 +1279,14 @@
         return driList;
     }
 
-    private void queryDirectShareTargets(ChooserListAdapter adapter) {
-        AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
-        if (appPredictor != null) {
-            appPredictor.requestPredictionUpdate();
-            return;
+    private void queryDirectShareTargets(
+                ChooserListAdapter adapter, boolean skipAppPredictionService) {
+        if (!skipAppPredictionService) {
+            AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
+            if (appPredictor != null) {
+                appPredictor.requestPredictionUpdate();
+                return;
+            }
         }
         // Default to just querying ShortcutManager if AppPredictor not present.
         final IntentFilter filter = getTargetIntentFilter();
@@ -1609,7 +1619,7 @@
         AbstractResolverComparator resolverComparator;
         if (appPredictor != null) {
             resolverComparator = new AppPredictionServiceResolverComparator(this, getTargetIntent(),
-                    appPredictor, getUser());
+                    getReferrerPackageName(), appPredictor, getUser());
         } else {
             resolverComparator =
                     new ResolverRankerServiceResolverComparator(this, getTargetIntent(),
@@ -2027,21 +2037,29 @@
                     return;
                 }
 
-                int lastHeight = 0;
+                int directShareHeight = 0;
                 rowsToShow = Math.min(4, rowsToShow);
                 for (int i = 0; i < Math.min(rowsToShow, mAdapterView.getChildCount()); i++) {
-                    lastHeight = mAdapterView.getChildAt(i).getHeight();
-                    offset += lastHeight;
+                    View child = mAdapterView.getChildAt(i);
+                    int height = child.getHeight();
+                    offset += height;
+
+                    if (child.getTag() != null
+                            && (child.getTag() instanceof DirectShareViewHolder)) {
+                        directShareHeight = height;
+                    }
                 }
 
                 boolean isPortrait = getResources().getConfiguration().orientation
                                          == Configuration.ORIENTATION_PORTRAIT;
-                if (lastHeight != 0 && isSendAction(getTargetIntent()) && isPortrait) {
+                if (directShareHeight != 0 && isSendAction(getTargetIntent()) && isPortrait) {
                     // make sure to leave room for direct share 4->8 expansion
-                    int expansionArea =
-                            (int) (mResolverDrawerLayout.getAlwaysShowHeight()
-                                    / DIRECT_SHARE_EXPANSION_RATE);
-                    offset = Math.min(offset, bottom - top - lastHeight - expansionArea);
+                    int requiredExpansionHeight =
+                            (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
+                    int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
+                                        - requiredExpansionHeight;
+
+                    offset = Math.min(offset, minHeight);
                 }
 
                 mResolverDrawerLayout.setCollapsibleHeightReserved(Math.min(offset, bottom - top));
@@ -2062,6 +2080,8 @@
 
         private static final int MAX_SERVICE_TARGETS = 8;
 
+        private int mNumShortcutResults = 0;
+
         // Reserve spots for incoming direct share targets by adding placeholders
         private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo();
         private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
@@ -2205,7 +2225,7 @@
                     Log.d(TAG, "querying direct share targets from ShortcutManager");
                 }
 
-                queryDirectShareTargets(this);
+                queryDirectShareTargets(this, false);
             }
             if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
                 if (DEBUG) {
@@ -2397,9 +2417,15 @@
                     // This incents ChooserTargetServices to define what's truly better.
                     targetScore = lastScore * 0.95f;
                 }
-                shouldNotify |= insertServiceTarget(
+                boolean isInserted = insertServiceTarget(
                         new SelectableTargetInfo(origTarget, target, targetScore));
 
+                if (isInserted && isShortcutResult) {
+                    mNumShortcutResults++;
+                }
+
+                shouldNotify |= isInserted;
+
                 if (DEBUG) {
                     Log.d(TAG, " => " + target.toString() + " score=" + targetScore
                             + " base=" + target.getScore()
@@ -2415,6 +2441,10 @@
             }
         }
 
+        private int getNumShortcutResults() {
+            return mNumShortcutResults;
+        }
+
         /**
           * Use the scoring system along with artificial boosts to create up to 3 distinct buckets:
           * <ol>
@@ -2871,10 +2901,10 @@
 
             if (startType != lastStartType
                     || rowPosition == getContentPreviewRowCount() + getProfileRowCount()) {
-                row.setBackground(
+                row.setForeground(
                         getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
             } else {
-                row.setBackground(null);
+                row.setForeground(null);
             }
 
             int columnCount = holder.getColumnCount();
@@ -2945,7 +2975,15 @@
         }
 
         public void handleScroll(View v, int y, int oldy) {
-            if (mDirectShareViewHolder != null) {
+            // Only expand direct share area if there is a minimum number of shortcuts,
+            // which will help reduce the amount of visible shuffling due to older-style
+            // direct share targets.
+            int orientation = getResources().getConfiguration().orientation;
+            boolean canExpandDirectShare =
+                    mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow()
+                    && orientation == Configuration.ORIENTATION_PORTRAIT;
+
+            if (mDirectShareViewHolder != null && canExpandDirectShare) {
                 mDirectShareViewHolder.handleScroll(mAdapterView, y, oldy, getMaxTargetsPerRow());
             }
         }
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index a781907..01e0622 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -66,9 +66,6 @@
 
     // timeout for establishing connections with a ResolverRankerService.
     private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
-    // timeout for establishing connections with a ResolverRankerService, collecting features and
-    // predicting ranking scores.
-    private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
 
     private final Collator mCollator;
     private final Map<String, UsageStats> mStats;
@@ -106,6 +103,10 @@
         if (msg.what != RANKER_SERVICE_RESULT) {
             return;
         }
+        if (msg.obj == null) {
+            Log.e(TAG, "Receiving null prediction results.");
+            return;
+        }
         final List<ResolverTarget> receivedTargets = (List<ResolverTarget>) msg.obj;
         if (receivedTargets != null && mTargets != null
                     && receivedTargets.size() == mTargets.size()) {
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 9e8bd64..cc2caca 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -60,9 +60,11 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
@@ -613,28 +615,28 @@
         return projection == null ? mDefaultProjection : projection;
     }
 
-    private void startObserving(File file, Uri notifyUri) {
+    private void startObserving(File file, Uri notifyUri, DirectoryCursor cursor) {
         synchronized (mObservers) {
             DirectoryObserver observer = mObservers.get(file);
             if (observer == null) {
-                observer = new DirectoryObserver(
-                        file, getContext().getContentResolver(), notifyUri);
+                observer =
+                        new DirectoryObserver(file, getContext().getContentResolver(), notifyUri);
                 observer.startWatching();
                 mObservers.put(file, observer);
             }
-            observer.mRefCount++;
+            observer.mCursors.add(cursor);
 
             if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer);
         }
     }
 
-    private void stopObserving(File file) {
+    private void stopObserving(File file, DirectoryCursor cursor) {
         synchronized (mObservers) {
             DirectoryObserver observer = mObservers.get(file);
             if (observer == null) return;
 
-            observer.mRefCount--;
-            if (observer.mRefCount == 0) {
+            observer.mCursors.remove(cursor);
+            if (observer.mCursors.size() == 0) {
                 mObservers.remove(file);
                 observer.stopWatching();
             }
@@ -650,27 +652,31 @@
         private final File mFile;
         private final ContentResolver mResolver;
         private final Uri mNotifyUri;
+        private final CopyOnWriteArrayList<DirectoryCursor> mCursors;
 
-        private int mRefCount = 0;
-
-        public DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
+        DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
             super(file.getAbsolutePath(), NOTIFY_EVENTS);
             mFile = file;
             mResolver = resolver;
             mNotifyUri = notifyUri;
+            mCursors = new CopyOnWriteArrayList<>();
         }
 
         @Override
         public void onEvent(int event, String path) {
             if ((event & NOTIFY_EVENTS) != 0) {
                 if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path);
+                for (DirectoryCursor cursor : mCursors) {
+                    cursor.notifyChanged();
+                }
                 mResolver.notifyChange(mNotifyUri, null, false);
             }
         }
 
         @Override
         public String toString() {
-            return "DirectoryObserver{file=" + mFile.getAbsolutePath() + ", ref=" + mRefCount + "}";
+            String filePath = mFile.getAbsolutePath();
+            return "DirectoryObserver{file=" + filePath + ", ref=" + mCursors.size() + "}";
         }
     }
 
@@ -681,16 +687,22 @@
             super(columnNames);
 
             final Uri notifyUri = buildNotificationUri(docId);
-            setNotificationUri(getContext().getContentResolver(), notifyUri);
+            boolean registerSelfObserver = false; // Our FileObserver sees all relevant changes.
+            setNotificationUris(getContext().getContentResolver(), Arrays.asList(notifyUri),
+                    getContext().getContentResolver().getUserId(), registerSelfObserver);
 
             mFile = file;
-            startObserving(mFile, notifyUri);
+            startObserving(mFile, notifyUri, this);
+        }
+
+        public void notifyChanged() {
+            onChange(false);
         }
 
         @Override
         public void close() {
             super.close();
-            stopObserving(mFile);
+            stopObserving(mFile, this);
         }
     }
 }
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 5d08a25..ad1ff90 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -64,6 +64,8 @@
     public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
     public static final int RECOMMEND_FAILED_INVALID_URI = -6;
     public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
+    /** {@hide} */
+    public static final int RECOMMEND_FAILED_WRONG_INSTALLED_VERSION = -8;
 
     private static final String TAG = "PackageHelper";
     // App installation location settings values
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index dd6fb72..2987b4e 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -406,8 +406,13 @@
         private int mHiddenApiAccessStatslogSampleRate = 0;
 
         public static void setHiddenApiAccessLogSampleRates(int sampleRate, int newSampleRate) {
-            sInstance.mHiddenApiAccessLogSampleRate = sampleRate;
-            sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate;
+            if (sampleRate != -1) {
+                sInstance.mHiddenApiAccessLogSampleRate = sampleRate;
+            }
+
+            if (newSampleRate != -1) {
+                sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate;
+            }
         }
 
         public static HiddenApiUsageLogger getInstance() {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index cfc32cf..7e501d2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -50,9 +50,11 @@
      * @param mask which flags to change
      * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
      * @param dockedBounds the current bounds of the docked stack, in screen coordinates
+     * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
      */
     void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, in Rect fullscreenBounds, in Rect dockedBounds);
+            int mask, in Rect fullscreenBounds, in Rect dockedBounds,
+            boolean navbarColorManagedByIme);
 
     void topAppWindowChanged(int displayId, boolean menuVisible);
     void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index ff94264..6b0f8b2 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -39,12 +39,13 @@
     public final IBinder mImeToken;
     public final Rect mFullscreenStackBounds;
     public final Rect mDockedStackBounds;
+    public final boolean mNavbarColorManagedByIme;
 
     public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
             int systemUiVisibility, boolean menuVisible, int imeWindowVis, int imeBackDisposition,
             boolean showImeSwitcher, int disabledFlags2, int fullscreenStackSysUiVisibility,
             int dockedStackSysUiVisibility, IBinder imeToken, Rect fullscreenStackBounds,
-            Rect dockedStackBounds) {
+            Rect dockedStackBounds, boolean navbarColorManagedByIme) {
         mIcons = new ArrayMap<>(icons);
         mDisabledFlags1 = disabledFlags1;
         mSystemUiVisibility = systemUiVisibility;
@@ -58,6 +59,7 @@
         mImeToken = imeToken;
         mFullscreenStackBounds = fullscreenStackBounds;
         mDockedStackBounds = dockedStackBounds;
+        mNavbarColorManagedByIme = navbarColorManagedByIme;
     }
 
     @Override
@@ -80,6 +82,7 @@
         dest.writeStrongBinder(mImeToken);
         dest.writeTypedObject(mFullscreenStackBounds, flags);
         dest.writeTypedObject(mDockedStackBounds, flags);
+        dest.writeBoolean(mNavbarColorManagedByIme);
     }
 
     /**
@@ -103,11 +106,12 @@
                     final IBinder imeToken = source.readStrongBinder();
                     final Rect fullscreenStackBounds = source.readTypedObject(Rect.CREATOR);
                     final Rect dockedStackBounds = source.readTypedObject(Rect.CREATOR);
+                    final boolean navbarColorManagedByIme = source.readBoolean();
                     return new RegisterStatusBarResult(icons, disabledFlags1, systemUiVisibility,
                             menuVisible, imeWindowVis, imeBackDisposition, showImeSwitcher,
                             disabledFlags2, fullscreenStackSysUiVisibility,
                             dockedStackSysUiVisibility, imeToken, fullscreenStackBounds,
-                            dockedStackBounds);
+                            dockedStackBounds, navbarColorManagedByIme);
                 }
 
                 @Override
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 2cfdaaa..41f902e 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -31,4 +31,5 @@
     void reportFullscreenMode(boolean fullscreen);
     void reportPreRendered(in EditorInfo info);
     void applyImeVisibility(boolean setVisible);
+    void updateActivityViewToScreenMatrix(int bindSequence, in float[] matrixValues);
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index cb18ca1..c29e823 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -68,4 +68,7 @@
     // This is kept due to @UnsupportedAppUsage.
     // TODO(Bug 113914148): Consider removing this.
     int getInputMethodWindowVisibleHeight();
+
+    void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
+            in float[] matrixValues);
 }
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 9fe49b4..1b133d2 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -19,10 +19,12 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.graphics.Matrix;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -192,13 +194,37 @@
      */
     public final int sequence;
 
+    @Nullable
+    private final float[] mActivityViewToScreenMatrixValues;
+
+    /**
+     * @return {@link Matrix} that corresponds to {@link #mActivityViewToScreenMatrixValues}.
+     *         {@code null} if {@link #mActivityViewToScreenMatrixValues} is {@code null}.
+     */
+    @Nullable
+    public Matrix getActivityViewToScreenMatrix() {
+        if (mActivityViewToScreenMatrixValues == null) {
+            return null;
+        }
+        final Matrix matrix = new Matrix();
+        matrix.setValues(mActivityViewToScreenMatrixValues);
+        return matrix;
+    }
+
     public InputBindResult(@ResultCode int _result,
-            IInputMethodSession _method, InputChannel _channel, String _id, int _sequence) {
+            IInputMethodSession _method, InputChannel _channel, String _id, int _sequence,
+            @Nullable Matrix activityViewToScreenMatrix) {
         result = _result;
         method = _method;
         channel = _channel;
         id = _id;
         sequence = _sequence;
+        if (activityViewToScreenMatrix == null) {
+            mActivityViewToScreenMatrixValues = null;
+        } else {
+            mActivityViewToScreenMatrixValues = new float[9];
+            activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues);
+        }
     }
 
     InputBindResult(Parcel source) {
@@ -211,12 +237,14 @@
         }
         id = source.readString();
         sequence = source.readInt();
+        mActivityViewToScreenMatrixValues = source.createFloatArray();
     }
 
     @Override
     public String toString() {
         return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
                 + " sequence=" + sequence
+                + " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix()
                 + "}";
     }
 
@@ -238,6 +266,7 @@
         }
         dest.writeString(id);
         dest.writeInt(sequence);
+        dest.writeFloatArray(mActivityViewToScreenMatrixValues);
     }
 
     /**
@@ -302,7 +331,7 @@
     }
 
     private static InputBindResult error(@ResultCode int result) {
-        return new InputBindResult(result, null, null, null, -1);
+        return new InputBindResult(result, null, null, null, -1, null);
     }
 
     /**
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 3a25e67..fc2b7f6 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2027,9 +2027,7 @@
 
 static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz,
         jint uid) {
-
-    //###
-    status_t status = NO_ERROR;//AudioSystem::removeUidDeviceAffinities();
+    status_t status = AudioSystem::removeUidDeviceAffinities((uid_t) uid);
     return (jint) nativeToJavaStatus(status);
 }
 
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index cb55618..e5b72ca 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -88,7 +88,7 @@
       mOwnsBuffer(true),
       mHandle(0) {
     if (size > 0) {
-        mBuffer = malloc(size);
+        mBuffer = calloc(size, 1);
     }
 }
 
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index c4c16ee..43c0bbe 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -546,7 +546,7 @@
             if (CC_LIKELY(transform.isPureTranslate())) {
                 // snap/round the computed bounds, so they match the rounding behavior
                 // of the clear done in SurfaceView#draw().
-                bounds.snapToPixelBoundaries();
+                bounds.snapGeometryToPixelBoundaries(false);
             } else {
                 // Conservatively round out so the punched hole (in the ZOrderOnTop = true case)
                 // doesn't extend beyond the other window
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ecc9d64..986771d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -136,8 +136,8 @@
     // ColorSpace.Named.SRGB.ordinal() = 0;
     static constexpr jint SRGB = 0;
 
-    // ColorSpace.Named.DISPLAY_P3.ordinal() = 6;
-    static constexpr jint DISPLAY_P3 = 6;
+    // ColorSpace.Named.DISPLAY_P3.ordinal() = 7;
+    static constexpr jint DISPLAY_P3 = 7;
 };
 
 constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 480b1ea..a3d4798 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -1,13 +1,8 @@
 # Be sure you are familiar with proto when you modify this directory.
 
 # Metrics
-bookatz@google.com
-cjyu@google.com
-jinyithu@google.com
 joeo@google.com
-kwekua@google.com
 singhtejinder@google.com
-yanglu@google.com
 yaochen@google.com
 yro@google.com
 
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 1ff7418..1594402 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2354,4 +2354,14 @@
 
     // OPEN: Settings > Pick preferred SIM dialog
     DIALOG_PREFERRED_SIM_PICKER = 1709;
+
+    // OPEN: Settings > Network & internet > Mobile network > Delete sim
+    DIALOG_DELETE_SIM_CONFIRMATION = 1713;
+
+    // OPEN: Settings >  Network & internet > Mobile network > Delete sim > (answer yes to
+    //       confirmation)
+    DIALOG_DELETE_SIM_PROGRESS = 1714;
+
+    // Settings > Apps and notifications > Notifications > Gentle notifications
+    GENTLE_NOTIFICATIONS_SCREEN = 1715;
 }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index dbd2191..e6ae226 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -159,6 +159,9 @@
     optional int32 surface_size = 14 [deprecated=true];
     optional string focused_app = 15;
     optional AppTransitionProto app_transition = 16;
+    repeated IdentifierProto opening_apps = 17;
+    repeated IdentifierProto closing_apps = 18;
+    repeated IdentifierProto changing_apps = 19;
 }
 
 /* represents DisplayFrames */
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0db7424..0821d14 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -149,4 +149,6 @@
   PROVISIONING_PREPARE_STARTED = 122;
   PROVISIONING_PREPARE_COMPLETED = 123;
   PROVISIONING_FLOW_TYPE = 124;
+  CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES = 125;
+  CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126;
 }
diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
new file mode 100644
index 0000000..34ed90a
--- /dev/null
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.stats.mediametrics;
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp
+ * Next Tag: 10
+ */
+message AudioPolicyData {
+    optional int32 status = 1;
+    optional string request_source = 2;
+    optional string request_package = 3;
+    optional int32 request_session = 4;
+    optional string request_device = 5;
+    optional string active_source = 6;
+    optional string active_package = 7;
+    optional int32 active_session = 8;
+    optional string active_device = 9;
+}
+
+/**
+ * Track properties of audio recording
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioRecord.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp
+ * Next Tag: 16
+ */
+message AudioRecordData {
+    optional string encoding = 1;
+    optional string source = 2;
+    optional int32 latency = 3;
+    optional int32 samplerate = 4;
+    optional int32 channels = 5;
+    optional int64 created_millis = 6;
+    optional int64 duration_millis = 7;
+    optional int32 count = 8;
+    optional int32 error_code = 9;
+    optional string error_function = 10;
+    optional int32 port_id = 11;
+    optional int32 frame_count = 12;
+    optional string attributes = 13;
+    optional int64 channel_mask = 14;
+    optional int64 start_count = 15;
+
+}
+
+/**
+ * Track audio thread performance data
+ * Logged from
+ *   frameworks/av/media/libnblog/ReportPerformance.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiothread.cpp
+ * Next Tag: 28
+ */
+message AudioThreadData {
+    optional string type = 1;
+    optional int32 framecount = 2;
+    optional int32 samplerate = 3;
+    optional string work_millis_hist = 4;
+    optional string latency_millis_hist = 5;
+    optional string warmup_millis_hist = 6;
+    optional int64 underruns = 7;
+    optional int64 overruns = 8;
+    optional int64 active_millis = 9;
+    optional int64 duration_millis = 10;
+
+    optional int32 id = 11;
+    optional int32 port_id = 12;
+    optional int32 sample_rate = 13;
+    optional int64 channel_mask = 14;
+    optional string encoding = 15;
+    optional int32 frame_count = 16;
+    optional string output_device = 17;
+    optional string input_device = 18;
+    optional double io_jitter_mean_millis = 19;
+    optional double io_jitter_stddev_millis = 20;
+    optional double process_time_mean_millis = 21;
+    optional double process_time_stddev_millis = 22;
+    optional double timestamp_jitter_mean_millis = 23;
+    optional double timestamp_jitter_stddev_millis = 24;
+    optional double latency_mean_millis = 25;
+    optional double latency_stddev_millis = 26;
+
+}
+
+/**
+ * Track audio track playback data
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioTrack.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp
+ * Next Tag: 12
+ */
+message AudioTrackData {
+    optional string stream_type = 1;
+    optional string content_type = 2;
+    optional string track_usage = 3;
+    optional int32 sample_rate = 4;
+    optional int64 channel_mask = 5;
+
+    optional int32 underrun_frames = 6;
+    optional int32 startup_glitch = 7;
+
+    optional int32 port_id = 8;
+    optional string encoding = 9;
+    optional int32 frame_count = 10;
+    optional string attributes = 11;
+
+
+}
+
+/**
+ * Track Media Codec usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/MediaCodec.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_codec.cpp
+ * Next Tag: 21
+ */
+message CodecData {
+    optional string codec = 1;
+    optional string mime = 2;
+    optional string mode = 3;
+    optional int32 encoder = 4;
+    optional int32 secure = 5;
+    optional int32 width = 6;
+    optional int32 height = 7;
+    optional int32 rotation = 8;
+    optional int32 crypto = 9;
+    optional int32 profile = 10;
+    optional int32 level = 11;
+    optional int32 max_width = 12;
+    optional int32 max_height = 13;
+    optional int32 error_code = 14;
+    optional string error_state = 15;
+    optional int64 latency_max = 16;
+    optional int64 latency_min = 17;
+    optional int64 latency_avg = 18;
+    optional int64 latency_count = 19;
+    optional int64 latency_unknown = 20;
+}
+
+/**
+ * Track Media Extractor (pulling video/audio streams out of containers) usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_extractor.cpp
+ * Next Tag: 4
+ */
+message ExtractorData {
+    optional string format = 1;
+    optional string mime = 2;
+    optional int32 tracks = 3;
+}
+
+/**
+ * Track Media Player usage
+ * this handles both nuplayer and nuplayer2
+ * Logged from:
+ *   frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+ *   frameworks/av/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp
+ * Next Tag: 21
+ */
+message NuPlayerData {
+    optional string whichPlayer = 1;
+
+    optional string video_mime = 2;
+    optional string video_codec = 3;
+    optional int32 width = 4;
+    optional int32 height = 5;
+    optional int64 frames = 6;
+    optional int64 frames_dropped = 7;
+    optional double framerate = 8;
+    optional string audio_mime = 9;
+    optional string audio_codec = 10;
+    optional int64 duration_millis = 11;
+    optional int64 playing_millis = 12;
+    optional int32 error = 13;
+    optional int32 error_code = 14;
+    optional string error_state = 15;
+    optional string data_source_type = 16;
+    optional int64 rebuffering_millis = 17;
+    optional int32 rebuffers = 18;
+    optional int32 rebuffer_at_exit = 19;
+    optional int64 frames_dropped_startup = 20;
+}
+
+/**
+ * Track information about recordings (e.g. camcorder)
+ * Logged from
+ *   frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
+ *   frameworks/av/services/mediaanalytics/if_statsd.cpp
+ * Next Tag: 22
+ */
+message RecorderData {
+    optional string audio_mime = 1;
+    optional string video_mime = 2;
+    optional int32 video_profile = 3;
+    optional int32 video_level = 4;
+    optional int32 width = 5;
+    optional int32 height = 6;
+    optional int32 rotation = 7;
+    optional int32 framerate = 8;
+    optional int32 capture_fps = 9;
+    optional double capture_fps_enable = 10;
+    optional int64 duration_millis = 11;
+    optional int64 paused_millis = 12;
+    optional int32 paused_count = 13;
+    optional int32 audio_bitrate = 14;
+    optional int32 audio_channels = 15;
+    optional int32 audio_samplerate = 16;
+    optional int32 movie_timescale = 17;
+    optional int32 audio_timescale = 18;
+    optional int32 video_timescale = 19;
+    optional int32 video_bitrate = 20;
+    optional int32 iframe_interval = 21;
+}
diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_0.xml b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml
new file mode 100644
index 0000000..f14cfcf
--- /dev/null
+++ b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="0" android:valueFrom="1" android:valueTo="0.5"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+                    android:startOffset="17" android:valueFrom="0.5"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="250" android:valueFrom="0.5"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+                    android:startOffset="267" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="500" android:valueFrom="1"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+                    android:startOffset="517" android:valueFrom="0.5"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="750" android:valueFrom="0.5"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_1.xml b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml
new file mode 100644
index 0000000..934bd17
--- /dev/null
+++ b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="250"
+                    android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="250" android:valueFrom="1"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+                    android:startOffset="267" android:valueFrom="0.5"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="500" android:valueFrom="0.5"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+                    android:startOffset="517" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="750" android:valueFrom="1"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_2.xml b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml
new file mode 100644
index 0000000..7dce34c
--- /dev/null
+++ b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="translateX" android:duration="1000"
+                    android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_0.xml b/core/res/res/anim/ic_hotspot_transient_animation_0.xml
new file mode 100644
index 0000000..d5a4c52
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_0.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="0" android:valueFrom="1" android:valueTo="0.5"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="633"
+                    android:startOffset="17" android:valueFrom="0.5"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="650" android:valueFrom="0.5"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_1.xml b/core/res/res/anim/ic_hotspot_transient_animation_1.xml
new file mode 100644
index 0000000..36db4b9
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_1.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="200"
+                    android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="200" android:valueFrom="1"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="617"
+                    android:startOffset="217" android:valueFrom="0.5"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="833" android:valueFrom="0.5"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_2.xml b/core/res/res/anim/ic_hotspot_transient_animation_2.xml
new file mode 100644
index 0000000..3d67436
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_2.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="400"
+                    android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="400" android:valueFrom="1"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="617"
+                    android:startOffset="417" android:valueFrom="0.5"
+                    android:valueTo="0.5" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="1033" android:valueFrom="0.5"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_3.xml b/core/res/res/anim/ic_hotspot_transient_animation_3.xml
new file mode 100644
index 0000000..1495dc9
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_3.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="translateX" android:duration="1250"
+                    android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml
new file mode 100644
index 0000000..f71d0ee
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleY" android:duration="0"
+                    android:startOffset="167" android:valueFrom="1" android:valueTo="0"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml
new file mode 100644
index 0000000..9af42a1
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleX" android:duration="0"
+                    android:startOffset="167" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml
new file mode 100644
index 0000000..0e7864a
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleY" android:duration="0"
+                    android:startOffset="333" android:valueFrom="1" android:valueTo="0"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml
new file mode 100644
index 0000000..bbec629
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleX" android:duration="0"
+                    android:startOffset="333" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml
new file mode 100644
index 0000000..2c921b1
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleY" android:duration="0"
+                    android:startOffset="500" android:valueFrom="1" android:valueTo="0"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml
new file mode 100644
index 0000000..51aedc0
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleX" android:duration="0"
+                    android:startOffset="500" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml
new file mode 100644
index 0000000..d1da131
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleY" android:duration="0"
+                    android:startOffset="667" android:valueFrom="1" android:valueTo="0"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml
new file mode 100644
index 0000000..0b6eb42
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="scaleX" android:duration="0"
+                    android:startOffset="667" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml
new file mode 100644
index 0000000..3469d43
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="translateX" android:duration="833"
+                    android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation.xml b/core/res/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..61b0f1a
--- /dev/null
+++ b/core/res/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@drawable/ic_bluetooth_transient_animation_drawable">
+    <target android:name="_R_G_L_0_G_D_1_P_0"
+            android:animation="@anim/ic_bluetooth_transient_animation_0"/>
+    <target android:name="_R_G_L_0_G_D_2_P_0"
+            android:animation="@anim/ic_bluetooth_transient_animation_1"/>
+    <target android:name="time_group" android:animation="@anim/ic_bluetooth_transient_animation_2"/>
+</animated-vector>
diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..a520fea
--- /dev/null
+++ b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+        android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+               android:translateY="12">
+            <group android:name="_R_G_L_0_G" android:translateX="-12"
+                   android:translateY="-12">
+                <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M17.71 7.71 C17.71,7.71 12,2 12,2 C12,2 11,2 11,2 C11,2 11,9.59 11,9.59 C11,9.59 6.41,5 6.41,5 C6.41,5 5,6.41 5,6.41 C5,6.41 10.59,12 10.59,12 C10.59,12 5,17.59 5,17.59 C5,17.59 6.41,19 6.41,19 C6.41,19 11,14.41 11,14.41 C11,14.41 11,22 11,22 C11,22 12,22 12,22 C12,22 17.71,16.29 17.71,16.29 C17.71,16.29 13.41,12 13.41,12 C13.41,12 17.71,7.71 17.71,7.71c  M13 5.83 C13,5.83 14.88,7.71 14.88,7.71 C14.88,7.71 13,9.59 13,9.59 C13,9.59 13,5.83 13,5.83c  M14.88 16.29 C14.88,16.29 13,18.17 13,18.17 C13,18.17 13,14.41 13,14.41 C13,14.41 14.88,16.29 14.88,16.29c "/>
+                <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M5 10.5 C5.83,10.5 6.5,11.17 6.5,12 C6.5,12.83 5.83,13.5 5,13.5 C4.17,13.5 3.5,12.83 3.5,12 C3.5,11.17 4.17,10.5 5,10.5c "/>
+                <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M19 10.5 C19.83,10.5 20.5,11.17 20.5,12 C20.5,12.83 19.83,13.5 19,13.5 C18.17,13.5 17.5,12.83 17.5,12 C17.5,11.17 18.17,10.5 19,10.5c "/>
+            </group>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_hotspot_transient_animation.xml b/core/res/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..efbdfdb
--- /dev/null
+++ b/core/res/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@drawable/ic_hotspot_transient_animation_drawable">
+    <target android:name="_R_G_L_0_G_D_0_P_0"
+            android:animation="@anim/ic_hotspot_transient_animation_0"/>
+    <target android:name="_R_G_L_0_G_D_1_P_0"
+            android:animation="@anim/ic_hotspot_transient_animation_1"/>
+    <target android:name="_R_G_L_0_G_D_2_P_0"
+            android:animation="@anim/ic_hotspot_transient_animation_2"/>
+    <target android:name="time_group"
+            android:animation="@anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..d81abbb
--- /dev/null
+++ b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+               android:translateY="12">
+            <group android:name="_R_G_L_0_G" android:translateX="-10"
+                   android:translateY="-9">
+                <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M10 8 C8.9,8 8,8.9 8,10 C8,10.55 8.23,11.05 8.59,11.41 C8.95,11.77 9.45,12 10,12 C10.55,12 11.05,11.77 11.41,11.41 C11.77,11.05 12,10.55 12,10 C12,8.9 11.1,8 10,8c "/>
+                <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M10 4 C6.69,4 4,6.69 4,10 C4,11.66 4.68,13.15 5.76,14.24 C5.76,14.24 7.18,12.82 7.18,12.82 C6.45,12.1 6,11.11 6,10 C6,7.79 7.79,6 10,6 C12.21,6 14,7.79 14,10 C14,11.11 13.55,12.1 12.82,12.82 C12.82,12.82 14.24,14.24 14.24,14.24 C15.32,13.15 16,11.66 16,10 C16,6.69 13.31,4 10,4c "/>
+                <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M10 0 C4.48,0 0,4.48 0,10 C0,12.76 1.12,15.26 2.93,17.07 C2.93,17.07 4.34,15.66 4.34,15.66 C2.9,14.21 2,12.21 2,10 C2,5.58 5.58,2 10,2 C14.42,2 18,5.58 18,10 C18,12.21 17.1,14.2 15.65,15.65 C15.65,15.65 17.06,17.06 17.06,17.06 C18.88,15.26 20,12.76 20,10 C20,4.48 15.52,0 10,0c "/>
+            </group>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..0219ae5
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ic_signal_wifi_transient_animation_drawable">
+    <target android:name="_R_G_L_4_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_0" />
+    <target android:name="_R_G_L_3_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_1" />
+    <target android:name="_R_G_L_3_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_2" />
+    <target android:name="_R_G_L_2_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_3" />
+    <target android:name="_R_G_L_2_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_4" />
+    <target android:name="_R_G_L_1_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_5" />
+    <target android:name="_R_G_L_1_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_6" />
+    <target android:name="_R_G_L_0_G_N_1_T_0"
+            android:animation="@anim/ic_signal_wifi_transient_animation_7" />
+    <target android:name="time_group"
+            android:animation="@anim/ic_signal_wifi_transient_animation_8" />
+</animated-vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..9cd4925
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="24dp" android:width="24dp" android:viewportHeight="24" android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_4_G_N_1_T_0" android:translateX="12"
+               android:translateY="12">
+            <group android:name="_R_G_L_4_G" android:translateX="-12.25"
+                   android:translateY="-10.25">
+                <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 2.25 C15.67,2.25 18.98,3.52 21.55,5.78 C21.55,5.78 12.25,17.1 12.25,17.1 C12.25,17.1 2.95,5.78 2.95,5.78 C5.52,3.52 8.83,2.25 12.25,2.25c  M12.25 0.25 C7.5,0.25 3.22,2.33 0.25,5.64 C0.25,5.64 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.64 24.25,5.64 C21.28,2.33 17,0.25 12.25,0.25c "/>
+            </group>
+        </group>
+        <group android:name="_R_G_L_3_G_N_1_T_0" android:translateX="12"
+               android:translateY="12">
+            <group android:name="_R_G_L_3_G" android:translateX="-12.25"
+                   android:translateY="-10.25">
+                <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c  M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+                <path android:name="_R_G_L_3_G_D_1_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 11.25 C10.15,11.25 8.15,12.15 6.85,13.65 C6.85,13.65 12.25,20.25 12.25,20.25 C12.25,20.25 17.65,13.65 17.65,13.65 C16.35,12.15 14.35,11.25 12.25,11.25c "/>
+            </group>
+        </group>
+        <group android:name="_R_G_L_2_G_N_1_T_0" android:translateX="12"
+               android:translateY="12">
+            <group android:name="_R_G_L_2_G" android:translateX="-12.25"
+                   android:translateY="-10.25">
+                <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c  M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+                <path android:name="_R_G_L_2_G_D_1_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 8.25 C9.45,8.25 6.75,9.45 5.05,11.45 C5.05,11.45 12.25,20.25 12.25,20.25 C12.25,20.25 19.45,11.45 19.45,11.45 C17.75,9.45 15.05,8.25 12.25,8.25c "/>
+            </group>
+        </group>
+        <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12"
+               android:translateY="12">
+            <group android:name="_R_G_L_1_G" android:translateX="-12.25"
+                   android:translateY="-10.25">
+                <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c  M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+                <path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/>
+            </group>
+        </group>
+        <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+               android:translateY="12">
+            <group android:name="_R_G_L_0_G" android:translateX="-12.25"
+                   android:translateY="-10.25">
+                <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c  M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+                <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/>
+                <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+                      android:fillAlpha="1" android:fillType="nonZero"
+                      android:pathData=" M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+            </group>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/interpolator/transient_interpolator.xml b/core/res/res/interpolator/transient_interpolator.xml
new file mode 100644
index 0000000..653a8cf
--- /dev/null
+++ b/core/res/res/interpolator/transient_interpolator.xml
@@ -0,0 +1,17 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
\ No newline at end of file
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index e889e85..9c725b9 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -23,7 +23,6 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:paddingBottom="@dimen/chooser_view_spacing"
     android:background="?android:attr/colorBackgroundFloating">
 
   <RelativeLayout
@@ -60,7 +59,7 @@
         android:minWidth="48dp"
         android:minHeight="48dp"
         android:clickable="true"
-        android:background="?android:attr/selectableItemBackgroundBorderless">
+        style="?attr/borderlessButtonStyle">
 
       <ImageView
           android:layout_width="24dp"
@@ -91,6 +90,7 @@
       android:orientation="horizontal"
       android:layout_marginLeft="@dimen/chooser_edge_margin_normal"
       android:layout_marginRight="@dimen/chooser_edge_margin_normal"
+      android:layout_marginBottom="@dimen/chooser_view_spacing"
       android:minHeight="80dp"
       android:background="@drawable/chooser_content_preview_rounded"
       android:id="@+id/content_preview_title_layout">
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 256d94e..7098c95 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -23,7 +23,7 @@
               android:minHeight="100dp"
               android:gravity="center"
               android:paddingTop="24dp"
-              android:paddingBottom="8dp"
+              android:paddingBottom="12dp"
               android:paddingLeft="12dp"
               android:paddingRight="12dp"
               android:focusable="true"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 04ccb74..f576fea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1257,6 +1257,8 @@
 
     <bool name="config_use_strict_phone_number_comparation">false</bool>
 
+    <bool name="config_use_strict_phone_number_comparation_for_russian">true</bool>
+
     <!-- Display low battery warning when battery level dips to this value.
          Also, the battery stats are flushed to disk when we hit this level.  -->
     <integer name="config_criticalBatteryWarningLevel">5</integer>
@@ -4006,7 +4008,7 @@
     <string name="config_batterymeterPerimeterPath" translatable="false">
 		M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z
     </string>
-
+    <string name="config_batterymeterErrorPerimeterPath" translatable="false">@string/config_batterymeterPerimeterPath</string>
     <string name="config_batterymeterFillMask" translatable="false">
         M2,18 v-14 h8 v14 z
     </string>
@@ -4057,4 +4059,38 @@
 
     <!-- Which binder services to include in incident reports containing restricted images. -->
     <string-array name="config_restrictedImagesServices" translatable="false"/>
+
+    <!-- Messages that should not be shown to the user during face auth enrollment. This should be
+         used to hide messages that may be too chatty or messages that the user can't do much about.
+         Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
+    <integer-array name="config_face_acquire_enroll_ignorelist" translatable="false" >
+    </integer-array>
+    <!-- Same as the above, but are defined by vendorCodes -->
+    <integer-array name="config_face_acquire_vendor_enroll_ignorelist" translatable="false" >
+    </integer-array>
+
+    <!-- Messages that should not be shown to the user during face authentication, on keyguard.
+         This includes both lockscreen and bouncer. This should be used to hide messages that may be
+         too chatty or messages that the user can't do much about. Entries are defined in
+         android.hardware.biometrics.face@1.0 types.hal -->
+    <integer-array name="config_face_acquire_keyguard_ignorelist" translatable="false" >
+    </integer-array>
+    <!-- Same as the above, but are defined by vendorCodes -->
+    <integer-array name="config_face_acquire_vendor_keyguard_ignorelist" translatable="false" >
+    </integer-array>
+
+    <!-- Messages that should not be shown to the user during face authentication, on
+     BiometricPrompt. This should be used to hide messages that may be too chatty or messages that
+     the user can't do much about. Entries are defined in
+     android.hardware.biometrics.face@1.0 types.hal -->
+    <integer-array name="config_face_acquire_biometricprompt_ignorelist" translatable="false" >
+    </integer-array>
+    <!-- Same as the above, but are defined by vendorCodes -->
+    <integer-array name="config_face_acquire_vendor_biometricprompt_ignorelist" translatable="false" >
+    </integer-array>
+
+    <!-- The component name for the default profile supervisor, which can be set as a profile owner
+    even after user setup is complete. The defined component should be used for supervision purposes
+    only. The component must be part of a system app. -->
+    <string name="config_defaultSupervisionProfileOwnerComponent" translatable="false"></string>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 75fd3a0..dd0a6e60 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1545,9 +1545,9 @@
     <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] -->
     <string name="face_acquired_too_similar">Too similar, please change your pose.</string>
     <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] -->
-    <string name="face_acquired_pan_too_extreme">Please look more directly at the screen.</string>
+    <string name="face_acquired_pan_too_extreme">Turn your head a little less.</string>
     <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] -->
-    <string name="face_acquired_tilt_too_extreme">Please look more directly at the screen.</string>
+    <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string>
     <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] -->
     <string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string>
     <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index b716ab9..b7c86d2 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -68,18 +68,27 @@
     <style name="Widget.DeviceDefault.HorizontalScrollView" parent="Widget.Material.HorizontalScrollView"/>
     <style name="Widget.DeviceDefault.Spinner" parent="Widget.Material.Spinner"/>
     <style name="Widget.DeviceDefault.CompoundButton.Star" parent="Widget.Material.CompoundButton.Star"/>
-    <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget"/>
+    <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TabWidget</item>
+    </style>
     <style name="Widget.DeviceDefault.WebTextView" parent="Widget.Material.WebTextView"/>
     <style name="Widget.DeviceDefault.WebView" parent="Widget.Material.WebView"/>
-    <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem"/>
-    <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.Material.DropDownItem.Spinner"/>
-    <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"/>
+    <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.DropDownItem</item>
+    </style>
+    <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem"/>
+    <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem</item>
+    </style>
     <style name="Widget.DeviceDefault.ListPopupWindow" parent="Widget.Material.ListPopupWindow"/>
     <style name="Widget.DeviceDefault.PopupMenu" parent="Widget.Material.PopupMenu"/>
     <style name="Widget.DeviceDefault.ActionButton" parent="Widget.Material.ActionButton"/>
     <style name="Widget.DeviceDefault.ActionButton.Overflow" parent="Widget.Material.ActionButton.Overflow"/>
     <style name="Widget.DeviceDefault.ActionButton.TextButton" parent="Widget.Material.ActionButton"/>
-    <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode"/>
+    <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode">
+        <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Title</item>
+        <item name="subtitleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle</item>
+    </style>
     <style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode"/>
     <style name="Widget.DeviceDefault.ActionBar" parent="Widget.Material.ActionBar"/>
     <style name="Widget.DeviceDefault.Button.Borderless" parent="Widget.Material.Button.Borderless"/>
@@ -108,7 +117,9 @@
     <style name="Widget.DeviceDefault.AbsListView" parent="Widget.Material.AbsListView"/>
     <style name="Widget.DeviceDefault.Spinner.DropDown.ActionBar" parent="Widget.Material.Spinner.DropDown.ActionBar"/>
     <style name="Widget.DeviceDefault.PopupWindow.ActionMode" parent="Widget.Material.PopupWindow.ActionMode"/>
-    <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch"/>
+    <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch">
+        <item name="switchTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Switch</item>
+    </style>
     <style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Material.ExpandableListView.White"/>
     <style name="Widget.DeviceDefault.FastScroll" parent="Widget.Material.FastScroll"/>
     <!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). -->
@@ -146,6 +157,7 @@
     <style name="Widget.DeviceDefault.TimePicker" parent="Widget.Material.TimePicker"/>
     <style name="Widget.DeviceDefault.Toolbar" parent="Widget.Material.Toolbar">
         <item name="titleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Title</item>
+        <item name="subtitleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle</item>
     </style>
 
     <style name="Widget.DeviceDefault.Light" parent="Widget.Material.Light"/>
@@ -186,12 +198,12 @@
     <style name="Widget.DeviceDefault.Light.HorizontalScrollView" parent="Widget.Material.Light.HorizontalScrollView"/>
     <style name="Widget.DeviceDefault.Light.Spinner" parent="Widget.Material.Light.Spinner"/>
     <style name="Widget.DeviceDefault.Light.CompoundButton.Star" parent="Widget.Material.Light.CompoundButton.Star"/>
-    <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.Material.Light.TabWidget"/>
+    <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.DeviceDefault.TabWidget"/>
     <style name="Widget.DeviceDefault.Light.WebTextView" parent="Widget.Material.Light.WebTextView"/>
     <style name="Widget.DeviceDefault.Light.WebView" parent="Widget.Material.Light.WebView"/>
-    <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.Material.Light.DropDownItem"/>
-    <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.Material.Light.DropDownItem.Spinner"/>
-    <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.Material.Light.TextView.SpinnerItem"/>
+    <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.DeviceDefault.DropDownItem"/>
+    <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem.Spinner"/>
+    <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.DeviceDefault.TextView.SpinnerItem"/>
     <style name="Widget.DeviceDefault.Light.ListPopupWindow" parent="Widget.Material.Light.ListPopupWindow"/>
     <style name="Widget.DeviceDefault.Light.PopupMenu" parent="Widget.Material.Light.PopupMenu"/>
     <style name="Widget.DeviceDefault.Light.Tab" parent="Widget.Material.Light.Tab"/>
@@ -202,12 +214,10 @@
     <style name="Widget.DeviceDefault.Light.ActionMode" parent="Widget.Material.Light.ActionMode"/>
     <style name="Widget.DeviceDefault.Light.ActionButton.CloseMode" parent="Widget.Material.Light.ActionButton.CloseMode"/>
     <style name="Widget.DeviceDefault.Light.ActionBar" parent="Widget.Material.Light.ActionBar"/>
-    <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.Material.Light.ActionBar.TabView"/>
-    <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.Material.Light.ActionBar.TabText"/>
-    <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.Material.Light.ActionBar.TabBar"/>
-    <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Material.Light.ActionBar.Solid">
-        <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionBar.Title</item>
-    </style>
+    <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.DeviceDefault.ActionBar.TabView" />
+    <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.DeviceDefault.ActionBar.TabText" />
+    <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.DeviceDefault.ActionBar.TabBar" />
+    <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.DeviceDefault.ActionBar.Solid" />
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
     <style name="Widget.DeviceDefault.Light.ActionBar.Solid.Inverse" parent="Widget.Holo.Light.ActionBar.Solid.Inverse"/>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
@@ -307,6 +317,9 @@
     <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
+    <style name="TextAppearance.DeviceDefault.Widget.Switch" parent="TextAppearance.Material.Widget.Switch">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored" parent="TextAppearance.DeviceDefault.Widget.Button">
         <item name="textColor">@color/btn_colored_borderless_text_material</item>
     </style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fbe340e..d178383 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -313,6 +313,7 @@
   <java-symbol type="bool" name="config_ui_enableFadingMarquee" />
   <java-symbol type="bool" name="config_enableHapticTextHandle" />
   <java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
+  <java-symbol type="bool" name="config_use_strict_phone_number_comparation_for_russian" />
   <java-symbol type="bool" name="config_single_volume" />
   <java-symbol type="bool" name="config_voice_capable" />
   <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
@@ -1406,6 +1407,9 @@
   <java-symbol type="drawable" name="ic_wifi_signal_2" />
   <java-symbol type="drawable" name="ic_wifi_signal_3" />
   <java-symbol type="drawable" name="ic_wifi_signal_4" />
+  <java-symbol type="drawable" name="ic_signal_wifi_transient_animation" />
+  <java-symbol type="drawable" name="ic_hotspot_transient_animation" />
+  <java-symbol type="drawable" name="ic_bluetooth_transient_animation" />
   <java-symbol type="drawable" name="stat_notify_rssi_in_range" />
   <java-symbol type="drawable" name="stat_sys_gps_on" />
   <java-symbol type="drawable" name="stat_sys_tether_wifi" />
@@ -2570,6 +2574,13 @@
   <java-symbol type="string" name="face_authenticated_no_confirmation_required" />
   <java-symbol type="string" name="face_authenticated_confirmation_required" />
 
+  <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
+  <java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
+  <java-symbol type="array" name="config_face_acquire_keyguard_ignorelist" />
+  <java-symbol type="array" name="config_face_acquire_vendor_keyguard_ignorelist" />
+  <java-symbol type="array" name="config_face_acquire_biometricprompt_ignorelist" />
+  <java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" />
+
   <!-- Face config -->
   <java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
 
@@ -3240,6 +3251,7 @@
 
   <java-symbol type="string" name="config_icon_mask" />
   <java-symbol type="string" name="config_batterymeterPerimeterPath" />
+  <java-symbol type="string" name="config_batterymeterErrorPerimeterPath" />
   <java-symbol type="string" name="config_batterymeterFillMask" />
   <java-symbol type="string" name="config_batterymeterBoltPath" />
   <java-symbol type="string" name="config_batterymeterPowersavePath" />
@@ -3773,4 +3785,6 @@
   <java-symbol type="dimen" name="chooser_direct_share_label_placeholder_max_width" />
   <java-symbol type="layout" name="chooser_az_label_row" />
   <java-symbol type="string" name="chooser_all_apps_button_label" />
+
+  <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" />
 </resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7163769..a92c500 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -70,7 +70,7 @@
     <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" />
 
     <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
-    <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765" />
+    <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075" />
 
     <!-- Chile: 4-5 digits (not confirmed), known premium codes listed -->
     <shortcode country="cl" pattern="\\d{4,5}" free="9963|9240" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 61fb811..e767545 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -695,7 +695,6 @@
                  Settings.Secure.TV_INPUT_CUSTOM_LABELS,
                  Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
                  Settings.Secure.TV_USER_SETUP_COMPLETE,
-                 Settings.Secure.UI_NIGHT_MODE, // candidate?
                  Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
                  Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS,
                  Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED,
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
index 2f17b32..a8ca6f0 100644
--- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -16,7 +16,10 @@
 
 package android.view.autofill;
 
+import static android.view.autofill.AutofillId.NO_SESSION;
+
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.testng.Assert.assertThrows;
 
@@ -33,20 +36,10 @@
     @Test
     public void testNonVirtual() {
         final AutofillId id = new AutofillId(42);
-        assertThat(id.getViewId()).isEqualTo(42);
-        assertThat(id.isNonVirtual()).isTrue();
-        assertThat(id.isVirtualInt()).isFalse();
-        assertThat(id.isVirtualLong()).isFalse();
-        assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
-        assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+        assertNonVirtual(id, 42, NO_SESSION);
 
         final AutofillId clone = cloneThroughParcel(id);
-        assertThat(clone.getViewId()).isEqualTo(42);
-        assertThat(clone.isNonVirtual()).isTrue();
-        assertThat(clone.isVirtualInt()).isFalse();
-        assertThat(clone.isVirtualLong()).isFalse();
-        assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID);
-        assertThat(clone.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+        assertNonVirtual(clone, 42, NO_SESSION);
     }
 
     @Test
@@ -124,49 +117,174 @@
     }
 
     @Test
-    public void testEqualsHashCode() {
-        final AutofillId realId = new AutofillId(42);
-        final AutofillId realIdSame = new AutofillId(42);
-        assertThat(realId).isEqualTo(realIdSame);
-        assertThat(realIdSame).isEqualTo(realId);
-        assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode());
+    public void testFactoryMethod_withoutSession() {
+        final AutofillId id = new AutofillId(42);
+        id.setSessionId(108);
+        assertNonVirtual(id, 42, 108);
+        final AutofillId idWithoutSession = AutofillId.withoutSession(id);
+        assertNonVirtual(idWithoutSession, 42, NO_SESSION);
+    }
 
-        final AutofillId realIdDifferent = new AutofillId(108);
-        assertThat(realId).isNotEqualTo(realIdDifferent);
-        assertThat(realIdDifferent).isNotEqualTo(realId);
+    @Test
+    public void testSetResetSession() {
+        final AutofillId id = new AutofillId(42);
+        assertNonVirtual(id, 42, NO_SESSION);
+        id.setSessionId(108);
+        assertNonVirtual(id, 42, 108);
 
-        final AutofillId virtualId = new AutofillId(42, 1);
-        final AutofillId virtualIdSame = new AutofillId(42, 1);
-        assertThat(virtualId).isEqualTo(virtualIdSame);
-        assertThat(virtualIdSame).isEqualTo(virtualId);
-        assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode());
-        assertThat(virtualId).isNotEqualTo(realId);
-        assertThat(realId).isNotEqualTo(virtualId);
+        final AutofillId clone1 = cloneThroughParcel(id);
+        assertNonVirtual(clone1, 42, 108);
 
-        final AutofillId virtualIdDifferentChild = new AutofillId(42, 2);
-        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId);
-        assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild);
-        assertThat(virtualIdDifferentChild).isNotEqualTo(realId);
-        assertThat(realId).isNotEqualTo(virtualIdDifferentChild);
+        id.resetSessionId();
+        assertThat(id.getSessionId()).isEqualTo(NO_SESSION);
+        final AutofillId clone2 = cloneThroughParcel(id);
+        assertNonVirtual(clone2, 42, NO_SESSION);
+    }
 
-        final AutofillId virtualIdDifferentParent = new AutofillId(108, 1);
-        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId);
-        assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent);
-        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
-        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
+    @Test
+    public void testEqualsHashCode_nonVirtual_same() {
+        final AutofillId id = new AutofillId(42);
+        final AutofillId sameId = new AutofillId(42);
 
-        final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1L, 108);
-        assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
-        assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
-        assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
-        assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
+        assertThat(id).isEqualTo(sameId);
+        assertThat(sameId).isEqualTo(id);
+        assertEqualsIgnoreSession(id, sameId);
+        assertEqualsIgnoreSession(sameId, id);
+        assertThat(id.hashCode()).isEqualTo(sameId.hashCode());
+    }
 
-        final AutofillId sameVirtualIdDifferentSession =
-                new AutofillId(new AutofillId(42), 1L, 108);
-        assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
-        assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
-        assertThat(sameVirtualIdDifferentSession.hashCode())
-                .isEqualTo(virtualIdDifferentSession.hashCode());
+    @Test
+    public void testEqualsHashCode_nonVirtual_other() {
+        final AutofillId id = new AutofillId(42);
+        final AutofillId otherId = new AutofillId(108);
+
+        assertThat(id).isNotEqualTo(otherId);
+        assertThat(otherId).isNotEqualTo(id);
+        assertNotEqualsIgnoreSession(id, otherId);
+        assertNotEqualsIgnoreSession(otherId, id);
+        assertThat(id.hashCode()).isNotEqualTo(otherId.hashCode());
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_same() {
+        final AutofillId id = new AutofillId(42);
+        final AutofillId virtual = new AutofillId(42, 1);
+        final AutofillId sameVirtual = new AutofillId(42, 1);
+
+        assertThat(virtual).isEqualTo(sameVirtual);
+        assertThat(sameVirtual).isEqualTo(virtual);
+        assertEqualsIgnoreSession(virtual, sameVirtual);
+        assertEqualsIgnoreSession(sameVirtual, virtual);
+        assertThat(virtual.hashCode()).isEqualTo(sameVirtual.hashCode());
+        assertThat(virtual).isNotEqualTo(id);
+        assertThat(id).isNotEqualTo(virtual);
+        assertNotEqualsIgnoreSession(id, virtual);
+        assertNotEqualsIgnoreSession(virtual, id);
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_otherChild() {
+        final AutofillId id = new AutofillId(42);
+        final AutofillId virtual = new AutofillId(42, 1);
+        final AutofillId virtualOtherChild = new AutofillId(42, 2);
+
+        assertThat(virtualOtherChild).isNotEqualTo(virtual);
+        assertThat(virtual).isNotEqualTo(virtualOtherChild);
+        assertNotEqualsIgnoreSession(virtualOtherChild, virtual);
+        assertNotEqualsIgnoreSession(virtual, virtualOtherChild);
+        assertThat(virtualOtherChild).isNotEqualTo(id);
+        assertThat(id).isNotEqualTo(virtualOtherChild);
+        assertNotEqualsIgnoreSession(virtualOtherChild, id);
+        assertNotEqualsIgnoreSession(id, virtualOtherChild);
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_otherParent() {
+        final AutofillId virtual = new AutofillId(42, 1);
+        final AutofillId virtualOtherParent = new AutofillId(108, 1);
+        final AutofillId virtualOtherChild = new AutofillId(42, 2);
+
+        assertThat(virtualOtherParent).isNotEqualTo(virtual);
+        assertThat(virtual).isNotEqualTo(virtualOtherParent);
+        assertNotEqualsIgnoreSession(virtualOtherParent, virtual);
+        assertNotEqualsIgnoreSession(virtual, virtualOtherParent);
+        assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild);
+        assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent);
+        assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild);
+        assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent);
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_otherSession() {
+        final AutofillId virtual = new AutofillId(42, 1);
+        final AutofillId virtualOtherSession = new AutofillId(42, 1);
+        virtualOtherSession.setSessionId(666);
+
+        assertThat(virtualOtherSession).isNotEqualTo(virtual);
+        assertThat(virtual).isNotEqualTo(virtualOtherSession);
+        assertEqualsIgnoreSession(virtualOtherSession, virtual);
+        assertEqualsIgnoreSession(virtual, virtualOtherSession);
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_longId_same() {
+        final AutofillId hostId = new AutofillId(42);
+        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+        final AutofillId sameVirtual = new AutofillId(hostId, 1L, 108);
+
+        assertThat(sameVirtual).isEqualTo(virtual);
+        assertThat(virtual).isEqualTo(sameVirtual);
+        assertEqualsIgnoreSession(sameVirtual, virtual);
+        assertEqualsIgnoreSession(virtual, sameVirtual);
+        assertThat(sameVirtual).isNotEqualTo(hostId);
+        assertThat(hostId).isNotEqualTo(sameVirtual);
+        assertNotEqualsIgnoreSession(sameVirtual, hostId);
+        assertNotEqualsIgnoreSession(hostId, sameVirtual);
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_longId_otherChild() {
+        final AutofillId hostId = new AutofillId(42);
+        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+        final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108);
+
+        assertThat(virtualOtherChild).isNotEqualTo(virtual);
+        assertThat(virtual).isNotEqualTo(virtualOtherChild);
+        assertNotEqualsIgnoreSession(virtualOtherChild, virtual);
+        assertNotEqualsIgnoreSession(virtual, virtualOtherChild);
+        assertThat(virtualOtherChild).isNotEqualTo(hostId);
+        assertThat(hostId).isNotEqualTo(virtualOtherChild);
+        assertNotEqualsIgnoreSession(virtualOtherChild, hostId);
+        assertNotEqualsIgnoreSession(hostId, virtualOtherChild);
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_longId_otherParent() {
+        final AutofillId hostId = new AutofillId(42);
+        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+        final AutofillId virtualOtherParent = new AutofillId(new AutofillId(666), 1L, 108);
+        final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108);
+
+        assertThat(virtualOtherParent).isNotEqualTo(virtual);
+        assertThat(virtual).isNotEqualTo(virtualOtherParent);
+        assertNotEqualsIgnoreSession(virtualOtherParent, virtual);
+        assertNotEqualsIgnoreSession(virtual, virtualOtherParent);
+        assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild);
+        assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent);
+        assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild);
+        assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent);
+    }
+
+    @Test
+    public void testEqualsHashCode_virtual_longId_otherSession() {
+        final AutofillId hostId = new AutofillId(42);
+        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+        final AutofillId virtualOtherSession = new AutofillId(hostId, 1L, 666);
+
+        assertThat(virtualOtherSession).isNotEqualTo(virtual);
+        assertThat(virtual).isNotEqualTo(virtualOtherSession);
+        assertEqualsIgnoreSession(virtualOtherSession, virtual);
+        assertEqualsIgnoreSession(virtual, virtualOtherSession);
     }
 
     private AutofillId cloneThroughParcel(AutofillId id) {
@@ -186,4 +304,28 @@
             parcel.recycle();
         }
     }
+
+    private void assertEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
+        assertWithMessage("id1 is null").that(id1).isNotNull();
+        assertWithMessage("id2 is null").that(id2).isNotNull();
+        assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2))
+                .isTrue();
+    }
+
+    private void assertNotEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
+        assertWithMessage("id1 is null").that(id1).isNotNull();
+        assertWithMessage("id2 is null").that(id2).isNotNull();
+        assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2))
+                .isFalse();
+    }
+
+    private void assertNonVirtual(AutofillId id, int expectedId, int expectSessionId) {
+        assertThat(id.getViewId()).isEqualTo(expectedId);
+        assertThat(id.isNonVirtual()).isTrue();
+        assertThat(id.isVirtualInt()).isFalse();
+        assertThat(id.isVirtualLong()).isFalse();
+        assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+        assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+        assertThat(id.getSessionId()).isEqualTo(expectSessionId);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
index ace6611..833530d 100644
--- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
@@ -16,19 +16,9 @@
 
 package android.view.inputmethod;
 
-import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
-import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
-import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 
 import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.os.Parcel;
-import android.text.TextUtils;
 import android.view.inputmethod.CursorAnchorInfo.Builder;
 
 import androidx.test.filters.SmallTest;
@@ -37,437 +27,38 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Objects;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class CursorAnchorInfoTest {
-    private static final float EPSILON = 0.0000001f;
-
-    private static final RectF[] MANY_BOUNDS = new RectF[] {
-            new RectF(101.0f, 201.0f, 301.0f, 401.0f),
-            new RectF(102.0f, 202.0f, 302.0f, 402.0f),
-            new RectF(103.0f, 203.0f, 303.0f, 403.0f),
-            new RectF(104.0f, 204.0f, 304.0f, 404.0f),
-            new RectF(105.0f, 205.0f, 305.0f, 405.0f),
-            new RectF(106.0f, 206.0f, 306.0f, 406.0f),
-            new RectF(107.0f, 207.0f, 307.0f, 407.0f),
-            new RectF(108.0f, 208.0f, 308.0f, 408.0f),
-            new RectF(109.0f, 209.0f, 309.0f, 409.0f),
-            new RectF(110.0f, 210.0f, 310.0f, 410.0f),
-            new RectF(111.0f, 211.0f, 311.0f, 411.0f),
-            new RectF(112.0f, 212.0f, 312.0f, 412.0f),
-            new RectF(113.0f, 213.0f, 313.0f, 413.0f),
-            new RectF(114.0f, 214.0f, 314.0f, 414.0f),
-            new RectF(115.0f, 215.0f, 315.0f, 415.0f),
-            new RectF(116.0f, 216.0f, 316.0f, 416.0f),
-            new RectF(117.0f, 217.0f, 317.0f, 417.0f),
-            new RectF(118.0f, 218.0f, 318.0f, 418.0f),
-            new RectF(119.0f, 219.0f, 319.0f, 419.0f),
-    };
-    private static final int[] MANY_FLAGS_ARRAY = new int[] {
-        FLAG_HAS_INVISIBLE_REGION,
-        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
-        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
-        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
-        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
-        FLAG_HAS_VISIBLE_REGION,
-        FLAG_HAS_INVISIBLE_REGION,
-        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
-    };
-
     @Test
-    public void testBuilder() throws Exception {
-        final int SELECTION_START = 30;
-        final int SELECTION_END = 40;
-        final int COMPOSING_TEXT_START = 32;
-        final String COMPOSING_TEXT = "test";
-        final int INSERTION_MARKER_FLAGS =
-                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
-        final float INSERTION_MARKER_HORIZONTAL = 10.5f;
-        final float INSERTION_MARKER_TOP = 100.1f;
-        final float INSERTION_MARKER_BASELINE = 110.4f;
-        final float INSERTION_MARKER_BOTOM = 111.0f;
-
-        Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
-        TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
-
+    public void testCreateForAdditionalParentMatrix() {
+        final Matrix originalMatrix = new Matrix();
+        originalMatrix.setTranslate(10.0f, 20.0f);
         final Builder builder = new Builder();
-        builder.setSelectionRange(SELECTION_START, SELECTION_END)
-                .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT)
-                .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
-                        INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS)
-                .setMatrix(TRANSFORM_MATRIX);
-        for (int i = 0; i < MANY_BOUNDS.length; i++) {
-            final RectF bounds = MANY_BOUNDS[i];
-            final int flags = MANY_FLAGS_ARRAY[i];
-            builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
-                    flags);
-        }
+        builder.setMatrix(originalMatrix);
 
-        final CursorAnchorInfo info = builder.build();
-        assertEquals(SELECTION_START, info.getSelectionStart());
-        assertEquals(SELECTION_END, info.getSelectionEnd());
-        assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart());
-        assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText()));
-        assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags());
-        assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal(), EPSILON);
-        assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop(), EPSILON);
-        assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline(), EPSILON);
-        assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom(), EPSILON);
-        assertEquals(TRANSFORM_MATRIX, info.getMatrix());
-        for (int i = 0; i < MANY_BOUNDS.length; i++) {
-            final RectF expectedBounds = MANY_BOUNDS[i];
-            assertEquals(expectedBounds, info.getCharacterBounds(i));
-        }
-        assertNull(info.getCharacterBounds(-1));
-        assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1));
-        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
-            final int expectedFlags = MANY_FLAGS_ARRAY[i];
-            assertEquals(expectedFlags, info.getCharacterBoundsFlags(i));
-        }
-        assertEquals(0, info.getCharacterBoundsFlags(-1));
-        assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+        final CursorAnchorInfo originalInstance = builder.build();
 
-        // Make sure that the builder can reproduce the same object.
-        final CursorAnchorInfo info2 = builder.build();
-        assertEquals(SELECTION_START, info2.getSelectionStart());
-        assertEquals(SELECTION_END, info2.getSelectionEnd());
-        assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart());
-        assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText()));
-        assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags());
-        assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal(), EPSILON);
-        assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop(), EPSILON);
-        assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline(), EPSILON);
-        assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom(), EPSILON);
-        assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
-        for (int i = 0; i < MANY_BOUNDS.length; i++) {
-            final RectF expectedBounds = MANY_BOUNDS[i];
-            assertEquals(expectedBounds, info2.getCharacterBounds(i));
-        }
-        assertNull(info2.getCharacterBounds(-1));
-        assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1));
-        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
-            final int expectedFlags = MANY_FLAGS_ARRAY[i];
-            assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i));
-        }
-        assertEquals(0, info2.getCharacterBoundsFlags(-1));
-        assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
-        assertEquals(info, info2);
-        assertEquals(info.hashCode(), info2.hashCode());
+        assertEquals(originalMatrix, originalInstance.getMatrix());
 
-        // Make sure that object can be marshaled via {@link Parsel}.
-        final CursorAnchorInfo info3 = cloneViaParcel(info2);
-        assertEquals(SELECTION_START, info3.getSelectionStart());
-        assertEquals(SELECTION_END, info3.getSelectionEnd());
-        assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart());
-        assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText()));
-        assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags());
-        assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal(), EPSILON);
-        assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop(), EPSILON);
-        assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline(), EPSILON);
-        assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom(), EPSILON);
-        assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
-        for (int i = 0; i < MANY_BOUNDS.length; i++) {
-            final RectF expectedBounds = MANY_BOUNDS[i];
-            assertEquals(expectedBounds, info3.getCharacterBounds(i));
-        }
-        assertNull(info3.getCharacterBounds(-1));
-        assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1));
-        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
-            final int expectedFlags = MANY_FLAGS_ARRAY[i];
-            assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i));
-        }
-        assertEquals(0, info3.getCharacterBoundsFlags(-1));
-        assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
-        assertEquals(info.hashCode(), info3.hashCode());
+        final Matrix additionalParentMatrix = new Matrix();
+        additionalParentMatrix.setTranslate(1.0f, 2.0f);
+
+        final Matrix newMatrix = new Matrix(originalMatrix);
+        newMatrix.postConcat(additionalParentMatrix);
 
         builder.reset();
-        final CursorAnchorInfo uninitializedInfo = builder.build();
-        assertEquals(-1, uninitializedInfo.getSelectionStart());
-        assertEquals(-1, uninitializedInfo.getSelectionEnd());
-        assertEquals(-1, uninitializedInfo.getComposingTextStart());
-        assertNull(uninitializedInfo.getComposingText());
-        assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
-        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON);
-        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON);
-        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON);
-        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON);
-        assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
-    }
+        builder.setMatrix(newMatrix);
+        // An instance created by the standard Builder class.
+        final CursorAnchorInfo newInstanceByBuilder = builder.build();
 
-    private static void assertNotEquals(final CursorAnchorInfo reference,
-            final CursorAnchorInfo actual) {
-        assertFalse(Objects.equals(reference, actual));
-    }
+        // An instance created by an @hide method.
+        final CursorAnchorInfo newInstanceByMethod =
+                CursorAnchorInfo.createForAdditionalParentMatrix(
+                        originalInstance, additionalParentMatrix);
 
-    @Test
-    public void testEquality() throws Exception {
-        final Matrix MATRIX1 = new Matrix();
-        MATRIX1.setTranslate(10.0f, 20.0f);
-        final Matrix MATRIX2 = new Matrix();
-        MATRIX2.setTranslate(110.0f, 120.0f);
-        final Matrix NAN_MATRIX = new Matrix();
-        NAN_MATRIX.setValues(new float[]{
-                Float.NaN, Float.NaN, Float.NaN,
-                Float.NaN, Float.NaN, Float.NaN,
-                Float.NaN, Float.NaN, Float.NaN});
-        final int SELECTION_START1 = 2;
-        final int SELECTION_END1 = 7;
-        final String COMPOSING_TEXT1 = "0123456789";
-        final int COMPOSING_TEXT_START1 = 0;
-        final int INSERTION_MARKER_FLAGS1 = FLAG_HAS_VISIBLE_REGION;
-        final float INSERTION_MARKER_HORIZONTAL1 = 10.5f;
-        final float INSERTION_MARKER_TOP1 = 100.1f;
-        final float INSERTION_MARKER_BASELINE1 = 110.4f;
-        final float INSERTION_MARKER_BOTOM1 = 111.0f;
-        final int SELECTION_START2 = 4;
-        final int SELECTION_END2 = 8;
-        final String COMPOSING_TEXT2 = "9876543210";
-        final int COMPOSING_TEXT_START2 = 3;
-        final int INSERTION_MARKER_FLAGS2 =
-                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
-        final float INSERTION_MARKER_HORIZONTAL2 = 14.5f;
-        final float INSERTION_MARKER_TOP2 = 200.1f;
-        final float INSERTION_MARKER_BASELINE2 = 210.4f;
-        final float INSERTION_MARKER_BOTOM2 = 211.0f;
-
-        // Default instance should be equal.
-        assertEquals(new Builder().build(), new Builder().build());
-
-        assertEquals(
-                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
-                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build());
-        assertNotEquals(
-                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
-                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END2).build());
-        assertNotEquals(
-                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
-                new Builder().setSelectionRange(SELECTION_START2, SELECTION_END1).build());
-        assertNotEquals(
-                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
-                new Builder().setSelectionRange(SELECTION_START2, SELECTION_END2).build());
-        assertEquals(
-                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
-                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build());
-        assertNotEquals(
-                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
-                new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT1).build());
-        assertNotEquals(
-                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
-                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT2).build());
-        assertNotEquals(
-                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
-                new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT2).build());
-
-        // For insertion marker locations, {@link Float#NaN} is treated as if it was a number.
-        assertEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        Float.NaN, Float.NaN, Float.NaN, Float.NaN,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        Float.NaN, Float.NaN, Float.NaN, Float.NaN,
-                        INSERTION_MARKER_FLAGS1).build());
-
-        // Check Matrix.
-        assertEquals(
-                new Builder().setMatrix(MATRIX1).build(),
-                new Builder().setMatrix(MATRIX1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).build(),
-                new Builder().setMatrix(MATRIX2).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).build(),
-                new Builder().setMatrix(NAN_MATRIX).build());
-        // Unlike insertion marker locations, {@link Float#NaN} in the matrix is treated as just a
-        // NaN as usual (NaN == NaN -> false).
-        assertNotEquals(
-                new Builder().setMatrix(NAN_MATRIX).build(),
-                new Builder().setMatrix(NAN_MATRIX).build());
-
-        assertEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        Float.NaN, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP2,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE2, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM2,
-                        INSERTION_MARKER_FLAGS1).build());
-        assertNotEquals(
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS1).build(),
-                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
-                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
-                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_FLAGS2).build());
-    }
-
-    @Test
-    public void testMatrixIsCopied() throws Exception {
-        final Matrix MATRIX1 = new Matrix();
-        MATRIX1.setTranslate(10.0f, 20.0f);
-        final Matrix MATRIX2 = new Matrix();
-        MATRIX2.setTranslate(110.0f, 120.0f);
-        final Matrix MATRIX3 = new Matrix();
-        MATRIX3.setTranslate(210.0f, 220.0f);
-        final Matrix matrix = new Matrix();
-        final Builder builder = new Builder();
-
-        matrix.set(MATRIX1);
-        builder.setMatrix(matrix);
-        matrix.postRotate(90.0f);
-
-        final CursorAnchorInfo firstInstance = builder.build();
-        assertEquals(MATRIX1, firstInstance.getMatrix());
-        matrix.set(MATRIX2);
-        builder.setMatrix(matrix);
-        final CursorAnchorInfo secondInstance = builder.build();
-        assertEquals(MATRIX1, firstInstance.getMatrix());
-        assertEquals(MATRIX2, secondInstance.getMatrix());
-
-        matrix.set(MATRIX3);
-        assertEquals(MATRIX1, firstInstance.getMatrix());
-        assertEquals(MATRIX2, secondInstance.getMatrix());
-    }
-
-    @Test
-    public void testMatrixIsRequired() throws Exception {
-        final int SELECTION_START = 30;
-        final int SELECTION_END = 40;
-        final int COMPOSING_TEXT_START = 32;
-        final String COMPOSING_TEXT = "test";
-        final int INSERTION_MARKER_FLAGS = FLAG_HAS_VISIBLE_REGION;
-        final float INSERTION_MARKER_HORIZONTAL = 10.5f;
-        final float INSERTION_MARKER_TOP = 100.1f;
-        final float INSERTION_MARKER_BASELINE = 110.4f;
-        final float INSERTION_MARKER_BOTOM = 111.0f;
-        Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
-        TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
-
-        final Builder builder = new Builder();
-        // Check twice to make sure if Builder#reset() works as expected.
-        for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
-            builder.setSelectionRange(SELECTION_START, SELECTION_END)
-                    .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT);
-            try {
-                // Should succeed as coordinate transformation matrix is not required if no
-                // positional information is specified.
-                builder.build();
-            } catch (IllegalArgumentException ex) {
-                assertTrue(false);
-            }
-
-            builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
-                    INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS);
-            try {
-                // Coordinate transformation matrix is required if no positional information is
-                // specified.
-                builder.build();
-                assertTrue(false);
-            } catch (IllegalArgumentException ex) {
-            }
-
-            builder.setMatrix(TRANSFORM_MATRIX);
-            try {
-                // Should succeed as coordinate transformation matrix is required.
-                builder.build();
-            } catch (IllegalArgumentException ex) {
-                assertTrue(false);
-            }
-
-            builder.reset();
-        }
-    }
-
-    @Test
-    public void testBuilderAddCharacterBounds() throws Exception {
-        // A negative index should be rejected.
-        try {
-            new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
-            assertTrue(false);
-        } catch (IllegalArgumentException ex) {
-        }
-    }
-
-    private static CursorAnchorInfo cloneViaParcel(final CursorAnchorInfo src) {
-        Parcel parcel = null;
-        try {
-            parcel = Parcel.obtain();
-            src.writeToParcel(parcel, 0);
-            parcel.setDataPosition(0);
-            return new CursorAnchorInfo(parcel);
-        } finally {
-            if (parcel != null) {
-                parcel.recycle();
-            }
-        }
+        assertEquals(newMatrix, newInstanceByBuilder.getMatrix());
+        assertEquals(newMatrix, newInstanceByMethod.getMatrix());
+        assertEquals(newInstanceByBuilder.hashCode(), newInstanceByMethod.hashCode());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
index f1cfe24..d54ce51 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
@@ -26,16 +26,17 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.IOException;
+import java.util.function.Supplier;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ConfigParserTest {
-    private static final String SETTINGS = "int=42,float=12.3,boolean=true,string=abc";
+    private static final Supplier<String> SETTINGS =
+            () -> "int=42,float=12.3,boolean=true,string=abc";
     private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
             "device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER;
     private static final String[] DEVICE_CONFIG_KEYS = new String[]{
@@ -59,7 +60,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getBoolean_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -79,7 +79,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getInt_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -97,7 +96,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getFloat_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -115,7 +113,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getString_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index f6bb1bf..789b829 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -50,9 +50,11 @@
                 + "in_app_conversation_action_types_default=text_reply,"
                 + "notification_conversation_action_types_default=send_email:call_phone,"
                 + "lang_id_threshold_override=0.3,"
-                + "lang_id_context_settings=10:1:0.5";
-        final TextClassificationConstants constants =
-                TextClassificationConstants.loadFromString(s);
+                + "lang_id_context_settings=10:1:0.5,"
+                + "detect_language_from_text_enabled=true,"
+                + "template_intent_factory_enabled=true,"
+                + "translate_in_classification_enabled=true";
+        final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
 
         assertWithMessage("local_textclassifier_enabled")
                 .that(constants.isLocalTextClassifierEnabled()).isTrue();
@@ -95,6 +97,12 @@
                 .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f);
         Assert.assertArrayEquals("lang_id_context_settings",
                 constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON);
+        assertWithMessage("detect_language_from_text_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("template_intent_factory_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("translate_in_classification_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
     }
 
     @Test
@@ -116,9 +124,11 @@
                 + "in_app_conversation_action_types_default=view_map:track_flight,"
                 + "notification_conversation_action_types_default=share_location,"
                 + "lang_id_threshold_override=2,"
-                + "lang_id_context_settings=30:0.5:0.3";
-        final TextClassificationConstants constants =
-                TextClassificationConstants.loadFromString(s);
+                + "lang_id_context_settings=30:0.5:0.3,"
+                + "detect_language_from_text_enabled=false,"
+                + "template_intent_factory_enabled=false,"
+                + "translate_in_classification_enabled=false";
+        final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
 
         assertWithMessage("local_textclassifier_enabled")
                 .that(constants.isLocalTextClassifierEnabled()).isFalse();
@@ -161,12 +171,17 @@
                 .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
         Assert.assertArrayEquals("lang_id_context_settings",
                 constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON);
+        assertWithMessage("detect_language_from_text_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isFalse();
+        assertWithMessage("template_intent_factory_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isFalse();
+        assertWithMessage("translate_in_classification_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isFalse();
     }
 
     @Test
     public void testLoadFromString_defaultValues() {
-        final TextClassificationConstants constants =
-                TextClassificationConstants.loadFromString("");
+        final TextClassificationConstants constants = new TextClassificationConstants(() -> "");
 
         assertWithMessage("local_textclassifier_enabled")
                 .that(constants.isLocalTextClassifierEnabled()).isTrue();
@@ -213,5 +228,11 @@
                 .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f);
         Assert.assertArrayEquals("lang_id_context_settings",
                 constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON);
+        assertWithMessage("detect_language_from_text_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("template_intent_factory_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("translate_in_classification_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 4fcd51c..9148185 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -77,7 +77,7 @@
 
         TextClassifier fallback = TextClassifier.NO_OP;
         TextClassifier classifier = new TextClassifierImpl(
-                fakeContext, TextClassificationConstants.loadFromString(null), fallback);
+                fakeContext, new TextClassificationConstants(() -> null), fallback);
 
         String text = "Contact me at +12122537077";
         String classifiedText = "+12122537077";
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index aeb8949..e3eb2a3 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -59,7 +59,7 @@
     // TODO: Implement TextClassifierService testing.
 
     private static final TextClassificationConstants TC_CONSTANTS =
-            TextClassificationConstants.loadFromString("");
+            new TextClassificationConstants(() -> "");
     private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
     private static final String NO_TYPE = null;
 
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 3cb1e18..b93c3a7 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -57,7 +57,8 @@
                 0x80 /* dockedStackSysUiVisibility */,
                 new Binder() /* imeToken */,
                 new Rect(0x100, 0x200, 0x400, 0x800) /* fullscreenStackBounds */,
-                new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */);
+                new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */,
+                true /* navbarColorManagedByIme */);
 
         final RegisterStatusBarResult copy = clone(original);
 
@@ -80,6 +81,7 @@
         assertThat(copy.mImeToken).isSameAs(original.mImeToken);
         assertThat(copy.mFullscreenStackBounds).isEqualTo(original.mFullscreenStackBounds);
         assertThat(copy.mDockedStackBounds).isEqualTo(original.mDockedStackBounds);
+        assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
     }
 
     private RegisterStatusBarResult clone(RegisterStatusBarResult original) {
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
new file mode 100644
index 0000000..390bb76
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.app.UiAutomation;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+
+class LocalOverlayManager {
+    private static final long TIMEOUT = 30;
+
+    public static void setEnabledAndWait(Executor executor, final String packageName,
+            boolean enable) throws Exception {
+        final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
+        if (executeShellCommand("cmd overlay list").contains(pattern)) {
+            // nothing to do, overlay already in the requested state
+            return;
+        }
+
+        final Resources res = InstrumentationRegistry.getContext().getResources();
+        final String[] oldApkPaths = res.getAssets().getApkPaths();
+        FutureTask<Boolean> task = new FutureTask<>(() -> {
+            while (true) {
+                if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
+                    return true;
+                }
+                Thread.sleep(10);
+            }
+        });
+        executor.execute(task);
+        executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
+        task.get(TIMEOUT, SECONDS);
+    }
+
+    private static String executeShellCommand(final String command)
+            throws Exception {
+        final UiAutomation uiAutomation =
+                InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
+        try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            final BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(in, StandardCharsets.UTF_8));
+            StringBuilder str = new StringBuilder();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                str.append(line);
+            }
+            return str.toString();
+        }
+    }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index f86743a..fdb6bbb 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -21,13 +21,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.app.UiAutomation;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
 import android.util.AttributeSet;
 import android.util.Xml;
 
@@ -569,60 +567,4 @@
         setLocale(new Locale("sv", "SE"));
         assertResource(resId, 200, 400, 600);
     }
-
-    /**
-     * Executes the shell command and reads all the output to ensure the command ran and didn't
-     * get stuck buffering on output.
-     */
-    protected static String executeShellCommand(UiAutomation automation, String command)
-            throws Exception {
-        final ParcelFileDescriptor pfd = automation.executeShellCommand(command);
-        try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
-            final BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(in, StandardCharsets.UTF_8));
-            StringBuilder str = new StringBuilder();
-            String line;
-            while ((line = reader.readLine()) != null) {
-                str.append(line);
-            }
-            return str.toString();
-        }
-    }
-
-    /**
-     * Enables overlay packages and waits for a configuration change event before
-     * returning, to guarantee that Resources are up-to-date.
-     * @param packages the list of package names to enable.
-     */
-    protected static void enableOverlayPackages(String... packages) throws Exception {
-        enableOverlayPackages(true, packages);
-    }
-
-    /**
-     * Disables overlay packages and waits for a configuration change event before
-     * returning, to guarantee that Resources are up-to-date.
-     * @param packages the list of package names to disable.
-     */
-    protected static void disableOverlayPackages(String... packages) throws Exception {
-        enableOverlayPackages(false, packages);
-    }
-
-    /**
-     * Enables/disables overlay packages and waits for a configuration change event before
-     * returning, to guarantee that Resources are up-to-date.
-     * @param enable enables the overlays when true, disables when false.
-     * @param packages the list of package names to enable/disable.
-     */
-    private static void enableOverlayPackages(boolean enable, String[] packages)
-            throws Exception {
-        final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation()
-                .getUiAutomation();
-        for (final String pkg : packages) {
-            executeShellCommand(uiAutomation,
-                    "cmd overlay " + (enable ? "enable " : "disable ") + pkg);
-        }
-
-        // Wait for the overlay change to propagate.
-        Thread.sleep(1000);
-    }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index cd3ed9d..d28c47d 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.Executor;
+
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -31,6 +33,9 @@
 
     @BeforeClass
     public static void enableOverlay() throws Exception {
-        enableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+        Executor executor = (cmd) -> new Thread(cmd).start();
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
+        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index c0d4281..6566ad3 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.Executor;
+
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithOverlayTest extends OverlayBaseTest {
@@ -31,7 +33,9 @@
 
     @BeforeClass
     public static void enableOverlay() throws Exception {
-        disableOverlayPackages(APP_OVERLAY_TWO_PKG);
-        enableOverlayPackages(APP_OVERLAY_ONE_PKG, FRAMEWORK_OVERLAY_PKG);
+        Executor executor = (cmd) -> new Thread(cmd).start();
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 33c7b25..48cfeab 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.Executor;
+
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithoutOverlayTest extends OverlayBaseTest {
@@ -31,6 +33,9 @@
 
     @BeforeClass
     public static void disableOverlays() throws Exception {
-        disableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+        Executor executor = (cmd) -> new Thread(cmd).start();
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
     }
 }
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
new file mode 100644
index 0000000..37020fc
--- /dev/null
+++ b/data/etc/car/Android.bp
@@ -0,0 +1,123 @@
+// Copyright (C} 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License"};
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+
+// Privapp permission whitelist files
+
+prebuilt_etc {
+    name: "privapp_whitelist_android.car.cluster.loggingrenderer",
+    sub_dir: "permissions",
+    src: "android.car.cluster.loggingrenderer.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_android.car.cluster.sample",
+    sub_dir: "permissions",
+    src: "android.car.cluster.sample.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_android.car.usb.handler",
+    sub_dir: "permissions",
+    src: "android.car.usb.handler.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.carlauncher",
+    sub_dir: "permissions",
+    src: "com.android.car.carlauncher.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.dialer",
+    sub_dir: "permissions",
+    src: "com.android.car.dialer.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.hvac",
+    sub_dir: "permissions",
+    src: "com.android.car.hvac.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.media",
+    sub_dir: "permissions",
+    src: "com.android.car.media.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.notification",
+    sub_dir: "permissions",
+    src: "com.android.car.notification.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.radio",
+    sub_dir: "permissions",
+    src: "com.android.car.radio.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.settings",
+    sub_dir: "permissions",
+    src: "com.android.car.settings.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.themeplayground",
+    sub_dir: "permissions",
+    src: "com.android.car.themeplayground.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.trust",
+    sub_dir: "permissions",
+    src: "com.android.car.trust.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car",
+    sub_dir: "permissions",
+    src: "com.android.car.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.google.android.car.kitchensink",
+    sub_dir: "permissions",
+    src: "com.google.android.car.kitchensink.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.car.developeroptions",
+    sub_dir: "permissions",
+    src: "com.android.car.developeroptions.xml",
+    filename_from_src: true,
+    product_specific: true,
+}
diff --git a/data/etc/car/android.car.cluster.loggingrenderer.xml b/data/etc/car/android.car.cluster.loggingrenderer.xml
new file mode 100644
index 0000000..784e0e7
--- /dev/null
+++ b/data/etc/car/android.car.cluster.loggingrenderer.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="android.car.cluster.loggingrenderer">
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/android.car.cluster.sample.xml b/data/etc/car/android.car.cluster.sample.xml
new file mode 100644
index 0000000..75c57b8
--- /dev/null
+++ b/data/etc/car/android.car.cluster.sample.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="android.car.cluster.sample">
+        <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/android.car.usb.handler.xml b/data/etc/car/android.car.usb.handler.xml
new file mode 100644
index 0000000..c67847c
--- /dev/null
+++ b/data/etc/car/android.car.usb.handler.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="android.car.usb.handler">
+        <permission name="android.permission.MANAGE_USB"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
new file mode 100644
index 0000000..8ec1cd4
--- /dev/null
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.carlauncher">
+        <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml
new file mode 100644
index 0000000..76c8c62
--- /dev/null
+++ b/data/etc/car/com.android.car.developeroptions.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.developeroptions">
+        <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
+        <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
+        <permission name="android.permission.BACKUP"/>
+        <permission name="android.permission.BATTERY_STATS"/>
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+        <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
+        <permission name="android.permission.CHANGE_CONFIGURATION"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+        <permission name="android.permission.DELETE_PACKAGES"/>
+        <permission name="android.permission.FORCE_STOP_PACKAGES"/>
+        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.MANAGE_DEBUGGING"/>
+        <permission name="android.permission.MANAGE_FINGERPRINT"/>
+        <permission name="android.permission.MANAGE_USB"/>
+        <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MASTER_CLEAR"/>
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+        <permission name="android.permission.MOVE_PACKAGE"/>
+        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+        <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.SET_TIME"/>
+        <permission name="android.permission.STATUS_BAR"/>
+        <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.USER_ACTIVITY"/>
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
+        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.dialer.xml b/data/etc/car/com.android.car.dialer.xml
new file mode 100644
index 0000000..d44f5a1
--- /dev/null
+++ b/data/etc/car/com.android.car.dialer.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.dialer">
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.hvac.xml b/data/etc/car/com.android.car.hvac.xml
new file mode 100644
index 0000000..d3631e0
--- /dev/null
+++ b/data/etc/car/com.android.car.hvac.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.hvac">
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.media.xml b/data/etc/car/com.android.car.media.xml
new file mode 100644
index 0000000..d17453d
--- /dev/null
+++ b/data/etc/car/com.android.car.media.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.media">
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.notification.xml b/data/etc/car/com.android.car.notification.xml
new file mode 100644
index 0000000..8479512
--- /dev/null
+++ b/data/etc/car/com.android.car.notification.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.notification">
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.radio.xml b/data/etc/car/com.android.car.radio.xml
new file mode 100644
index 0000000..d7853ab
--- /dev/null
+++ b/data/etc/car/com.android.car.radio.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.radio">
+        <permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.settings.xml b/data/etc/car/com.android.car.settings.xml
new file mode 100644
index 0000000..5f7e1c1
--- /dev/null
+++ b/data/etc/car/com.android.car.settings.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.settings">
+        <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
+        <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
+        <permission name="android.permission.BACKUP"/>
+        <permission name="android.permission.BATTERY_STATS"/>
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+        <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
+        <permission name="android.permission.CHANGE_CONFIGURATION"/>
+        <permission name="android.permission.DELETE_PACKAGES"/>
+        <permission name="android.permission.DELETE_CACHE_FILES"/>
+        <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.FORCE_STOP_PACKAGES"/>
+        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+        <permission name="android.permission.MANAGE_DEBUGGING"/>
+        <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
+        <permission name="android.permission.MANAGE_FINGERPRINT"/>
+        <permission name="android.permission.MANAGE_USB"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
+        <permission name="android.permission.MASTER_CLEAR"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+        <permission name="android.permission.MOVE_PACKAGE"/>
+        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+        <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.SET_TIME"/>
+        <permission name="android.permission.SET_TIME_ZONE"/>
+        <permission name="android.permission.STATUS_BAR"/>
+        <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.USER_ACTIVITY"/>
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
+        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.themeplayground.xml b/data/etc/car/com.android.car.themeplayground.xml
new file mode 100644
index 0000000..cab4718
--- /dev/null
+++ b/data/etc/car/com.android.car.themeplayground.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.themeplayground">
+        <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.trust.xml b/data/etc/car/com.android.car.trust.xml
new file mode 100644
index 0000000..dc87af2
--- /dev/null
+++ b/data/etc/car/com.android.car.trust.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.trust">
+        <permission name="android.permission.BLUETOOTH" />
+        <permission name="android.permission.BLUETOOTH_ADMIN" />
+        <permission name="android.permission.ACCESS_COARSE_LOCATION"/>
+        <permission name="android.permission.ACCESS_FINE_LOCATION"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+        <permission name="android.permission.MANAGE_USERS" />
+        <permission name="android.permission.CONTROL_KEYGUARD" />
+        <permission name="android.permission.PROVIDE_TRUST_AGENT" />
+        <permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml
new file mode 100644
index 0000000..f1797de
--- /dev/null
+++ b/data/etc/car/com.android.car.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car">
+        <permission name="android.permission.LOCATION_HARDWARE"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+        <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+        <permission name="android.permission.REAL_GET_TASKS"/>
+        <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
new file mode 100644
index 0000000..6b26e8f
--- /dev/null
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.google.android.car.kitchensink">
+        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+        <permission name="android.permission.LOCATION_HARDWARE"/>
+        <permission name="android.permission.MANAGE_USB"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+        <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+        <permission name="android.permission.REAL_GET_TASKS"/>
+        <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 485add9..27e859c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -83,6 +83,7 @@
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SHUTDOWN"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.mms.service">
@@ -235,6 +236,7 @@
         <permission name="android.permission.CALL_PRIVILEGED"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MANAGE_ROLE_HOLDERS"/>
         <permission name="android.permission.MODIFY_AUDIO_ROUTING" />
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.STOP_APP_SWITCHES"/>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 07f81c1..4471017 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -2197,8 +2197,12 @@
     }
 
     /**
-     *
      * @return {@link GraphicBuffer} which is internally used by hardware bitmap
+     *
+     * Note: the GraphicBuffer does *not* have an associated {@link ColorSpace}.
+     * To render this object the same as its rendered with this Bitmap, you
+     * should also call {@link getColorSpace}.
+     *
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 816d1fd..11d635e 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -637,6 +637,7 @@
      * @see #setOrientation(Orientation)
      */
     public Orientation getOrientation() {
+        updateGradientStateOrientation();
         return mGradientState.mOrientation;
     }
 
@@ -653,6 +654,9 @@
      * @see #getOrientation()
      */
     public void setOrientation(Orientation orientation) {
+        // Update the angle here so that subsequent attempts to obtain the orientation
+        // from the angle overwrite previously configured values during inflation
+        mGradientState.mAngle = getAngleFromOrientation(orientation);
         mGradientState.mOrientation = orientation;
         mGradientIsDirty = true;
         invalidateSelf();
@@ -1242,6 +1246,76 @@
     }
 
     /**
+     * Update the orientation of the gradient based on the given angle only if the type is
+     * {@link #LINEAR_GRADIENT}
+     */
+    private void updateGradientStateOrientation() {
+        if (mGradientState.mGradient == LINEAR_GRADIENT) {
+            int angle = mGradientState.mAngle;
+            if (angle % 45 != 0) {
+                throw new IllegalArgumentException("Linear gradient requires 'angle' attribute to "
+                     + "be a multiple of 45");
+            }
+
+            Orientation orientation;
+            switch (angle) {
+                case 0:
+                    orientation = Orientation.LEFT_RIGHT;
+                    break;
+                case 45:
+                    orientation = Orientation.BL_TR;
+                    break;
+                case 90:
+                    orientation = Orientation.BOTTOM_TOP;
+                    break;
+                case 135:
+                    orientation = Orientation.BR_TL;
+                    break;
+                case 180:
+                    orientation = Orientation.RIGHT_LEFT;
+                    break;
+                case 225:
+                    orientation = Orientation.TR_BL;
+                    break;
+                case 270:
+                    orientation = Orientation.TOP_BOTTOM;
+                    break;
+                case 315:
+                    orientation = Orientation.TL_BR;
+                    break;
+                default:
+                    // Should not get here as exception is thrown above if angle is not multiple
+                    // of 45 degrees
+                    orientation = Orientation.LEFT_RIGHT;
+                    break;
+            }
+            mGradientState.mOrientation = orientation;
+        }
+    }
+
+    private int getAngleFromOrientation(Orientation orientation) {
+        switch (orientation) {
+            default:
+            case LEFT_RIGHT:
+                return 0;
+            case BL_TR:
+                return 45;
+            case BOTTOM_TOP:
+                return 90;
+            case BR_TL:
+                return 135;
+            case RIGHT_LEFT:
+                return 180;
+            case TR_BL:
+                return 225;
+            case TOP_BOTTOM:
+                return 270;
+            case TL_BR:
+                return 315;
+        }
+    }
+
+    /**
      * This checks mGradientIsDirty, and if it is true, recomputes both our drawing
      * rectangle (mRect) and the gradient itself, since it depends on our
      * rectangle too.
@@ -1270,6 +1344,7 @@
 
                 if (st.mGradient == LINEAR_GRADIENT) {
                     final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
+                    updateGradientStateOrientation();
                     switch (st.mOrientation) {
                     case TOP_BOTTOM:
                         x0 = r.left;            y0 = r.top;
@@ -1312,10 +1387,6 @@
                     y0 = r.top + (r.bottom - r.top) * st.mCenterY;
 
                     float radius = st.mGradientRadius;
-                    if (Float.compare(radius, 0.0f) == -1 || Float.isNaN(radius)) {
-                        throw new IllegalArgumentException("Gradient radius must be a valid "
-                                + "number greater than or equal to 0");
-                    }
                     if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) {
                         // Fall back to parent width or height if intrinsic
                         // size is not specified.
@@ -1511,8 +1582,6 @@
                     st.mAttrGradient, R.styleable.GradientDrawableGradient);
             try {
                 updateGradientDrawableGradient(t.getResources(), a);
-            } catch (XmlPullParserException e) {
-                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
@@ -1700,8 +1769,7 @@
         }
     }
 
-    private void updateGradientDrawableGradient(Resources r, TypedArray a)
-            throws XmlPullParserException {
+    private void updateGradientDrawableGradient(Resources r, TypedArray a) {
         final GradientState st = mGradientState;
 
         // Account for any configuration changes.
@@ -1764,42 +1832,7 @@
         }
 
         int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
-        angle %= 360;
-
-        if (angle % 45 != 0) {
-            throw new XmlPullParserException(a.getPositionDescription()
-                    + "<gradient> tag requires 'angle' attribute to "
-                    + "be a multiple of 45");
-        }
-
-        st.mAngle = angle;
-
-        switch (angle) {
-            case 0:
-                st.mOrientation = Orientation.LEFT_RIGHT;
-                break;
-            case 45:
-                st.mOrientation = Orientation.BL_TR;
-                break;
-            case 90:
-                st.mOrientation = Orientation.BOTTOM_TOP;
-                break;
-            case 135:
-                st.mOrientation = Orientation.BR_TL;
-                break;
-            case 180:
-                st.mOrientation = Orientation.RIGHT_LEFT;
-                break;
-            case 225:
-                st.mOrientation = Orientation.TR_BL;
-                break;
-            case 270:
-                st.mOrientation = Orientation.TOP_BOTTOM;
-                break;
-            case 315:
-                st.mOrientation = Orientation.TL_BR;
-                break;
-        }
+        st.mAngle = angle % 360;
 
         final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
         if (tv != null) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 9248ead..8092b1d 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -166,8 +166,6 @@
     auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
 
     int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
-    ALOGD("Setting buffer count to %d, min_undequeued %u, extraBuffers %u",
-            bufferCount, min_undequeued_buffers, extraBuffers);
     native_window_set_buffer_count(window, bufferCount);
 }
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 1d55334..159cf49 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -235,8 +235,6 @@
 }
 
 void EglManager::loadConfigs() {
-    ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
-
     // Note: The default pixel format is RGBA_8888, when other formats are
     // available, we should check the target pixel format and configure the
     // attributes list properly.
@@ -246,7 +244,6 @@
             // Try again without dirty regions enabled
             ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
             mSwapBehavior = SwapBehavior::Discard;
-            ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
             mEglConfig = load8BitsConfig(mEglDisplay, mSwapBehavior);
         } else {
             // Failed to get a valid config
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 977e790..8612e1b 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -816,6 +816,8 @@
          */
         @UnsupportedAppUsage
         public Builder setInternalLegacyStreamType(int streamType) {
+            mContentType = CONTENT_TYPE_UNKNOWN;
+            mUsage = USAGE_UNKNOWN;
             if (AudioProductStrategy.getAudioProductStrategies().size() > 0) {
                 AudioAttributes attributes =
                         AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
@@ -828,49 +830,52 @@
                     mTags = attributes.mTags;
                     mBundle = attributes.mBundle;
                     mSource = attributes.mSource;
-                    return this;
                 }
             }
-            switch(streamType) {
-                case AudioSystem.STREAM_VOICE_CALL:
-                    mContentType = CONTENT_TYPE_SPEECH;
-                    break;
-                case AudioSystem.STREAM_SYSTEM_ENFORCED:
-                    mFlags |= FLAG_AUDIBILITY_ENFORCED;
-                    // intended fall through, attributes in common with STREAM_SYSTEM
-                case AudioSystem.STREAM_SYSTEM:
-                    mContentType = CONTENT_TYPE_SONIFICATION;
-                    break;
-                case AudioSystem.STREAM_RING:
-                    mContentType = CONTENT_TYPE_SONIFICATION;
-                    break;
-                case AudioSystem.STREAM_MUSIC:
-                    mContentType = CONTENT_TYPE_MUSIC;
-                    break;
-                case AudioSystem.STREAM_ALARM:
-                    mContentType = CONTENT_TYPE_SONIFICATION;
-                    break;
-                case AudioSystem.STREAM_NOTIFICATION:
-                    mContentType = CONTENT_TYPE_SONIFICATION;
-                    break;
-                case AudioSystem.STREAM_BLUETOOTH_SCO:
-                    mContentType = CONTENT_TYPE_SPEECH;
-                    mFlags |= FLAG_SCO;
-                    break;
-                case AudioSystem.STREAM_DTMF:
-                    mContentType = CONTENT_TYPE_SONIFICATION;
-                    break;
-                case AudioSystem.STREAM_TTS:
-                    mContentType = CONTENT_TYPE_SONIFICATION;
-                    mFlags |= FLAG_BEACON;
-                    break;
-                case AudioSystem.STREAM_ACCESSIBILITY:
-                    mContentType = CONTENT_TYPE_SPEECH;
-                    break;
-                default:
-                    Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
+            if (mContentType == CONTENT_TYPE_UNKNOWN) {
+                switch (streamType) {
+                    case AudioSystem.STREAM_VOICE_CALL:
+                        mContentType = CONTENT_TYPE_SPEECH;
+                        break;
+                    case AudioSystem.STREAM_SYSTEM_ENFORCED:
+                        mFlags |= FLAG_AUDIBILITY_ENFORCED;
+                        // intended fall through, attributes in common with STREAM_SYSTEM
+                    case AudioSystem.STREAM_SYSTEM:
+                        mContentType = CONTENT_TYPE_SONIFICATION;
+                        break;
+                    case AudioSystem.STREAM_RING:
+                        mContentType = CONTENT_TYPE_SONIFICATION;
+                        break;
+                    case AudioSystem.STREAM_MUSIC:
+                        mContentType = CONTENT_TYPE_MUSIC;
+                        break;
+                    case AudioSystem.STREAM_ALARM:
+                        mContentType = CONTENT_TYPE_SONIFICATION;
+                        break;
+                    case AudioSystem.STREAM_NOTIFICATION:
+                        mContentType = CONTENT_TYPE_SONIFICATION;
+                        break;
+                    case AudioSystem.STREAM_BLUETOOTH_SCO:
+                        mContentType = CONTENT_TYPE_SPEECH;
+                        mFlags |= FLAG_SCO;
+                        break;
+                    case AudioSystem.STREAM_DTMF:
+                        mContentType = CONTENT_TYPE_SONIFICATION;
+                        break;
+                    case AudioSystem.STREAM_TTS:
+                        mContentType = CONTENT_TYPE_SONIFICATION;
+                        mFlags |= FLAG_BEACON;
+                        break;
+                    case AudioSystem.STREAM_ACCESSIBILITY:
+                        mContentType = CONTENT_TYPE_SPEECH;
+                        break;
+                    default:
+                        Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
+                }
             }
-            mUsage = usageForStreamType(streamType);
+            if (mUsage == USAGE_UNKNOWN) {
+                mUsage = usageForStreamType(streamType);
+            }
             return this;
         }
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c09bd79..56e8e85 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1659,6 +1659,7 @@
     private final Object mListenerLock = new Object();
     private MediaCodecInfo mCodecInfo;
     private final Object mCodecInfoLock = new Object();
+    private MediaCrypto mCrypto;
 
     private static final int EVENT_CALLBACK = 1;
     private static final int EVENT_SET_CALLBACK = 2;
@@ -1858,6 +1859,7 @@
     @Override
     protected void finalize() {
         native_finalize();
+        mCrypto = null;
     }
 
     /**
@@ -1873,6 +1875,7 @@
     public final void reset() {
         freeAllTrackedBuffers(); // free buffers first
         native_reset();
+        mCrypto = null;
     }
 
     private native final void native_reset();
@@ -1887,6 +1890,7 @@
     public final void release() {
         freeAllTrackedBuffers(); // free buffers first
         native_release();
+        mCrypto = null;
     }
 
     private native final void native_release();
@@ -1916,6 +1920,10 @@
      * @param crypto  Specify a crypto object to facilitate secure decryption
      *                of the media data. Pass {@code null} as {@code crypto} for
      *                non-secure codecs.
+     *                Please note that {@link MediaCodec} does NOT take ownership
+     *                of the {@link MediaCrypto} object; it is the application's
+     *                responsibility to properly cleanup the {@link MediaCrypto} object
+     *                when not in use.
      * @param flags   Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
      *                component as an encoder.
      * @throws IllegalArgumentException if the surface has been released (or is invalid),
@@ -2000,6 +2008,7 @@
         }
 
         mHasSurface = surface != null;
+        mCrypto = crypto;
 
         native_configure(keys, values, surface, crypto, descramblerBinder, flags);
     }
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index a56e7f5..f304f7c 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -24,6 +24,7 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -1105,7 +1106,11 @@
             mBitrateRange = Range.create(0, Integer.MAX_VALUE);
             mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT;
             // mBitrateRange = Range.create(1, 320000);
-            mSampleRateRanges = new Range[] { Range.create(8000, 96000) };
+            final int minSampleRate = SystemProperties.
+                getInt("ro.mediacodec.min_sample_rate", 7350);
+            final int maxSampleRate = SystemProperties.
+                getInt("ro.mediacodec.max_sample_rate", 192000);
+            mSampleRateRanges = new Range[] { Range.create(minSampleRate, maxSampleRate) };
             mSampleRates = null;
         }
 
@@ -1677,6 +1682,13 @@
                 return "PerformancePoint(" + info + ")";
             }
 
+            @Override
+            public int hashCode() {
+                // only max frame rate must equal between performance points that equal to one
+                // another
+                return mMaxFrameRate;
+            }
+
             /**
              * Create a detailed performance point with custom max frame rate and macroblock size.
              *
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 21b194d..5de56c7 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -244,30 +244,77 @@
 
         final Resizer resizer = new Resizer(size, signal);
         final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
+        Bitmap bitmap = null;
+        ExifInterface exif = null;
+        int orientation = 0;
+
+        // get orientation
+        if (MediaFile.isExifMimeType(mimeType)) {
+            exif = new ExifInterface(file);
+            switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
+                case ExifInterface.ORIENTATION_ROTATE_90:
+                    orientation = 90;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_180:
+                    orientation = 180;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_270:
+                    orientation = 270;
+                    break;
+            }
+        }
+
+        boolean isHeifFile = false;
+
         if (mimeType.equals("image/heif")
                 || mimeType.equals("image/heif-sequence")
                 || mimeType.equals("image/heic")
                 || mimeType.equals("image/heic-sequence")) {
+            isHeifFile = true;
             try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
                 retriever.setDataSource(file.getAbsolutePath());
-                return retriever.getThumbnailImageAtIndex(-1,
+                bitmap = retriever.getThumbnailImageAtIndex(-1,
                         new MediaMetadataRetriever.BitmapParams(), size.getWidth(),
                         size.getWidth() * size.getHeight());
             } catch (RuntimeException e) {
                 throw new IOException("Failed to create thumbnail", e);
             }
-        } else if (MediaFile.isExifMimeType(mimeType)) {
-            final ExifInterface exif = new ExifInterface(file);
+        }
+
+        if (bitmap == null && exif != null) {
             final byte[] raw = exif.getThumbnailBytes();
             if (raw != null) {
-                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+                try {
+                    bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+                } catch (ImageDecoder.DecodeException e) {
+                    Log.w(TAG, e);
+                }
             }
         }
 
         // Checkpoint before going deeper
         if (signal != null) signal.throwIfCanceled();
 
-        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
+        if (bitmap == null) {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
+            // Use ImageDecoder to do full image decode of heif format file
+            // will have right orientation. Don't rotate the bitmap again.
+            if (isHeifFile) {
+                return bitmap;
+            }
+        }
+
+        // Transform the bitmap if the orientation of the image is not 0.
+        if (orientation != 0 && bitmap != null) {
+            final int width = bitmap.getWidth();
+            final int height = bitmap.getHeight();
+
+            final Matrix m = new Matrix();
+            m.setRotate(orientation, width / 2, height / 2);
+            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
+        }
+
+        return bitmap;
     }
 
     /**
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 12b41ca..07dbf93 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -951,12 +951,10 @@
         return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
     }
 
-    /** Returns true if the current user makes it through the setup wizard, false otherwise. */
-    private boolean getIsUserSetup() {
-        return mUserSetup;
-    }
-
     private void setNotificationViewClipBounds(int height) {
+        if (height > mNotificationView.getHeight()) {
+            height = mNotificationView.getHeight();
+        }
         Rect clipBounds = new Rect();
         clipBounds.set(0, 0, mNotificationView.getWidth(), height);
         // Sets the clip region on the notification list view.
@@ -980,7 +978,6 @@
         }
     }
 
-    private static final int SWIPE_UP_MIN_DISTANCE = 75;
     private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
     private static final int SWIPE_MAX_OFF_PATH = 75;
     private static final int SWIPE_THRESHOLD_VELOCITY = 200;
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index b2baff5..73c7895 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.ActivityThread;
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -71,6 +72,7 @@
 /**
  * Notification assistant that provides guidance on notification channel blocking
  */
+@SuppressLint("OverrideAbstract")
 public class Assistant extends NotificationAssistantService {
     private static final String TAG = "ExtAssistant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -238,7 +240,7 @@
         }
         mSingleThreadExecutor.submit(() -> {
             NotificationEntry entry =
-                    new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+                    new NotificationEntry(getContext(), mPackageManager, sbn, channel, mSmsHelper);
             SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry);
             if (DEBUG) {
                 Log.d(TAG, String.format(
@@ -295,7 +297,7 @@
             }
             Ranking ranking = getRanking(sbn.getKey(), rankingMap);
             if (ranking != null && ranking.getChannel() != null) {
-                NotificationEntry entry = new NotificationEntry(mPackageManager,
+                NotificationEntry entry = new NotificationEntry(getContext(), mPackageManager,
                         sbn, ranking.getChannel(), mSmsHelper);
                 String key = getKey(
                         sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
index 84a8a8c..1ffbac9 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
@@ -28,18 +28,23 @@
 import android.app.Person;
 import android.app.RemoteInput;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.media.AudioSystem;
 import android.os.Build;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.util.SparseArray;
 
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Holds data about notifications.
@@ -47,6 +52,10 @@
 public class NotificationEntry {
     static final String TAG = "NotificationEntry";
 
+    // Copied from hidden definitions in Notification.TvExtender
+    private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
+
+    private final Context mContext;
     private final StatusBarNotification mSbn;
     private final IPackageManager mPackageManager;
     private int mTargetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -60,9 +69,10 @@
 
     private final Object mLock = new Object();
 
-    public NotificationEntry(IPackageManager packageManager, StatusBarNotification sbn,
-            NotificationChannel channel, SmsHelper smsHelper) {
-        mSbn = sbn;
+    public NotificationEntry(Context applicationContext, IPackageManager packageManager,
+            StatusBarNotification sbn, NotificationChannel channel, SmsHelper smsHelper) {
+        mContext = applicationContext;
+        mSbn = cloneStatusBarNotificationLight(sbn);
         mChannel = channel;
         mPackageManager = packageManager;
         mPreChannelsNotification = isPreChannelsNotification();
@@ -71,6 +81,66 @@
         mSmsHelper = smsHelper;
     }
 
+    /** Adapted from {@code Notification.lightenPayload}. */
+    @SuppressWarnings("nullness")
+    private static void lightenNotificationPayload(Notification notification) {
+        notification.tickerView = null;
+        notification.contentView = null;
+        notification.bigContentView = null;
+        notification.headsUpContentView = null;
+        notification.largeIcon = null;
+        if (notification.extras != null && !notification.extras.isEmpty()) {
+            final Set<String> keyset = notification.extras.keySet();
+            final int keysetSize = keyset.size();
+            final String[] keys = keyset.toArray(new String[keysetSize]);
+            for (int i = 0; i < keysetSize; i++) {
+                final String key = keys[i];
+                if (EXTRA_TV_EXTENDER.equals(key)
+                        || Notification.EXTRA_MESSAGES.equals(key)
+                        || Notification.EXTRA_MESSAGING_PERSON.equals(key)
+                        || Notification.EXTRA_PEOPLE_LIST.equals(key)) {
+                    continue;
+                }
+                final Object obj = notification.extras.get(key);
+                if (obj != null
+                        && (obj instanceof Parcelable
+                        || obj instanceof Parcelable[]
+                        || obj instanceof SparseArray
+                        || obj instanceof ArrayList)) {
+                    notification.extras.remove(key);
+                }
+            }
+        }
+    }
+
+    /** An interpretation of {@code Notification.cloneInto} with heavy=false. */
+    private Notification cloneNotificationLight(Notification notification) {
+        // We can't just use clone() here because the only way to remove the icons is with the
+        // builder, which we can only create with a Context.
+        Notification lightNotification =
+                Notification.Builder.recoverBuilder(mContext, notification)
+                        .setSmallIcon(0)
+                        .setLargeIcon((Icon) null)
+                        .build();
+        lightenNotificationPayload(lightNotification);
+        return lightNotification;
+    }
+
+    /** Adapted from {@code StatusBarNotification.cloneLight}. */
+    public StatusBarNotification cloneStatusBarNotificationLight(StatusBarNotification sbn) {
+        return new StatusBarNotification(
+                sbn.getPackageName(),
+                sbn.getOpPkg(),
+                sbn.getId(),
+                sbn.getTag(),
+                sbn.getUid(),
+                /*initialPid=*/ 0,
+                /*score=*/ 0,
+                cloneNotificationLight(sbn.getNotification()),
+                sbn.getUser(),
+                sbn.getPostTime());
+    }
+
     private boolean isPreChannelsNotification() {
         try {
             ApplicationInfo info = mPackageManager.getApplicationInfo(
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 10360a3..93c522e 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -77,15 +77,13 @@
             new ConversationActions(Collections.emptyList(), null);
 
     private Context mContext;
-    private TextClassifier mTextClassifier;
+    private TextClassificationManager mTextClassificationManager;
     private AssistantSettings mSettings;
     private LruCache<String, Session> mSessionCache = new LruCache<>(MAX_RESULT_ID_TO_CACHE);
 
     SmartActionsHelper(Context context, AssistantSettings settings) {
         mContext = context;
-        TextClassificationManager textClassificationManager =
-                mContext.getSystemService(TextClassificationManager.class);
-        mTextClassifier = textClassificationManager.getTextClassifier();
+        mTextClassificationManager = mContext.getSystemService(TextClassificationManager.class);
         mSettings = settings;
     }
 
@@ -244,7 +242,7 @@
                                 .map(ConversationAction::getType)
                                 .toArray(String[]::new))
                         .build();
-        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+        getTextClassifier().onTextClassifierEvent(textClassifierEvent);
     }
 
     /**
@@ -286,7 +284,7 @@
                         .setTypeConfig(typeConfigBuilder.build())
                         .build();
         ConversationActions conversationActions =
-                mTextClassifier.suggestConversationActions(request);
+                getTextClassifier().suggestConversationActions(request);
         reportActionsGenerated(
                 conversationActions.getId(), conversationActions.getConversationActions());
         return conversationActions;
@@ -310,7 +308,7 @@
                         TextClassifierEvent.TYPE_ACTIONS_SHOWN, session.resultId)
                         .build();
         // TODO: If possible, report which replies / actions are actually seen by user.
-        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+        getTextClassifier().onTextClassifierEvent(textClassifierEvent);
     }
 
     void onNotificationDirectReplied(String key) {
@@ -322,7 +320,7 @@
                 createTextClassifierEventBuilder(
                         TextClassifierEvent.TYPE_MANUAL_REPLY, session.resultId)
                         .build();
-        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+        getTextClassifier().onTextClassifierEvent(textClassifierEvent);
     }
 
     void onSuggestedReplySent(String key, CharSequence reply,
@@ -340,7 +338,7 @@
                         .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
                         .setScores(session.repliesScores.getOrDefault(reply, 0f))
                         .build();
-        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+        getTextClassifier().onTextClassifierEvent(textClassifierEvent);
     }
 
     void onActionClicked(String key, Notification.Action action,
@@ -361,7 +359,7 @@
                         TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
                         .setEntityTypes(actionType)
                         .build();
-        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+        getTextClassifier().onTextClassifierEvent(textClassifierEvent);
     }
 
     private Notification.Action createNotificationActionFromRemoteAction(
@@ -471,6 +469,10 @@
         return new ArrayList<>(extractMessages);
     }
 
+    private TextClassifier getTextClassifier() {
+        return mTextClassificationManager.getTextClassifier();
+    }
+
     private static boolean arePersonsEqual(Person left, Person right) {
         return Objects.equals(left.getKey(), right.getKey())
                 && Objects.equals(left.getName(), right.getName())
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
index 3db275a..a87d57c 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
@@ -102,7 +102,8 @@
     public void testNoSnoozingOnPost() {
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         StatusBarNotification sbn = generateSbn(channel.getId());
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
 
 
         mAgingHelper.onNotificationPosted(entry);
@@ -113,7 +114,8 @@
     public void testPostResetsSnooze() {
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         StatusBarNotification sbn = generateSbn(channel.getId());
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
 
 
         mAgingHelper.onNotificationPosted(entry);
@@ -124,7 +126,8 @@
     public void testSnoozingOnSeen() {
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         StatusBarNotification sbn = generateSbn(channel.getId());
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
         entry.setSeen();
         when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
 
@@ -137,7 +140,8 @@
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
         StatusBarNotification sbn = generateSbn(channel.getId());
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
         when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
 
         mAgingHelper.onNotificationSeen(entry);
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index ee29bc5..012dcc0 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -468,8 +468,10 @@
     @Test
     public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
         StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
-        Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry(
-                mPackageManager, sbn, P1C3, mSmsHelper), new ArrayList<>(), new ArrayList<>());
+        Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(
+                new NotificationEntry(mContext, mPackageManager, sbn, P1C3, mSmsHelper),
+                new ArrayList<>(),
+                new ArrayList<>());
         assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
     }
 }
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
index f51e911..c026079 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
@@ -24,6 +24,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
@@ -34,6 +35,8 @@
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.os.Build;
 import android.os.Process;
@@ -41,9 +44,6 @@
 import android.service.notification.StatusBarNotification;
 import android.testing.TestableContext;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -53,6 +53,9 @@
 
 import java.util.ArrayList;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 @RunWith(AndroidJUnit4.class)
 public class NotificationEntryTest {
     private String mPkg = "pkg";
@@ -113,7 +116,8 @@
         people.add(new Person.Builder().setKey("mailto:testing@android.com").build());
         sbn.getNotification().extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, people);
 
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
         assertTrue(entry.involvesPeople());
     }
 
@@ -121,7 +125,8 @@
     public void testNotPerson() {
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         StatusBarNotification sbn = generateSbn(channel.getId());
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
         assertFalse(entry.involvesPeople());
     }
 
@@ -129,7 +134,8 @@
     public void testHasPerson_matchesDefaultSmsApp() {
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         StatusBarNotification sbn = generateSbn(channel.getId(), DEFAULT_SMS_PACKAGE_NAME);
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
         assertTrue(entry.involvesPeople());
     }
 
@@ -137,7 +143,8 @@
     public void testHasPerson_doesntMatchDefaultSmsApp() {
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         StatusBarNotification sbn = generateSbn(channel.getId(), "abc");
-        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, sbn, channel, mSmsHelper);
         assertFalse(entry.involvesPeople());
     }
 
@@ -148,8 +155,8 @@
         Notification n = new Notification.Builder(mContext, channel.getId())
                 .setStyle(new Notification.InboxStyle())
                 .build();
-        NotificationEntry entry =
-                new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
         assertTrue(entry.hasStyle(Notification.InboxStyle.class));
     }
 
@@ -160,8 +167,8 @@
         Notification n = new Notification.Builder(mContext, channel.getId())
                 .setStyle(new Notification.MessagingStyle(""))
                 .build();
-        NotificationEntry entry =
-                new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
         assertTrue(entry.hasStyle(Notification.MessagingStyle.class));
     }
 
@@ -172,8 +179,8 @@
         Notification n = new Notification.Builder(mContext, channel.getId())
                 .setStyle(new Notification.BigPictureStyle())
                 .build();
-        NotificationEntry entry =
-                new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
         assertFalse(entry.hasStyle(Notification.InboxStyle.class));
         assertFalse(entry.hasStyle(Notification.MessagingStyle.class));
     }
@@ -184,7 +191,7 @@
         channel.setSound(null, new AudioAttributes.Builder().setUsage(USAGE_ALARM).build());
 
         NotificationEntry entry = new NotificationEntry(
-                mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
+                mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
 
         assertTrue(entry.isAudioAttributesUsage(USAGE_ALARM));
     }
@@ -193,7 +200,7 @@
     public void testIsNotAudioAttributes() {
         NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
         NotificationEntry entry = new NotificationEntry(
-                mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
+                mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
 
         assertFalse(entry.isAudioAttributesUsage(USAGE_ALARM));
     }
@@ -205,8 +212,8 @@
         Notification n = new Notification.Builder(mContext, channel.getId())
                 .setCategory(Notification.CATEGORY_EMAIL)
                 .build();
-        NotificationEntry entry =
-                new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
 
         assertTrue(entry.isCategory(Notification.CATEGORY_EMAIL));
         assertFalse(entry.isCategory(Notification.CATEGORY_MESSAGE));
@@ -219,8 +226,8 @@
         Notification n = new Notification.Builder(mContext, channel.getId())
                 .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .build();
-        NotificationEntry entry =
-                new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
 
         assertTrue(entry.isOngoing());
     }
@@ -232,9 +239,28 @@
         Notification n = new Notification.Builder(mContext, channel.getId())
                 .setFlag(FLAG_CAN_COLORIZE, true)
                 .build();
-        NotificationEntry entry =
-                new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper);
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
 
         assertFalse(entry.isOngoing());
     }
+
+    @Test
+    public void testShrinkNotification() {
+        Notification n = new Notification.Builder(mContext, "")
+                .setLargeIcon(Icon.createWithResource(
+                        mContext, android.R.drawable.alert_dark_frame))
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .build();
+        n.largeIcon = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
+
+        NotificationEntry entry = new NotificationEntry(
+                mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
+
+        assertNull(entry.getNotification().getSmallIcon());
+        assertNull(entry.getNotification().getLargeIcon());
+        assertNull(entry.getNotification().largeIcon);
+        assertNull(entry.getNotification().extras.getParcelable(Notification.EXTRA_LARGE_ICON));
+    }
 }
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
index dfa1ea0..69abe87 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
@@ -46,9 +46,6 @@
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextClassifierEvent;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
 import com.google.common.truth.FailureStrategy;
 import com.google.common.truth.Subject;
 import com.google.common.truth.SubjectFactory;
@@ -71,9 +68,11 @@
 
 import javax.annotation.Nullable;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 @RunWith(AndroidJUnit4.class)
 public class SmartActionsHelperTest {
-    private static final String NOTIFICATION_KEY = "key";
     private static final String RESULT_ID = "id";
     private static final float SCORE = 0.7f;
     private static final CharSequence SMART_REPLY = "Home";
@@ -88,7 +87,6 @@
     IPackageManager mIPackageManager;
     @Mock
     private TextClassifier mTextClassifier;
-    @Mock
     private StatusBarNotification mStatusBarNotification;
     @Mock
     private SmsHelper mSmsHelper;
@@ -108,9 +106,6 @@
         when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
                 .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID));
 
-        when(mStatusBarNotification.getPackageName()).thenReturn("random.app");
-        when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle());
-        when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY);
         mNotificationBuilder = new Notification.Builder(mContext, "channel");
         mSettings = AssistantSettings.createForTesting(
                 null, null, Process.myUserHandle().getIdentifier(), null);
@@ -119,10 +114,15 @@
         mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings);
     }
 
+    private void setStatusBarNotification(Notification n) {
+        mStatusBarNotification = new StatusBarNotification("random.app", "random.app", 0,
+        "tag", Process.myUid(), Process.myPid(), n, Process.myUserHandle(), null, 0);
+    }
+
     @Test
     public void testSuggest_notMessageNotification() {
         Notification notification = mNotificationBuilder.setContentText(MESSAGE).build();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
 
@@ -137,7 +137,7 @@
                         .setContentText(MESSAGE)
                         .setCategory(Notification.CATEGORY_MESSAGE)
                         .build();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         ConversationActions.Request request = runSuggestAndCaptureRequest();
 
@@ -154,7 +154,7 @@
         mSettings.mGenerateActions = false;
         mSettings.mGenerateReplies = false;
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
 
@@ -167,7 +167,7 @@
         mSettings.mGenerateReplies = true;
         mSettings.mGenerateActions = false;
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         ConversationActions.Request request = runSuggestAndCaptureRequest();
 
@@ -184,7 +184,7 @@
         mSettings.mGenerateReplies = false;
         mSettings.mGenerateActions = true;
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         ConversationActions.Request request = runSuggestAndCaptureRequest();
 
@@ -200,7 +200,7 @@
     @Test
     public void testSuggest_nonMessageStyleMessageNotification() {
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         List<ConversationActions.Message> messages =
                 runSuggestAndCaptureRequest().getConversation();
@@ -233,7 +233,7 @@
                         .setStyle(style)
                         .setActions(createReplyAction())
                         .build();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         List<ConversationActions.Message> messages =
                 runSuggestAndCaptureRequest().getConversation();
@@ -288,7 +288,7 @@
                         .setStyle(style)
                         .setActions(createReplyAction())
                         .build();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
 
@@ -307,7 +307,7 @@
                         .setStyle(style)
                         .setActions(createReplyAction())
                         .build();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
 
@@ -318,11 +318,11 @@
     @Test
     public void testOnSuggestedReplySent() {
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
-        mSmartActionsHelper.onSuggestedReplySent(
-                NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+        mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
+                NotificationAssistantService.SOURCE_FROM_ASSISTANT);
 
         ArgumentCaptor<TextClassifierEvent> argumentCaptor =
                 ArgumentCaptor.forClass(TextClassifierEvent.class);
@@ -338,7 +338,7 @@
     @Test
     public void testOnSuggestedReplySent_anotherNotification() {
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
         mSmartActionsHelper.onSuggestedReplySent(
@@ -353,11 +353,11 @@
         when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
                 .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null));
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
-        mSmartActionsHelper.onSuggestedReplySent(
-                NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+        mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
+                NotificationAssistantService.SOURCE_FROM_ASSISTANT);
 
         verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class));
     }
@@ -365,10 +365,10 @@
     @Test
     public void testOnNotificationDirectReply() {
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
-        mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY);
+        mSmartActionsHelper.onNotificationDirectReplied(mStatusBarNotification.getKey());
 
         ArgumentCaptor<TextClassifierEvent> argumentCaptor =
                 ArgumentCaptor.forClass(TextClassifierEvent.class);
@@ -381,7 +381,7 @@
     @Test
     public void testOnNotificationExpansionChanged() {
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
         mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
@@ -397,7 +397,7 @@
     @Test
     public void testOnNotificationsSeen_notExpanded() {
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
         mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false);
@@ -409,7 +409,7 @@
     @Test
     public void testOnNotifications_expanded() {
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
 
         mSmartActionsHelper.suggest(createNotificationEntry());
         mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
@@ -438,7 +438,7 @@
                                 Collections.singletonList(conversationAction), null));
 
         Notification notification = createMessageNotification();
-        when(mStatusBarNotification.getNotification()).thenReturn(notification);
+        setStatusBarNotification(notification);
         SmartActionsHelper.SmartSuggestions suggestions =
                 mSmartActionsHelper.suggest(createNotificationEntry());
 
@@ -477,7 +477,8 @@
     private NotificationEntry createNotificationEntry() {
         NotificationChannel channel =
                 new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
-        return new NotificationEntry(mIPackageManager, mStatusBarNotification, channel, mSmsHelper);
+        return new NotificationEntry(
+                mContext, mIPackageManager, mStatusBarNotification, channel, mSmsHelper);
     }
 
     private Notification createMessageNotification() {
diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml
index d00a551..3da566f 100644
--- a/packages/NetworkStack/AndroidManifestBase.xml
+++ b/packages/NetworkStack/AndroidManifestBase.xml
@@ -24,6 +24,7 @@
         android:label="NetworkStack"
         android:defaultToDeviceProtectedStorage="true"
         android:directBootAware="true"
+        android:persistent="true"
         android:usesCleartextTraffic="true">
 
         <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 0ffd471..05c2f24 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -174,7 +174,7 @@
                             packageManager.getResourcesForApplication(mActivityPackage);
                     title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE));
                 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
-                    Log.d(TAG, "Couldn't find info", e);
+                    Log.w(TAG, "Couldn't find info", e);
                 }
             } else {
                 title = mMetaData.getString(META_DATA_PREFERENCE_TITLE);
@@ -183,26 +183,16 @@
         // Set the preference title to the activity's label if no
         // meta-data is found
         if (title == null) {
-            title = getActivityInfo(context).loadLabel(packageManager);
+            final ActivityInfo activityInfo = getActivityInfo(context);
+            if (activityInfo == null) {
+                return null;
+            }
+            title = activityInfo.loadLabel(packageManager);
         }
         return title;
     }
 
     /**
-     * Returns the raw metadata for summary, this is used for comparing 2 summary text without
-     * loading the real string.
-     */
-    public String getSummaryReference() {
-        if (mSummaryOverride != null) {
-            return mSummaryOverride.toString();
-        }
-        if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
-            return mMetaData.get(META_DATA_PREFERENCE_SUMMARY).toString();
-        }
-        return null;
-    }
-
-    /**
      * Overrides the summary. This can happen when injected tile wants to provide dynamic summary.
      */
     public void overrideSummary(CharSequence summaryOverride) {
@@ -302,7 +292,7 @@
         if (iconResId != 0) {
             final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId);
             if (isIconTintable(context)) {
-                final TypedArray a = context.obtainStyledAttributes(new int[] {
+                final TypedArray a = context.obtainStyledAttributes(new int[]{
                         android.R.attr.colorControlNormal});
                 final int tintColor = a.getColor(0, 0);
                 a.recycle();
@@ -357,6 +347,9 @@
             if (infoList != null && !infoList.isEmpty()) {
                 mActivityInfo = infoList.get(0).activityInfo;
                 mMetaData = mActivityInfo.metaData;
+            } else {
+                Log.e(TAG, "Cannot find package info for "
+                        + intent.getComponent().flattenToString());
             }
         }
         return mActivityInfo;
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
index 6b45a47..c8a80ac 100644
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
+++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.24" android:color="@android:color/black" />
+    <item android:alpha="0.24" android:color="?android:attr/colorBackground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
index 5ef085b..8dcfdbb 100644
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
+++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.47" android:color="@android:color/black" />
+    <item android:alpha="0.47" android:color="?android:attr/colorBackground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
index 81f63a4..34de548 100644
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
+++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.3" android:color="@android:color/white" />
+    <item android:alpha="0.3" android:color="?android:attr/colorForeground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
index 61f4e26..15944c3 100644
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
+++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@android:color/white" />
+    <item android:color="?android:attr/colorForeground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index ff34578..1d351a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -50,6 +50,8 @@
 
     // See mConnectAttempted
     private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
+    // Some Hearing Aids (especially the 2nd device) needs more time to do service discovery
+    private static final long MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT = 15000;
     private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
 
     private final Context mContext;
@@ -223,7 +225,7 @@
                 // various profiles
                 // If UUIDs are not available yet, connect will be happen
                 // upon arrival of the ACTION_UUID intent.
-                Log.d(TAG, "No profiles. Maybe we will connect later");
+                Log.d(TAG, "No profiles. Maybe we will connect later for device " + mDevice);
                 return;
             }
 
@@ -608,10 +610,12 @@
         long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT;
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) {
             timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT;
+        } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
+            timeout = MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT;
         }
 
         if (BluetoothUtils.D) {
-            Log.d(TAG, "onUuidChanged: Time since last connect"
+            Log.d(TAG, "onUuidChanged: Time since last connect="
                     + (SystemClock.elapsedRealtime() - mConnectAttempted));
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
index 257943e..a5b5312 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
@@ -44,6 +44,8 @@
     //    doesn't touch the walls
     private val perimeterPath = Path()
     private val scaledPerimeter = Path()
+    private val errorPerimeterPath = Path()
+    private val scaledErrorPerimeter = Path()
     // Fill will cover the whole bounding rect of the fillMask, and be masked by the path
     private val fillMask = Path()
     private val scaledFill = Path()
@@ -242,7 +244,7 @@
             }
         } else if (powerSaveEnabled) {
             // If power save is enabled draw the perimeter path with colorError
-            c.drawPath(scaledPerimeter, errorPaint)
+            c.drawPath(scaledErrorPerimeter, errorPaint)
             // And draw the plus sign on top of the fill
             c.drawPath(scaledPlus, errorPaint)
         }
@@ -364,6 +366,7 @@
         }
 
         perimeterPath.transform(scaleMatrix, scaledPerimeter)
+        errorPerimeterPath.transform(scaleMatrix, scaledErrorPerimeter)
         fillMask.transform(scaleMatrix, scaledFill)
         scaledFill.computeBounds(fillRect, true)
         boltPath.transform(scaleMatrix, scaledBolt)
@@ -382,8 +385,12 @@
         val pathString = context.resources.getString(
                 com.android.internal.R.string.config_batterymeterPerimeterPath)
         perimeterPath.set(PathParser.createPathFromPathData(pathString))
-        val b = RectF()
-        perimeterPath.computeBounds(b, true)
+        perimeterPath.computeBounds(RectF(), true)
+
+        val errorPathString = context.resources.getString(
+                com.android.internal.R.string.config_batterymeterErrorPerimeterPath)
+        errorPerimeterPath.set(PathParser.createPathFromPathData(errorPathString))
+        errorPerimeterPath.computeBounds(RectF(), true)
 
         val fillMaskString = context.resources.getString(
                 com.android.internal.R.string.config_batterymeterFillMask)
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 5547df1..49c8ce3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -123,7 +123,8 @@
     /**
      * Synchronization lock for managing concurrency between main and worker threads.
      *
-     * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}.
+     * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints} and
+     * {@link #mScanner}.
      */
     private final Object mLock = new Object();
 
@@ -168,6 +169,7 @@
     private static final String WIFI_SECURITY_OWE = "OWE";
     private static final String WIFI_SECURITY_SUITE_B_192 = "SUITE_B_192";
 
+    @GuardedBy("mLock")
     @VisibleForTesting
     Scanner mScanner;
 
@@ -276,9 +278,11 @@
      * <p>Sets {@link #mStaleScanResults} to true.
      */
     private void pauseScanning() {
-        if (mScanner != null) {
-            mScanner.pause();
-            mScanner = null;
+        synchronized (mLock) {
+            if (mScanner != null) {
+                mScanner.pause();
+                mScanner = null;
+            }
         }
         mStaleScanResults = true;
     }
@@ -289,12 +293,14 @@
      * <p>The score cache should be registered before this method is invoked.
      */
     public void resumeScanning() {
-        if (mScanner == null) {
-            mScanner = new Scanner();
-        }
+        synchronized (mLock) {
+            if (mScanner == null) {
+                mScanner = new Scanner();
+            }
 
-        if (isWifiEnabled()) {
-            mScanner.resume();
+            if (isWifiEnabled()) {
+                mScanner.resume();
+            }
         }
     }
 
@@ -744,7 +750,6 @@
     }
 
     private void updateNetworkInfo(NetworkInfo networkInfo) {
-
         /* Sticky broadcasts can call this when wifi is disabled */
         if (!isWifiEnabled()) {
             clearAccessPointsAndConditionallyUpdate();
@@ -881,18 +886,25 @@
      * true.
      */
     private void updateWifiState(int state) {
+        if (isVerboseLoggingEnabled()) {
+            Log.d(TAG, "updateWifiState: " + state);
+        }
         if (state == WifiManager.WIFI_STATE_ENABLED) {
-            if (mScanner != null) {
-                // We only need to resume if mScanner isn't null because
-                // that means we want to be scanning.
-                mScanner.resume();
+            synchronized (mLock) {
+                if (mScanner != null) {
+                    // We only need to resume if mScanner isn't null because
+                    // that means we want to be scanning.
+                    mScanner.resume();
+                }
             }
         } else {
             clearAccessPointsAndConditionallyUpdate();
             mLastInfo = null;
             mLastNetworkInfo = null;
-            if (mScanner != null) {
-                mScanner.pause();
+            synchronized (mLock) {
+                if (mScanner != null) {
+                    mScanner.pause();
+                }
             }
             mStaleScanResults = true;
         }
@@ -920,12 +932,18 @@
         private int mRetry = 0;
 
         void resume() {
+            if (isVerboseLoggingEnabled()) {
+                Log.d(TAG, "Scanner resume");
+            }
             if (!hasMessages(MSG_SCAN)) {
                 sendEmptyMessage(MSG_SCAN);
             }
         }
 
         void pause() {
+            if (isVerboseLoggingEnabled()) {
+                Log.d(TAG, "Scanner pause");
+            }
             mRetry = 0;
             removeMessages(MSG_SCAN);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index d0d1e58..5da6205 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -23,6 +23,7 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(RobolectricTestRunner.class)
 public class TileTest {
@@ -164,4 +165,17 @@
 
         assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp);
     }
+
+    @Test
+    public void getTitle_noActivity_returnNull() {
+        final ResolveInfo info = new ResolveInfo();
+        info.activityInfo = mActivityInfo;
+        final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager());
+        spm.removePackage(mActivityInfo.packageName);
+
+        final Tile tile = new Tile(mActivityInfo, "category");
+        ReflectionHelpers.setField(tile, "mActivityInfo", null);
+
+        assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull();
+    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 66d5d11..25a3fa2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -17,6 +17,7 @@
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.view.View;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
@@ -32,13 +33,20 @@
     void startPendingIntentDismissingKeyguard(PendingIntent intent);
 
     /**
-     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but allows
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent)}, but allows
      * you to specify the callback that is executed on the UI thread after the intent is sent.
      */
     void startPendingIntentDismissingKeyguard(PendingIntent intent,
             Runnable intentSentUiThreadCallback);
 
     /**
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but also
+     * specifies an associated view that should be used for the activity launch animation.
+     */
+    void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback, View associatedView);
+
+    /**
      * The intent flag can be specified in startActivity().
      */
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java
index 6be7707..68d2ed7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java
@@ -26,8 +26,8 @@
  * Plugin which provides a "Panel" {@link View} to be rendered inside of the GlobalActions menu.
  *
  * Implementations should construct a new {@link PanelViewController} with the given
- * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks)}, and should not hold onto
- * a reference, instead allowing Global Actions to manage the lifetime of the object.
+ * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks, boolean)}, and should not
+ * hold onto a reference, instead allowing Global Actions to manage the lifetime of the object.
  *
  * Under this assumption, {@link PanelViewController} represents the lifetime of a single invocation
  * of the Global Actions menu. The {@link View} for the Panel is generated when the
@@ -50,9 +50,10 @@
      *
      * @param callbacks {@link Callbacks} instance that can be used by the Panel to interact with
      *                  the Global Actions menu.
+     * @param deviceLocked Indicates whether or not the device is currently locked.
      * @return A {@link PanelViewController} instance used to receive Global Actions events.
      */
-    PanelViewController onPanelShown(Callbacks callbacks);
+    PanelViewController onPanelShown(Callbacks callbacks, boolean deviceLocked);
 
     /**
      * Provides methods to interact with the Global Actions menu.
@@ -92,5 +93,10 @@
          * {@link #getPanelContent()}) is dismissed.
          */
         void onDismissed();
+
+        /**
+         * Invoked when the device is either locked or unlocked.
+         */
+        void onDeviceLockStateChanged(boolean locked);
     }
 }
diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
index e88e2c9..7c15fe6 100644
--- a/packages/SystemUI/res-keyguard/layout/digital_clock.xml
+++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
@@ -20,9 +20,19 @@
     android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal"
     android:layout_alignParentTop="true">
-  <include
+  <TextClock
       android:id="@+id/lock_screen_clock"
-      layout="@layout/text_clock"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal"
+      android:gravity="center_horizontal"
+      android:letterSpacing="0.03"
+      android:textColor="?attr/wallpaperTextColor"
+      android:singleLine="true"
+      style="@style/widget_small_bold"
+      android:format12Hour="@string/keyguard_widget_12_hours_format"
+      android:format24Hour="@string/keyguard_widget_24_hours_format"
+      android:elegantTextHeight="false"
   />
 </FrameLayout>
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 86ed9e3..3118ab7 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -30,9 +30,20 @@
          android:layout_height="wrap_content"
          android:layout_gravity="center_horizontal"
          android:layout_alignParentTop="true">
-        <include
+        <TextClock
              android:id="@+id/default_clock_view"
-             layout="@layout/text_clock" />
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:layout_gravity="center_horizontal"
+             android:gravity="center_horizontal"
+             android:letterSpacing="0.03"
+             android:textColor="?attr/wallpaperTextColor"
+             android:singleLine="true"
+             style="@style/widget_big"
+             android:format12Hour="@string/keyguard_widget_12_hours_format"
+             android:format24Hour="@string/keyguard_widget_24_hours_format"
+             android:elegantTextHeight="false"
+        />
         <TextClock
              android:id="@+id/default_clock_view_bold"
              android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml
deleted file mode 100644
index 9f7ea0d..0000000
--- a/packages/SystemUI/res-keyguard/layout/text_clock.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<TextClock
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal"
-    android:gravity="center_horizontal"
-    android:letterSpacing="0.03"
-    android:textColor="?attr/wallpaperTextColor"
-    android:singleLine="true"
-    style="@style/widget_big"
-    android:format12Hour="@string/keyguard_widget_12_hours_format"
-    android:format24Hour="@string/keyguard_widget_24_hours_format"
-    android:elegantTextHeight="false"
-/>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 6e4416a..ab48f1d 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -73,6 +73,13 @@
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:ellipsize">none</item>
     </style>
+    <style name="widget_small_bold">
+        <item name="android:textStyle">bold</item>
+        <item name="android:textSize">@dimen/widget_title_font_size</item>
+        <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:ellipsize">none</item>
+    </style>
 
     <style name="BouncerSecurityContainer">
         <item name="android:layout_gravity">center_horizontal|bottom</item>
diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml
deleted file mode 100644
index b5dd5c3..0000000
--- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml
deleted file mode 100644
index 14704bf..0000000
--- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="233"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml
deleted file mode 100644
index d465f19..0000000
--- a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="250"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 12.699,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-    <objectAnimator
-        android:duration="200"
-        android:propertyName="rotation"
-        android:valueFrom="-45.0"
-        android:valueTo="45.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml
deleted file mode 100644
index 7a38ac3..0000000
--- a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="250"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 11.287,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-    <objectAnimator
-        android:duration="200"
-        android:propertyName="rotation"
-        android:valueFrom="45.0"
-        android:valueTo="-45.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml
deleted file mode 100644
index 125a616..0000000
--- a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="250"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 12.699,15.287 c -0.04833,0.452 -0.04833,-7.03583 0.0,-6.586"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-    <objectAnimator
-        android:duration="200"
-        android:propertyName="rotation"
-        android:valueFrom="45.0"
-        android:valueTo="-45.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml
deleted file mode 100644
index 3fd4df5..0000000
--- a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="250"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 11.287,15.287 c 0.04883,0.452 0.04883,-7.03583 0.0,-6.586"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-    <objectAnimator
-        android:duration="200"
-        android:propertyName="rotation"
-        android:valueFrom="-45.0"
-        android:valueTo="45.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml
deleted file mode 100644
index 5e71847..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="633"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml
deleted file mode 100644
index 9081e6f..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="633"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml
deleted file mode 100644
index eaf5f05..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="400"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="633"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml b/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml
deleted file mode 100644
index 919a4dd..0000000
--- a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="333"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="316"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="316"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="333"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/drawable/button_border_selected.xml b/packages/SystemUI/res/drawable/button_border_selected.xml
index 1e40ade..01e7099 100644
--- a/packages/SystemUI/res/drawable/button_border_selected.xml
+++ b/packages/SystemUI/res/drawable/button_border_selected.xml
@@ -19,7 +19,7 @@
     <solid
         android:color="@color/notification_guts_selection_bg" />
     <stroke
-        android:width="2dp"
+        android:width="1dp"
         android:color="@color/GM2_grey_300"/>
     <corners android:radius="@dimen/rect_button_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/button_border_unselected.xml b/packages/SystemUI/res/drawable/button_border_unselected.xml
index 4ea3764..b9c4ced 100644
--- a/packages/SystemUI/res/drawable/button_border_unselected.xml
+++ b/packages/SystemUI/res/drawable/button_border_unselected.xml
@@ -18,7 +18,7 @@
        android:shape="rectangle"
        android:color="@color/notification_guts_selection_bg">
     <stroke
-        android:width="2dp"
+        android:width="1dp"
         android:color="@color/GM2_grey_300"/>
 
     <corners android:radius="@dimen/rect_button_radius" />
diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml
deleted file mode 100644
index 33d1fb3..0000000
--- a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_bluetooth_transient"
-    android:width="48dp"
-    android:viewportWidth="48"
-    android:height="48dp"
-    android:viewportHeight="48" >
-    <group
-        android:name="ic_bluetooth_transient_0"
-        android:translateX="23.99883"
-        android:translateY="23.99839" >
-        <group
-            android:name="ic_bluetooth_transient_pivot"
-            android:translateX="-23.99883"
-            android:translateY="-23.99839" >
-            <group
-                android:name="ic_bluetooth_white_outlines"
-                android:translateX="22.73986"
-                android:translateY="23.99839" >
-                <group
-                    android:name="ic_bluetooth_white_outlines_pivot"
-                    android:translateX="-12.84937"
-                    android:translateY="-20.4772" >
-                    <path
-                        android:name="b_shape_merged"
-                        android:pathData="M 17.1289978027,20.4790039062 c 0.0,0.0 7.5,-7.48100280762 7.5,-7.48100280762 c 0.81999206543,-0.819000244141 0.81999206543,-2.13899230957 0.0,-2.95999145508 c 0.0,0.0 -8.93899536133,-8.93899536133 -8.93899536133,-8.93899536133 c 0.0,0.0 -0.0610046386719,-0.0610046386719 -0.0610046386718,-0.0610046386719 c -0.844009399414,-0.788009643555 -2.16799926758,-0.74299621582 -2.95600891113,0.102005004883 c -0.359985351562,0.384994506836 -0.561996459961,0.891998291016 -0.56298828125,1.41899108887 c 0.0,0.0 0.0,12.8800048828 0.0,12.8800048828 c 0.0,0.0 -8.10000610352,-8.10000610352 -8.10000610352,-8.10000610352 c -0.81999206543,-0.81999206543 -2.12100219727,-0.81999206543 -2.9409942627,0.0 c -0.819000244141,0.819000244141 -0.819000244141,2.12001037598 0.0,2.94000244141 c 0.0,0.0 10.1799926758,10.1999969482 10.1799926758,10.1999969482 c 0.0,0.0 -10.1799926758,10.1790008545 -10.1799926758,10.1790008545 c -0.819000244141,0.820999145508 -0.819000244141,2.12100219727 0.0,2.94100952148 c 0.81999206543,0.81999206543 2.12100219727,0.81999206543 2.9409942627,0.0 c 0.0,0.0 8.10000610352,-8.1009979248 8.10000610352,-8.1009979248 c 0.0,0.0 0.0,12.9009857178 0.0,12.9009857178 c 0.0,1.14801025391 0.929992675781,2.08000183105 2.08000183105,2.08000183105 c 0.526992797852,0.0 1.03399658203,-0.199996948242 1.41999816895,-0.559997558594 c 0.0,0.0 0.0989990234375,-0.100006103516 0.0989990234375,-0.100006103516 c 0.0,0.0 8.91999816895,-8.91999816895 8.91999816895,-8.91999816895 c 0.81999206543,-0.820999145508 0.81999206543,-2.13999938965 0.0,-2.95999145508 c 0.0,0.0 -7.5,-7.46000671387 -7.5,-7.46000671387 Z M 16.0899963379,15.8190002441 c 0.0,0.0 0.0,-8.59999084473 0.0,-8.59999084473 c 0.0,0.0 4.30000305176,4.30000305176 4.30000305176,4.30000305176 c 0.0,0.0 -4.30000305176,4.29998779297 -4.30000305176,4.29998779297 Z M 16.0899963379,33.7190093994 c 0.0,0.0 0.0,-8.6009979248 0.0,-8.6009979248 c 0.0,0.0 4.30000305176,4.30099487305 4.30000305176,4.30099487305 c 0.0,0.0 -4.30000305176,4.30000305176 -4.30000305176,4.30000305176 Z"
-                        android:fillColor="#FFFFFFFF" />
-                </group>
-            </group>
-            <group
-                android:name="dot_left_outlines"
-                android:translateX="20.6501"
-                android:translateY="24.00011" >
-                <group
-                    android:name="dot_left_outlines_pivot"
-                    android:translateX="-14.2"
-                    android:translateY="-3.55" >
-                    <path
-                        android:name="dot_left"
-                        android:pathData="M 5.66999816895,5.66999816895 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.03999328613 -0.0199890136719,-4.2200012207 c 0.0,0.0 0.0199890136719,-0.0200042724609 0.0199890136719,-0.0200042724609 c 1.16000366211,-1.17999267578 3.04000854492,-1.17999267578 4.2200012207,-0.0199890136719 c 0.0,0.0 0.0200042724609,0.0199890136719 0.0200042724609,0.0199890136719 c 1.17999267578,1.17900085449 1.17999267578,3.06001281738 0.0,4.24000549316 Z"
-                        android:fillColor="#FFFFFFFF"
-                        android:fillAlpha="0.5" />
-                </group>
-            </group>
-            <group
-                android:name="dot_right_outlines"
-                android:translateX="27.3501"
-                android:translateY="23.99741" >
-                <group
-                    android:name="dot_right_outlines_pivot"
-                    android:translateX="7.1"
-                    android:translateY="-3.5525" >
-                    <path
-                        android:name="dot_right"
-                        android:pathData="M 5.66999816895,1.43499755859 c 1.17999267578,1.18000793457 1.17999267578,3.08000183105 0.0,4.24000549316 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.04000854492 -0.0200042724609,-4.21899414062 c 0.0,0.0 0.0200042724609,-0.0210113525391 0.0200042724609,-0.0210113525391 c 1.15299987793,-1.17098999023 3.03799438477,-1.18499755859 4.20899963379,-0.0309906005859 c 0.0,0.0 0.031005859375,0.0309906005859 0.031005859375,0.0309906005859 Z"
-                        android:fillColor="#FFFFFFFF" />
-                </group>
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml
deleted file mode 100644
index dc3c3e2..0000000
--- a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/ic_bluetooth_transient" >
-    <target
-        android:name="dot_left"
-        android:animation="@anim/ic_bluetooth_transient_dot_left_animation" />
-    <target
-        android:name="dot_right"
-        android:animation="@anim/ic_bluetooth_transient_dot_right_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient.xml
deleted file mode 100644
index 22f7267..0000000
--- a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_hotspot_transient"
-    android:width="48dp"
-    android:viewportWidth="48"
-    android:height="48dp"
-    android:viewportHeight="48" >
-    <group
-        android:name="ic_hotspot_transient_0"
-        android:translateX="24.00209"
-        android:translateY="24.2354" >
-        <group
-            android:name="ic_hotspot_transient_pivot"
-            android:translateX="-24.00209"
-            android:translateY="-24.2354" >
-            <group
-                android:name="circle01"
-                android:translateX="24.0002"
-                android:translateY="25.9996" >
-                <group
-                    android:name="circle01_pivot"
-                    android:translateX="-24.0002"
-                    android:translateY="-25.9996" >
-                    <path
-                        android:name="circle01_0"
-                        android:pathData="M 24.0001983643,21.9996032715 c -2.19999694824,0.0 -4.0,1.80000305176 -4.0,4.0 c 0.0,2.20100402832 1.80000305176,4.0 4.0,4.0 c 2.19999694824,0.0 4.0,-1.79899597168 4.0,-4.0 c 0.0,-2.19999694824 -1.80000305176,-4.0 -4.0,-4.0 Z"
-                        android:fillColor="#FFFFFFFF" />
-                </group>
-            </group>
-            <group
-                android:name="circle02"
-                android:translateX="24.00071"
-                android:translateY="24.74686" >
-                <group
-                    android:name="circle02_pivot"
-                    android:translateX="-24.00071"
-                    android:translateY="-24.74686" >
-                    <path
-                        android:name="circle02_0"
-                        android:pathData="M 36.0001983643,25.9996032715 c -0.00299072265625,-6.62699890137 -5.37899780273,-11.9969940186 -12.0059967041,-11.9940032959 c -0.5,0.0 -0.999008178711,0.031005859375 -1.4940032959,0.0940093994141 c -5.24000549316,0.640991210938 -9.55999755859,4.81999206543 -10.3600006104,10.0399932861 c -0.639999389648,4.2799987793 0.979995727539,8.22099304199 3.83999633789,10.7799987793 c 0.960006713867,0.86100769043 2.48001098633,0.660003662109 3.1190032959,-0.460006713867 c 0.481002807617,-0.839996337891 0.281005859375,-1.87998962402 -0.438995361328,-2.51899719238 c -2.21299743652,-1.97099304199 -3.15200805664,-5.00500488281 -2.44000244141,-7.8809967041 c 0.695007324219,-2.86999511719 2.93099975586,-5.11500549316 5.79899597168,-5.81900024414 c 4.29100036621,-1.08599853516 8.65000915527,1.51100158691 9.73600769043,5.80200195312 c 0.162002563477,0.639999389648 0.244003295898,1.29699707031 0.244995117188,1.95700073242 c 0.0,2.36099243164 -1.02000427246,4.45999145508 -2.66000366211,5.91999816895 c -0.720001220703,0.660003662109 -0.939987182617,1.69999694824 -0.459991455078,2.53999328613 c 0.619995117188,1.08000183105 2.08000183105,1.38000488281 3.0,0.560012817383 c 2.61599731445,-2.26699829102 4.1190032959,-5.55801391602 4.11999511719,-9.02000427246 Z"
-                        android:fillColor="#FFFFFFFF" />
-                </group>
-            </group>
-            <group
-                android:name="circle03"
-                android:translateX="24.00209"
-                android:translateY="24.23539" >
-                <group
-                    android:name="circle03_pivot"
-                    android:translateX="-24.00209"
-                    android:translateY="-24.23539" >
-                    <path
-                        android:name="circle03_0"
-                        android:pathData="M 21.6602020264,6.13960266113 c -9.24000549316,1.03999328613 -16.6999969482,8.66000366211 -17.5599975586,17.9199981689 c -0.690002441406,7.00500488281 2.36599731445,13.8540039062 8.03999328613,18.0200042725 c 0.958999633789,0.700988769531 2.32000732422,0.401000976562 2.91900634766,-0.620010375977 c 0.5,-0.860000610352 0.280990600586,-1.97898864746 -0.518997192383,-2.57998657227 c -4.56001281738,-3.38000488281 -7.30000305176,-9.09901428223 -6.32000732422,-15.3990020752 c 1.08000183105,-7.0 6.91900634766,-12.5800018311 13.9600067139,-13.3610076904 c 9.63999938965,-1.09999084473 17.8199920654,6.44000244141 17.8199920654,15.8800048828 c 0.0,5.30000305176 -2.57998657227,9.95999145508 -6.53999328613,12.8800048828 c -0.800003051758,0.600997924805 -1.02000427246,1.69999694824 -0.520004272461,2.57998657227 c 0.600006103516,1.04000854492 1.96000671387,1.32099914551 2.91999816895,0.620010375977 c 5.12100219727,-3.75500488281 8.14500427246,-9.72799682617 8.13999938965,-16.0800018311 c 0.0,-11.8200073242 -10.2599945068,-21.2400054932 -22.3399963379,-19.8600006104 Z"
-                        android:fillColor="#FFFFFFFF" />
-                </group>
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml
deleted file mode 100644
index 929a941..0000000
--- a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/ic_hotspot_transient" >
-    <target
-        android:name="circle01_0"
-        android:animation="@anim/ic_hotspot_transient_circle_1_animation" />
-    <target
-        android:name="circle02_0"
-        android:animation="@anim/ic_hotspot_transient_circle_2_animation" />
-    <target
-        android:name="circle03_0"
-        android:animation="@anim/ic_hotspot_transient_circle_3_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml
deleted file mode 100644
index 880e1bb..0000000
--- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_signal_wifi_transient"
-    android:width="48dp"
-    android:viewportWidth="48"
-    android:height="48dp"
-    android:viewportHeight="48" >
-    <group
-        android:name="ic_signal_wifi_4_bar_48px_2"
-        android:translateX="24.25"
-        android:translateY="25.73401" >
-        <group
-            android:name="ic_signal_wifi_4_bar_48px_2_pivot"
-            android:translateX="-23.21545"
-            android:translateY="-18.86649" >
-            <group
-                android:name="wifi_2"
-                android:translateX="23.481"
-                android:translateY="18.71151" >
-                <path
-                    android:name="wifi"
-                    android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-                    android:fillColor="#FFFFFFFF"
-                    android:fillAlpha="0.5" />
-            </group>
-            <group
-                android:name="wifi_0"
-                android:translateX="23.481"
-                android:translateY="18.71151" >
-                <path
-                    android:name="wifi_1"
-                    android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
-                    android:fillColor="#FFFFFFFF"
-                    android:fillAlpha="0" />
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml
deleted file mode 100644
index 9f5aaeb..0000000
--- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/ic_signal_wifi_transient" >
-    <target
-        android:name="wifi_1"
-        android:animation="@anim/ic_signal_wifi_transient_wifi_1_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml b/packages/SystemUI/res/drawable/ic_sysbar_menu.xml
deleted file mode 100644
index d53c95b..0000000
--- a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="21dp"
-    android:height="21dp"
-    android:viewportWidth="28"
-    android:viewportHeight="28">
-
-    <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M14,9.5c1.24,0,2.25-1.01,2.25-2.25S15.24,5,14,5s-2.25,1.01-2.25,2.25S12.76,9.5,14,9.5z M14,11.75 c-1.24,0-2.25,1.01-2.25,2.25s1.01,2.25,2.25,2.25s2.25-1.01,2.25-2.25S15.24,11.75,14,11.75z M14,18.5 c-1.24,0-2.25,1.01-2.25,2.25S12.76,23,14,23s2.25-1.01,2.25-2.25S15.24,18.5,14,18.5z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse.xml b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
deleted file mode 100644
index 3853d12..0000000
--- a/packages/SystemUI/res/drawable/ic_volume_collapse.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_caret_down"
-    android:width="24dp"
-    android:viewportWidth="24"
-    android:height="24dp"
-    android:viewportHeight="24"
-    android:tint="?android:attr/colorControlNormal" >
-    <group
-        android:name="caret___4" >
-        <group
-            android:name="right"
-            android:translateX="11.287"
-            android:translateY="8.701"
-            android:rotation="45" >
-            <group
-                android:name="right_pivot"
-                android:translateX="4.242" >
-                <path
-                    android:name="rectangle_path_1"
-                    android:fillColor="#FFFFFFFF"
-                    android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
-            </group>
-        </group>
-        <group
-            android:name="left"
-            android:translateX="12.699"
-            android:translateY="8.701"
-            android:rotation="-45" >
-            <group
-                android:name="left_pivot"
-                android:translateX="-4.242" >
-                <path
-                    android:name="rectangle_path_2"
-                    android:fillColor="#FFFFFFFF"
-                    android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
index 18c6307..101a0c8 100644
--- a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
@@ -13,13 +13,223 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/ic_volume_collapse" >
-    <target
-        android:name="right"
-        android:animation="@anim/ic_caret_down_right_animation" />
-    <target
-        android:name="left"
-        android:animation="@anim/ic_caret_down_left_animation" />
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="24dp" android:width="24dp" android:viewportHeight="24"
+                android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12"
+                       android:translateY="12">
+                    <group android:name="_R_G_L_1_G_T_1" android:translateX="-0.004"
+                           android:translateY="-3.608" android:rotation="0">
+                        <group android:name="_R_G_L_1_G" android:translateX="-0.246"
+                               android:translateY="-1.642">
+                            <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                                  android:fillAlpha="1" android:fillType="nonZero"
+                                  android:pathData=" M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "/>
+                        </group>
+                    </group>
+                </group>
+                <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+                       android:translateY="12">
+                    <group android:name="_R_G_L_0_G_T_1" android:translateX="0"
+                           android:translateY="-3.585" android:rotation="0">
+                        <group android:name="_R_G_L_0_G" android:translateX="-8.25"
+                               android:translateY="-1.665">
+                            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                                  android:fillAlpha="1" android:fillType="nonZero"
+                                  android:pathData=" M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "/>
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData" android:duration="50"
+                                android:startOffset="0"
+                                android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+                                android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="33"
+                                android:startOffset="50"
+                                android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+                                android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="83"
+                                android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c "
+                                android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="33"
+                                android:startOffset="100"
+                                android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c "
+                                android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="133"
+                                android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c "
+                                android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateXY" android:duration="250"
+                                android:startOffset="0" android:propertyXName="translateX"
+                                android:propertyYName="translateY"
+                                android:pathData="M -0.004,-3.608C -0.004,-2.39758332538605 -0.004,2.44358332538605 -0.004,3.654">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="rotation" android:duration="200"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="-90"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData" android:duration="50"
+                                android:startOffset="0"
+                                android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+                                android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="50"
+                                android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c "
+                                android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="67"
+                                android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+                                android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="83"
+                                android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c "
+                                android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="33"
+                                android:startOffset="100"
+                                android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c "
+                                android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="133"
+                                android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+                                android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateXY" android:duration="250"
+                                android:startOffset="0" android:propertyXName="translateX"
+                                android:propertyYName="translateY"
+                                android:pathData="M 0,-3.585C 0,-2.3787500476837202 0,2.4457500476837204 0,3.652">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="rotation" android:duration="200"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="90"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX" android:duration="267"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
 </animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand.xml b/packages/SystemUI/res/drawable/ic_volume_expand.xml
deleted file mode 100644
index 79ff808..0000000
--- a/packages/SystemUI/res/drawable/ic_volume_expand.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_caret_up"
-    android:width="24dp"
-    android:viewportWidth="24"
-    android:height="24dp"
-    android:viewportHeight="24"
-    android:tint="?android:attr/colorControlNormal" >
-    <group
-        android:name="caret___4" >
-        <group
-            android:name="right"
-            android:translateX="11.287"
-            android:translateY="15.287"
-            android:rotation="-45" >
-            <group
-                android:name="right_pivot"
-                android:translateX="4.242" >
-                <path
-                    android:name="rectangle_path_1"
-                    android:fillColor="#FFFFFFFF"
-                    android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
-            </group>
-        </group>
-        <group
-            android:name="left"
-            android:translateX="12.699"
-            android:translateY="15.287"
-            android:rotation="45" >
-            <group
-                android:name="left_pivot"
-                android:translateX="-4.242" >
-                <path
-                    android:name="rectangle_path_2"
-                    android:fillColor="#FFFFFFFF"
-                    android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
index abd6678..152d26c 100644
--- a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
@@ -13,13 +13,233 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/ic_volume_expand" >
-    <target
-        android:name="right"
-        android:animation="@anim/ic_caret_up_right_animation" />
-    <target
-        android:name="left"
-        android:animation="@anim/ic_caret_up_left_animation" />
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="24dp" android:width="24dp" android:viewportHeight="24"
+                android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12"
+                       android:translateY="12">
+                    <group android:name="_R_G_L_1_G_T_1" android:translateX="0"
+                           android:translateY="3.439" android:rotation="0">
+                        <group android:name="_R_G_L_1_G" android:translateX="-8.25"
+                               android:translateY="-8.1">
+                            <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                                  android:fillAlpha="1" android:fillType="nonZero"
+                                  android:pathData=" M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c "/>
+                        </group>
+                    </group>
+                </group>
+                <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+                       android:translateY="12">
+                    <group android:name="_R_G_L_0_G_T_1" android:translateX="0"
+                           android:translateY="3.439" android:rotation="0">
+                        <group android:name="_R_G_L_0_G" android:translateX="-0.25"
+                               android:translateY="-8.1">
+                            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                                  android:fillAlpha="1" android:fillType="nonZero"
+                                  android:pathData=" M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.25,9.66 0.25,9.66 C0.25,9.66 0.25,6.83 0.25,6.83 C0.25,6.83 6.84,0.25 6.84,0.25c "/>
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData" android:duration="33"
+                                android:startOffset="0"
+                                android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c "
+                                android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="33"
+                                android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+                                android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="50"
+                                android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+                                android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="67"
+                                android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c "
+                                android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="83"
+                                android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c "
+                                android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="50"
+                                android:startOffset="100"
+                                android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c "
+                                android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.95,9.61 7.95,9.61 C7.95,9.61 9.58,8.01 9.58,8.01 C9.58,8.01 1.66,0.25 1.66,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateXY" android:duration="250"
+                                android:startOffset="0" android:propertyXName="translateX"
+                                android:propertyYName="translateY"
+                                android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="rotation" android:duration="200"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="-90"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="0"
+                                android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.24,9.63 0.24,9.63 C0.24,9.63 -0.15,7.2 -0.15,7.2 C-0.15,7.2 6.84,0.25 6.84,0.25c "
+                                android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="17"
+                                android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c "
+                                android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="33"
+                                android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c "
+                                android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="33"
+                                android:startOffset="50"
+                                android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c "
+                                android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="17"
+                                android:startOffset="83"
+                                android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c "
+                                android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData" android:duration="117"
+                                android:startOffset="100"
+                                android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c "
+                                android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.56,9.63 0.56,9.63 C0.56,9.63 -1.08,8.07 -1.08,8.07 C-1.08,8.07 6.84,0.25 6.84,0.25c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateXY" android:duration="250"
+                                android:startOffset="0" android:propertyXName="translateX"
+                                android:propertyYName="translateY"
+                                android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="rotation" android:duration="200"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="90"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX" android:duration="267"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
 </animated-vector>
diff --git a/packages/SystemUI/res/layout-land/global_actions_column.xml b/packages/SystemUI/res/layout-land/global_actions_column.xml
new file mode 100644
index 0000000..99a4e13
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_column.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="center_horizontal | top"
+    android:clipChildren="false"
+>
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="0dp"
+        android:orientation="horizontal"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+    >
+        <!-- Grid of action items -->
+        <LinearLayout
+            android:id="@android:id/list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        />
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="horizontal"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
new file mode 100644
index 0000000..0f86131
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="center_horizontal | bottom"
+    android:clipChildren="false"
+>
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="0dp"
+        android:orientation="horizontal"
+    >
+        <!-- Grid of action items -->
+        <com.android.systemui.globalactions.ListGridLayout
+            android:id="@android:id/list"
+            android:layout_gravity="bottom|left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        />
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_gravity="top|left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="horizontal"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index 65ede3d..b734125 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -33,13 +33,15 @@
         android:layout_height="wrap_content"
         android:animateLayoutChanges="true">
 
-        <ImageView
+        <com.android.systemui.statusbar.AlphaOptimizedButton
+            style="@android:style/Widget.Material.Button.Borderless"
             android:id="@+id/settings_button"
-            android:layout_width="@dimen/bubble_header_icon_size"
-            android:layout_height="@dimen/bubble_header_icon_size"
-            android:src="@drawable/ic_settings"
-            android:scaleType="center"
-            android:layout_gravity="end"/>
+            android:layout_gravity="end"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:text="@string/manage_bubbles_text"
+            android:textColor="?attr/wallpaperTextColor"/>
 
         <include layout="@layout/bubble_permission_view"
                  android:id="@+id/permission_layout"
diff --git a/packages/SystemUI/res/layout/global_actions_column.xml b/packages/SystemUI/res/layout/global_actions_column.xml
new file mode 100644
index 0000000..b58146b
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_column.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="center_vertical | right"
+    android:clipChildren="false"
+>
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:gravity="top | right"
+        android:orientation="vertical"
+        android:padding="0dp"
+        android:layout_marginTop="@dimen/global_actions_grid_container_bottom_margin"
+        android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
+    >
+        <!-- Global actions is right-aligned to be physically near power button -->
+        <LinearLayout
+            android:id="@android:id/list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:gravity="right"
+            android:translationZ="@dimen/global_actions_translate"
+            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+        />
+
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="vertical"
+            android:gravity="center"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index f9bf47b..89d1a19 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -37,16 +37,17 @@
     <color name="notification_ripple_untinted_color">#30ffffff</color>
 
     <!-- The "inside" of a notification, reached via longpress -->
-    <color name="notification_guts_bg_color">@*android:color/notification_material_background_color</color>
+    <color name="notification_guts_bg_color">@color/GM2_grey_900</color>
 
     <!-- The color of the text inside a notification -->
     <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
 
-    <color name="notification_guts_selection_bg">#202124</color>
-    <color name="notification_guts_selection_border">#669DF6</color>
-    <color name="notification_guts_link_icon_tint">@color/GM2_grey_200</color>
+    <color name="notification_guts_selection_bg">@color/GM2_grey_800</color>
+    <color name="notification_guts_selection_border">@color/GM2_grey_700</color>
+    <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
     <color name="notification_guts_sub_text_color">@color/GM2_grey_200</color>
-    <color name="notification_guts_header_text_color">@color/GM2_grey_100</color>
+    <color name="notification_guts_header_text_color">@color/GM2_grey_200</color>
+    <color name="notification_guts_button_color">@color/GM2_blue_200</color>
 
     <!-- The color of the background in the top part of QSCustomizer -->
     <color name="qs_customize_background">@color/GM2_grey_900</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4e1a7d0..b673e52 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -95,6 +95,7 @@
     <color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
     <color name="notification_silence_color">#FF32c1de</color>
     <color name="notification_alert_color">#FFF87B2B</color>
+    <color name="notification_guts_button_color">@color/GM2_blue_700</color>
 
     <color name="assist_orb_color">#ffffff</color>
 
@@ -177,6 +178,9 @@
     <color name="GM2_red_300">#F28B82</color>
     <color name="GM2_red_500">#B71C1C</color>
 
+    <color name="GM2_blue_200">#AECBFA</color>
+    <color name="GM2_blue_700">#1967D2</color>
+
     <color name="GM2_yellow_500">#FFFBBC04</color>
     <color name="GM2_green_500">#FF34A853</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4abe9f0..2871d06 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -494,4 +494,7 @@
     <!-- Launcher package name for overlaying icons. -->
     <string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string>
 
+    <!-- ThemePicker package name for overlaying icons. -->
+    <string name="themepicker_overlayable_package" translatable="false">com.android.wallpaper</string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ce958ab..bb7b663 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -591,6 +591,8 @@
     <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
     <!-- Minimum margin between clock and status bar -->
     <dimen name="keyguard_clock_top_margin">36dp</dimen>
+    <!-- The margin between top of clock and bottom of lock icon. -->
+    <dimen name="keyguard_clock_lock_margin">16dp</dimen>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 501b1b5..f75f255 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -108,6 +108,8 @@
 
     <item type="id" name="display_cutout" />
 
+    <item type="id" name="row_tag_for_content_view" />
+
     <!-- Optional cancel button on Keyguard -->
     <item type="id" name="cancel_button"/>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cea336c..dc35653 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2414,6 +2414,8 @@
     <!-- Text for asking the user whether bubbles (floating app content) should be enabled for an
          app. [CHAR LIMIT=NONE] -->
     <string name="bubbles_prompt">Allow bubbles from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+    <!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] -->
+    <string name="manage_bubbles_text">Manage</string>
     <!-- Text used for button allowing user to opt out of bubbles [CHAR LIMIT=20] -->
     <string name="no_bubbles">Deny</string>
     <!-- Text used for button allowing user to approve / enable bubbles [CHAR LIMIT=20] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 23de2ac..aa89dce 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -452,7 +452,7 @@
     <style name="TextAppearance.NotificationInfo.Button">
         <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
         <item name="android:textSize">16sp</item>
-        <item name="android:textColor">?android:attr/colorAccent</item>
+        <item name="android:textColor">@color/notification_guts_button_color</item>
         <item name="android:background">@drawable/btn_borderless_rect</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:focusable">true</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 40d98c1..f384507 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -151,19 +151,34 @@
         return plugins.size() != 0;
     }
 
-    private void disable(PluginInfo info,
-            @PluginEnabler.DisableReason int reason) {
+    private boolean isPluginWhitelisted(ComponentName pluginName) {
+        for (String componentNameOrPackage : mWhitelistedPlugins) {
+            ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
+            if (componentName == null) {
+                if (componentNameOrPackage.equals(pluginName.getPackageName())) {
+                    return true;
+                }
+            } else {
+                if (componentName.equals(pluginName)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void disable(PluginInfo info, @PluginEnabler.DisableReason int reason) {
         // Live by the sword, die by the sword.
         // Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
 
+        ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
         // If a plugin is detected in the stack of a crash then this will be called for that
         // plugin, if the plugin causing a crash cannot be identified, they are all disabled
         // assuming one of them must be bad.
-        if (mWhitelistedPlugins.contains(info.mPackage)) {
+        if (isPluginWhitelisted(pluginComponent)) {
             // Don't disable whitelisted plugins as they are a part of the OS.
             return;
         }
-        ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
         Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
         mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
     }
@@ -288,6 +303,13 @@
             if (result.size() > 1 && !mAllowMultiple) {
                 // TODO: Show warning.
                 Log.w(TAG, "Multiple plugins found for " + mAction);
+                if (DEBUG) {
+                    for (ResolveInfo info : result) {
+                        ComponentName name = new ComponentName(info.serviceInfo.packageName,
+                                info.serviceInfo.name);
+                        Log.w(TAG, "  " + name);
+                    }
+                }
                 return;
             }
             for (ResolveInfo info : result) {
@@ -305,7 +327,7 @@
         protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
             // This was already checked, but do it again here to make extra extra sure, we don't
             // use these on production builds.
-            if (!isDebuggable && !mWhitelistedPlugins.contains(component.getPackageName())) {
+            if (!isDebuggable && !isPluginWhitelisted(component)) {
                 // Never ever ever allow these on production builds, they are only for prototyping.
                 Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
                 return null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 6e3eb7c..d250acc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -70,6 +70,15 @@
 
     public void removeListener(TaskStackChangeListener listener) {
         mTaskStackListeners.remove(listener);
+        if (mTaskStackListeners.isEmpty() && mRegistered) {
+            // Unregister mTaskStackListener once we have no more listeners
+            try {
+                ActivityTaskManager.getService().unregisterTaskStackListener(this);
+                mRegistered = false;
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
+            }
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
index eec1d70..f468eca 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
@@ -21,7 +21,6 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.Paint.Style;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextClock;
@@ -96,11 +95,8 @@
 
         mView = mLayoutInflater.inflate(R.layout.digital_clock, null);
         mLockClock = mView.findViewById(R.id.lock_screen_clock);
-        final int textSize = mResources.getDimensionPixelSize(R.dimen.widget_title_font_size);
-        mLockClock.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
     }
 
-
     @Override
     public void onDestroyView() {
         mBigClockView = null;
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index 5fec61b..61a6952 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -21,7 +21,6 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.Paint.Style;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextClock;
@@ -96,8 +95,6 @@
 
         mLockClockContainer = mLayoutInflater.inflate(R.layout.digital_clock, null);
         mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock);
-        final int textSize = mResources.getDimensionPixelSize(R.dimen.widget_title_font_size);
-        mLockClock.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 07c2f10..9f4c403e 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -32,7 +32,6 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.Observer;
 
-import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManager.DockEventListener;
@@ -89,6 +88,7 @@
     private final Observer<Integer> mCurrentUserObserver = (newUserId) -> reload();
 
     private final PluginManager mPluginManager;
+    @Nullable private final DockManager mDockManager;
 
     /**
      * Observe changes to dock state to know when to switch the clock face.
@@ -102,7 +102,6 @@
                     reload();
                 }
             };
-    @Nullable private DockManager mDockManager;
 
     /**
      * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
@@ -125,21 +124,24 @@
 
     @Inject
     public ClockManager(Context context, InjectionInflationController injectionInflater,
-            PluginManager pluginManager, SysuiColorExtractor colorExtractor) {
+            PluginManager pluginManager, SysuiColorExtractor colorExtractor,
+            @Nullable DockManager dockManager) {
         this(context, injectionInflater, pluginManager, colorExtractor,
                 context.getContentResolver(), new CurrentUserObservable(context),
-                new SettingsWrapper(context.getContentResolver()));
+                new SettingsWrapper(context.getContentResolver()), dockManager);
     }
 
+    @VisibleForTesting
     ClockManager(Context context, InjectionInflationController injectionInflater,
             PluginManager pluginManager, SysuiColorExtractor colorExtractor,
             ContentResolver contentResolver, CurrentUserObservable currentUserObservable,
-            SettingsWrapper settingsWrapper) {
+            SettingsWrapper settingsWrapper, DockManager dockManager) {
         mContext = context;
         mPluginManager = pluginManager;
         mContentResolver = contentResolver;
         mSettingsWrapper = settingsWrapper;
         mCurrentUserObservable = currentUserObservable;
+        mDockManager = dockManager;
         mPreviewClocks = new AvailableClocks();
 
         Resources res = context.getResources();
@@ -223,9 +225,6 @@
                 Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
                 false, mContentObserver, UserHandle.USER_ALL);
         mCurrentUserObservable.getCurrentUser().observeForever(mCurrentUserObserver);
-        if (mDockManager == null) {
-            mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class);
-        }
         if (mDockManager != null) {
             mDockManager.addListener(mDockEventListener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 04f887b..41a7bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -16,6 +16,7 @@
 
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.view.View;
 
 import com.android.systemui.plugins.ActivityStarter;
 
@@ -53,6 +54,16 @@
     }
 
     @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentCallback, View associatedView) {
+        if (mActualStarter == null) {
+            return;
+        }
+        mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback,
+                associatedView);
+    }
+
+    @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
             int flags) {
         if (mActualStarter == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 802903d..ad2e002 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -80,16 +80,6 @@
     }
 
     @Override
-    public void removeAllItems() {
-        if (mList != null) {
-            mList.removeAllViews();
-        }
-        if (mSeparatedView != null) {
-            mSeparatedView.removeAllViews();
-        }
-    }
-
-    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         updateSettings();
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index f8287a4..d153fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -45,11 +45,6 @@
     protected abstract ViewGroup getListView();
 
     /**
-     * Removes all child items from the separated and list views, if they exist.
-     */
-    protected abstract void removeAllItems();
-
-    /**
      * Sets the divided view, which may have a differently-colored background.
      */
     public abstract void setDivisionView(View v);
@@ -110,6 +105,25 @@
         onUpdateList();
     }
 
+    protected void removeAllSeparatedViews() {
+        ViewGroup separated = getSeparatedView();
+        if (separated != null) {
+            separated.removeAllViews();
+        }
+    }
+
+    protected void removeAllListViews() {
+        ViewGroup list = getListView();
+        if (list != null) {
+            list.removeAllViews();
+        }
+    }
+
+    protected void removeAllItems() {
+        removeAllListViews();
+        removeAllSeparatedViews();
+    }
+
     protected void onUpdateList() {
         removeAllItems();
         setSeparatedViewVisibility(mAdapter.hasSeparatedItems());
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index d071363..9ecc6f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.bubbles;
 
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.View.INVISIBLE;
@@ -53,13 +57,13 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
@@ -210,6 +214,7 @@
 
         mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
+        mNotificationEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
 
         mStatusBarWindowController = statusBarWindowController;
         mStatusBarStateListener = new StatusBarStateListener();
@@ -389,6 +394,46 @@
     }
 
     @SuppressWarnings("FieldCanBeLocal")
+    private final NotificationRemoveInterceptor mRemoveInterceptor =
+            new NotificationRemoveInterceptor() {
+            @Override
+            public boolean onNotificationRemoveRequested(String key, int reason) {
+                if (!mBubbleData.hasBubbleWithKey(key)) {
+                    return false;
+                }
+                NotificationEntry entry = mBubbleData.getBubbleWithKey(key).entry;
+
+                final boolean isClearAll = reason == REASON_CANCEL_ALL;
+                final boolean isUserDimiss = reason == REASON_CANCEL;
+                final boolean isAppCancel = reason == REASON_APP_CANCEL
+                        || reason == REASON_APP_CANCEL_ALL;
+
+                // Need to check for !appCancel here because the notification may have
+                // previously been dismissed & entry.isRowDismissed would still be true
+                boolean userRemovedNotif = (entry.isRowDismissed() && !isAppCancel)
+                        || isClearAll || isUserDimiss;
+
+                // The bubble notification sticks around in the data as long as the bubble is
+                // not dismissed and the app hasn't cancelled the notification.
+                boolean bubbleExtended = entry.isBubble() && !entry.isBubbleDismissed()
+                        && userRemovedNotif;
+                if (bubbleExtended) {
+                    entry.setShowInShadeWhenBubble(false);
+                    if (mStackView != null) {
+                        mStackView.updateDotVisibility(entry.key);
+                    }
+                    mNotificationEntryManager.updateNotifications();
+                    return true;
+                } else if (!userRemovedNotif && !entry.isBubbleDismissed()) {
+                    // This wasn't a user removal so we should remove the bubble as well
+                    mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
+                    return false;
+                }
+                return false;
+            }
+        };
+
+    @SuppressWarnings("FieldCanBeLocal")
     private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
@@ -396,7 +441,6 @@
                 return;
             }
             if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
-                // TODO: handle group summaries?
                 updateShowInShadeForSuppressNotification(entry);
             }
         }
@@ -426,23 +470,6 @@
                 updateBubble(entry);
             }
         }
-
-        @Override
-        public void onEntryRemoved(NotificationEntry entry,
-                @Nullable NotificationVisibility visibility,
-                boolean removedByUser) {
-            if (!areBubblesEnabled(mContext)) {
-                return;
-            }
-            entry.setShowInShadeWhenBubble(false);
-            if (mStackView != null) {
-                mStackView.updateDotVisibility(entry.key);
-            }
-            if (!removedByUser) {
-                // This was a cancel so we should remove the bubble
-                removeBubble(entry.key, DISMISS_NOTIF_CANCEL);
-            }
-        }
     };
 
     @SuppressWarnings("FieldCanBeLocal")
@@ -455,13 +482,15 @@
         }
 
         @Override
-        public void onBubbleRemoved(Bubble bubble, int reason) {
+        public void onBubbleRemoved(Bubble bubble, @DismissReason int reason) {
             if (mStackView != null) {
                 mStackView.removeBubble(bubble);
             }
-            if (!bubble.entry.showInShadeWhenBubble()) {
-                // The notification is gone & bubble is gone, time to actually remove it
-                mNotificationEntryManager.performRemoveNotification(bubble.entry.notification);
+            if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
+                    && !bubble.entry.showInShadeWhenBubble()) {
+                // The bubble is gone & the notification is gone, time to actually remove it
+                mNotificationEntryManager.performRemoveNotification(bubble.entry.notification,
+                        0 /* reason */);
             } else {
                 // The notification is still in the shade but we've removed the bubble so
                 // lets make sure NoMan knows it's not a bubble anymore
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 38ba91e..f15ba6e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -354,7 +354,7 @@
             return false;
         }
         if (mExpanded && bubble != null) {
-            mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
+            bubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
         }
         mSelectedBubble = bubble;
         dispatchOnSelectionChanged(mSelectedBubble);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 346660d..67f3274 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -40,11 +40,8 @@
 import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Point;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -66,6 +63,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
+import com.android.systemui.statusbar.AlphaOptimizedButton;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -80,7 +78,7 @@
     private View mPointerView;
     private int mPointerMargin;
 
-    private ImageView mSettingsIcon;
+    private AlphaOptimizedButton mSettingsIcon;
 
     // Permission view
     private View mPermissionView;
@@ -100,8 +98,6 @@
     private int mSettingsIconHeight;
     private int mBubbleHeight;
     private int mPermissionHeight;
-    private int mIconInset;
-    private Drawable mSettingsIconDrawable;
     private int mPointerWidth;
     private int mPointerHeight;
 
@@ -218,10 +214,7 @@
         mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
                 R.dimen.bubble_expanded_header_height);
         mSettingsIcon = findViewById(R.id.settings_button);
-        mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
         mSettingsIcon.setOnClickListener(this);
-        // Save initial drawable to create adaptive icons that will take its place.
-        mSettingsIconDrawable = mSettingsIcon.getDrawable();
 
         mPermissionHeight = getContext().getResources().getDimensionPixelSize(
                 R.dimen.bubble_permission_height);
@@ -377,16 +370,6 @@
         int foregroundColor = ta.getColor(1, Color.BLACK /* default */);
         ta.recycle();
 
-        // Must clear tint first - otherwise tint updates inconsistently.
-        mSettingsIconDrawable.setTintList(null);
-        mSettingsIconDrawable.setTint(foregroundColor);
-
-        InsetDrawable foreground = new InsetDrawable(mSettingsIconDrawable, mIconInset);
-        ColorDrawable background = new ColorDrawable(backgroundColor);
-        AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(background,
-                foreground);
-        mSettingsIcon.setImageDrawable(adaptiveIcon);
-
         // Update permission prompt color.
         mPermissionView.setBackground(createPermissionBackground(backgroundColor));
         mPermissionPrompt.setTextColor(foregroundColor);
@@ -649,11 +632,12 @@
     }
 
     private Intent getSettingsIntent(String packageName, final int appUid) {
-        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
         intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
         intent.putExtra(Settings.EXTRA_APP_UID, appUid);
         intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         return intent;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index a63fdbd..d4e7bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -32,6 +32,7 @@
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Dependency;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.analytics.DataCollector;
@@ -62,6 +63,8 @@
             Sensor.TYPE_LIGHT,
             Sensor.TYPE_ROTATION_VECTOR,
     };
+    private static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts";
+    private static final String FALSING_SUCCESS = "falsing_success_after_attempts";
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Context mContext;
@@ -83,6 +86,8 @@
     private boolean mScreenOn;
     private boolean mShowingAod;
     private Runnable mPendingWtf;
+    private int mIsFalseTouchCalls;
+    private MetricsLogger mMetricsLogger;
 
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
@@ -99,6 +104,7 @@
         mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
         mUiOffloadThread = Dependency.get(UiOffloadThread.class);
         mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
+        mMetricsLogger = new MetricsLogger();
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
@@ -143,6 +149,14 @@
     private void sessionExitpoint(boolean force) {
         if (mSessionActive && (force || !shouldSessionBeActive())) {
             mSessionActive = false;
+            if (mIsFalseTouchCalls != 0) {
+                if (FalsingLog.ENABLED) {
+                    FalsingLog.i(
+                            "isFalseTouchCalls", "Calls before failure: " + mIsFalseTouchCalls);
+                }
+                mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls);
+                mIsFalseTouchCalls = 0;
+            }
 
             // This can be expensive, and doesn't need to happen on the main thread.
             mUiOffloadThread.submit(() -> {
@@ -166,6 +180,7 @@
         }
         mBouncerOn = false;
         mSessionActive = true;
+        mIsFalseTouchCalls = 0;
 
         if (mHumanInteractionClassifier.isEnabled()) {
             registerSensors(CLASSIFIER_SENSORS);
@@ -250,7 +265,16 @@
             // anti-falsed.
             return false;
         }
-        return mHumanInteractionClassifier.isFalseTouch();
+        mIsFalseTouchCalls++;
+        boolean isFalse = mHumanInteractionClassifier.isFalseTouch();
+        if (!isFalse) {
+            if (FalsingLog.ENABLED) {
+                FalsingLog.i("isFalseTouchCalls", "Calls before success: " + mIsFalseTouchCalls);
+            }
+            mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls);
+            mIsFalseTouchCalls = 0;
+        }
+        return isFalse;
     }
 
     private void clearPendingWtf() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
new file mode 100644
index 0000000..5907028
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions;
+
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
+/**
+ * Grid-based implementation of the button layout created by the global actions dialog.
+ */
+public class GlobalActionsColumnLayout extends GlobalActionsLayout {
+    private boolean mLastSnap;
+
+    public GlobalActionsColumnLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        post(() -> updateSnap());
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @VisibleForTesting
+    protected boolean shouldReverseListItems() {
+        int rotation = getCurrentRotation();
+        if (rotation == ROTATION_NONE) {
+            return false;
+        }
+        if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            return rotation == ROTATION_LANDSCAPE;
+        }
+        return rotation == ROTATION_SEASCAPE;
+    }
+
+    @Override
+    public void onUpdateList() {
+        super.onUpdateList();
+        updateChildOrdering();
+    }
+
+    private void updateChildOrdering() {
+        if (shouldReverseListItems()) {
+            getListView().bringToFront();
+        } else {
+            getSeparatedView().bringToFront();
+        }
+    }
+
+    /**
+     *  Snap this layout to align with the power button.
+     */
+    @VisibleForTesting
+    protected void snapToPowerButton() {
+        int offset = getPowerButtonOffsetDistance();
+        switch (getCurrentRotation()) {
+            case (ROTATION_LANDSCAPE):
+                setPadding(offset, 0, 0, 0);
+                setGravity(Gravity.LEFT | Gravity.TOP);
+                break;
+            case (ROTATION_SEASCAPE):
+                setPadding(0, 0, offset, 0);
+                setGravity(Gravity.RIGHT | Gravity.BOTTOM);
+                break;
+            default:
+                setPadding(0, offset, 0, 0);
+                setGravity(Gravity.TOP | Gravity.RIGHT);
+                break;
+        }
+    }
+
+    /**
+     *  Detach this layout from snapping to the power button and instead center along that edge.
+     */
+    @VisibleForTesting
+    protected void centerAlongEdge() {
+        switch (getCurrentRotation()) {
+            case (ROTATION_LANDSCAPE):
+                setPadding(0, 0, 0, 0);
+                setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
+                break;
+            case (ROTATION_SEASCAPE):
+                setPadding(0, 0, 0, 0);
+                setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+                break;
+            default:
+                setPadding(0, 0, 0, 0);
+                setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+                break;
+        }
+    }
+
+    /**
+     * Determines the distance from the top of the screen to the power button.
+     */
+    @VisibleForTesting
+    protected int getPowerButtonOffsetDistance() {
+        return Math.round(getContext().getResources().getDimension(
+                R.dimen.global_actions_top_padding));
+    }
+
+    /**
+     * Check whether there is enough extra space below the dialog such that we can offset the top
+     * of the dialog from the top of the phone to line it up with the power button, then either
+     * snap the dialog to the power button or center it along the edge with snapToPowerButton.
+     */
+    @VisibleForTesting
+    protected boolean shouldSnapToPowerButton() {
+        int offsetSize = getPowerButtonOffsetDistance();
+        int dialogSize;
+        int screenSize;
+        View wrapper = getWrapper();
+        int rotation = getCurrentRotation();
+        if (rotation == ROTATION_NONE) {
+            dialogSize = wrapper.getMeasuredHeight();
+            screenSize = getMeasuredHeight();
+        } else {
+            dialogSize = wrapper.getMeasuredWidth();
+            screenSize = getMeasuredWidth();
+        }
+        return dialogSize + offsetSize < screenSize;
+    }
+
+    @VisibleForTesting
+    protected void updateSnap() {
+        boolean snap = shouldSnapToPowerButton();
+        if (snap != mLastSnap) {
+            if (snap) {
+                snapToPowerButton();
+            } else {
+                centerAlongEdge();
+            }
+        }
+        mLastSnap = snap;
+    }
+
+    @VisibleForTesting
+    protected float getGridItemSize() {
+        return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height);
+    }
+
+    @VisibleForTesting
+    protected float getAnimationDistance() {
+        return getGridItemSize() / 2;
+    }
+
+    @Override
+    public float getAnimationOffsetX() {
+        if (getCurrentRotation() == ROTATION_NONE) {
+            return getAnimationDistance();
+        }
+        return 0;
+    }
+
+    @Override
+    public float getAnimationOffsetY() {
+        switch (getCurrentRotation()) {
+            case ROTATION_LANDSCAPE:
+                return -getAnimationDistance();
+            case ROTATION_SEASCAPE:
+                return getAnimationDistance();
+            default: // Portrait
+                return 0;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 64511a0..d6c8971 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -410,8 +410,10 @@
                                         mActivityStarter
                                                 .startPendingIntentDismissingKeyguard(intent);
                                     }
-                                })
+                                },
+                                mKeyguardManager.isDeviceLocked())
                         : null;
+
         ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController);
         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
         dialog.setKeyguardShowing(mKeyguardShowing);
@@ -1583,13 +1585,20 @@
         }
 
         private int getGlobalActionsLayoutId(Context context) {
-            if (isForceGridEnabled(context) || shouldUsePanel()) {
-                if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+            boolean useGridLayout = isForceGridEnabled(context) || shouldUsePanel();
+            if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+                if (useGridLayout) {
                     return com.android.systemui.R.layout.global_actions_grid_seascape;
+                } else {
+                    return com.android.systemui.R.layout.global_actions_column_seascape;
                 }
-                return com.android.systemui.R.layout.global_actions_grid;
+            } else {
+                if (useGridLayout) {
+                    return com.android.systemui.R.layout.global_actions_grid;
+                } else {
+                    return com.android.systemui.R.layout.global_actions_column;
+                }
             }
-            return com.android.systemui.R.layout.global_actions_wrapped;
         }
 
         @Override
@@ -1712,7 +1721,7 @@
         }
 
         public void onRotate(int from, int to) {
-            if (mShowing && (shouldUsePanel() || isForceGridEnabled(mContext))) {
+            if (mShowing) {
                 refreshDialog();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index 554ed73..e1462d1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -22,58 +22,23 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.HardwareBgDrawable;
-import com.android.systemui.MultiListLayout;
-import com.android.systemui.util.leak.RotationUtils;
 
 /**
  * Grid-based implementation of the button layout created by the global actions dialog.
  */
-public class GlobalActionsGridLayout extends MultiListLayout {
-
-    boolean mBackgroundsSet;
-
+public class GlobalActionsGridLayout extends GlobalActionsLayout {
     public GlobalActionsGridLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    private void setBackgrounds() {
-        int gridBackgroundColor = getResources().getColor(
-                com.android.systemui.R.color.global_actions_grid_background, null);
-        int separatedBackgroundColor = getResources().getColor(
-                com.android.systemui.R.color.global_actions_separated_background, null);
-        HardwareBgDrawable listBackground  = new HardwareBgDrawable(true, true, getContext());
-        HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
-        listBackground.setTint(gridBackgroundColor);
-        separatedBackground.setTint(separatedBackgroundColor);
-        getListView().setBackground(listBackground);
-        getSeparatedView().setBackground(separatedBackground);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        // backgrounds set only once, the first time onMeasure is called after inflation
-        if (getListView() != null && !mBackgroundsSet) {
-            setBackgrounds();
-            mBackgroundsSet = true;
-        }
-    }
-
     @VisibleForTesting
-    protected int getCurrentRotation() {
-        return RotationUtils.getRotation(mContext);
-    }
-
-    @VisibleForTesting
-    protected void setupListView(ListGridLayout listView, int itemCount) {
-        listView.setExpectedCount(itemCount);
+    protected void setupListView() {
+        ListGridLayout listView = getListView();
+        listView.setExpectedCount(mAdapter.countListItems());
         listView.setReverseSublists(shouldReverseSublists());
         listView.setReverseItems(shouldReverseListItems());
         listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
@@ -81,29 +46,8 @@
 
     @Override
     public void onUpdateList() {
+        setupListView();
         super.onUpdateList();
-
-        ViewGroup separatedView = getSeparatedView();
-        ListGridLayout listView = getListView();
-        setupListView(listView, mAdapter.countListItems());
-
-        for (int i = 0; i < mAdapter.getCount(); i++) {
-            // generate the view item
-            View v;
-            boolean separated = mAdapter.shouldBeSeparated(i);
-            if (separated) {
-                v = mAdapter.getView(i, null, separatedView);
-            } else {
-                v = mAdapter.getView(i, null, listView);
-            }
-            Log.d("GlobalActionsGridLayout", "View: " + v);
-
-            if (separated) {
-                separatedView.addView(v);
-            } else {
-                listView.addItem(v);
-            }
-        }
         updateSeparatedItemSize();
     }
 
@@ -111,7 +55,8 @@
      * If the separated view contains only one item, expand the bounds of that item to take up the
      * entire view, so that the whole thing is touch-able.
      */
-    private void updateSeparatedItemSize() {
+    @VisibleForTesting
+    protected void updateSeparatedItemSize() {
         ViewGroup separated = getSeparatedView();
         if (separated.getChildCount() == 0) {
             return;
@@ -129,13 +74,24 @@
     }
 
     @Override
-    protected ViewGroup getSeparatedView() {
-        return findViewById(com.android.systemui.R.id.separated_button);
+    protected ListGridLayout getListView() {
+        return (ListGridLayout) super.getListView();
     }
 
     @Override
-    protected ListGridLayout getListView() {
-        return findViewById(android.R.id.list);
+    protected void removeAllListViews() {
+        ListGridLayout list = getListView();
+        if (list != null) {
+            list.removeAllItems();
+        }
+    }
+
+    @Override
+    protected void addToListView(View v, boolean reverse) {
+        ListGridLayout list = getListView();
+        if (list != null) {
+            list.addItem(v);
+        }
     }
 
     @Override
@@ -174,12 +130,7 @@
         return true;
     }
 
-    /**
-     * Determines whether the ListGridLayout should reverse the ordering of items within sublists.
-     * Used for RTL languages to ensure that items appear in the same positions, without having to
-     * override layoutDirection, which breaks Talkback ordering.
-     */
-    @VisibleForTesting
+    @Override
     protected boolean shouldReverseListItems() {
         int rotation = getCurrentRotation();
         boolean reverse = false; // should we add items to parents in the reverse order?
@@ -187,20 +138,13 @@
                 || rotation == ROTATION_SEASCAPE) {
             reverse = !reverse; // if we're in portrait or seascape, reverse items
         }
-        if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+        if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
             reverse = !reverse; // if we're in an RTL language, reverse items (again)
         }
         return reverse;
     }
 
-    /**
-     * Not ued in this implementation of the Global Actions Menu, but necessary for some others.
-     */
-    @Override
-    public void setDivisionView(View v) {
-        // do nothing
-    }
-
+    @VisibleForTesting
     protected float getAnimationDistance() {
         int rows = getListView().getRowCount();
         float gridItemSize = getContext().getResources().getDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
new file mode 100644
index 0000000..f755a93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.HardwareBgDrawable;
+import com.android.systemui.MultiListLayout;
+import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
+
+import java.util.Locale;
+
+/**
+ * Grid-based implementation of the button layout created by the global actions dialog.
+ */
+public abstract class GlobalActionsLayout extends MultiListLayout {
+
+    boolean mBackgroundsSet;
+
+    public GlobalActionsLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    private void setBackgrounds() {
+        int gridBackgroundColor = getResources().getColor(
+                R.color.global_actions_grid_background, null);
+        int separatedBackgroundColor = getResources().getColor(
+                R.color.global_actions_separated_background, null);
+        HardwareBgDrawable listBackground  = new HardwareBgDrawable(true, true, getContext());
+        HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
+        listBackground.setTint(gridBackgroundColor);
+        separatedBackground.setTint(separatedBackgroundColor);
+        getListView().setBackground(listBackground);
+        getSeparatedView().setBackground(separatedBackground);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // backgrounds set only once, the first time onMeasure is called after inflation
+        if (getListView() != null && !mBackgroundsSet) {
+            setBackgrounds();
+            mBackgroundsSet = true;
+        }
+    }
+
+    protected void addToListView(View v, boolean reverse) {
+        if (reverse) {
+            getListView().addView(v, 0);
+        } else {
+            getListView().addView(v);
+        }
+    }
+
+    protected void addToSeparatedView(View v, boolean reverse) {
+        if (reverse) {
+            getSeparatedView().addView(v, 0);
+        } else {
+            getSeparatedView().addView(v);
+        }
+    }
+
+    @VisibleForTesting
+    protected int getCurrentLayoutDirection() {
+        return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
+    }
+
+    @VisibleForTesting
+    protected int getCurrentRotation() {
+        return RotationUtils.getRotation(mContext);
+    }
+
+    /**
+     * Determines whether the ListGridLayout should reverse the ordering of items within sublists.
+     * Used for RTL languages to ensure that items appear in the same positions, without having to
+     * override layoutDirection, which breaks Talkback ordering.
+     */
+    protected abstract boolean shouldReverseListItems();
+
+    @Override
+    public void onUpdateList() {
+        super.onUpdateList();
+
+        ViewGroup separatedView = getSeparatedView();
+        ViewGroup listView = getListView();
+
+        for (int i = 0; i < mAdapter.getCount(); i++) {
+            // generate the view item
+            View v;
+            boolean separated = mAdapter.shouldBeSeparated(i);
+            if (separated) {
+                v = mAdapter.getView(i, null, separatedView);
+            } else {
+                v = mAdapter.getView(i, null, listView);
+            }
+            if (separated) {
+                addToSeparatedView(v, false);
+            } else {
+                addToListView(v, shouldReverseListItems());
+            }
+        }
+    }
+
+    @Override
+    protected ViewGroup getSeparatedView() {
+        return findViewById(R.id.separated_button);
+    }
+
+    @Override
+    protected ViewGroup getListView() {
+        return findViewById(android.R.id.list);
+    }
+
+    protected View getWrapper() {
+        return getChildAt(0);
+    }
+
+    /**
+     * Not used in this implementation of the Global Actions Menu, but necessary for some others.
+     */
+    @Override
+    public void setDivisionView(View v) {
+        // do nothing
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
index 477e7d7e..24a4b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -30,13 +30,15 @@
 import android.util.Log;
 
 /**
- * A helper class that computes histogram and percentile 85 from a bitmap.
- * Percentile 85 will be computed each time the user picks a new image wallpaper.
+ * A helper class that computes threshold from a bitmap.
+ * Threshold will be computed each time the user picks a new image wallpaper.
  */
 class ImageProcessHelper {
     private static final String TAG = ImageProcessHelper.class.getSimpleName();
-    private static final float DEFAULT_PER85 = 0.8f;
-    private static final int MSG_UPDATE_PER85 = 1;
+    private static final float DEFAULT_THRESHOLD = 0.8f;
+    private static final float DEFAULT_OTSU_THRESHOLD = 0f;
+    private static final float MAX_THRESHOLD = 0.89f;
+    private static final int MSG_UPDATE_THRESHOLD = 1;
 
     /**
      * This color matrix will be applied to each pixel to get luminance from rgb by below formula:
@@ -53,8 +55,8 @@
         @Override
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_UPDATE_PER85:
-                    mPer85 = (float) msg.obj;
+                case MSG_UPDATE_THRESHOLD:
+                    mThreshold = (float) msg.obj;
                     return true;
                 default:
                     return false;
@@ -62,20 +64,20 @@
         }
     });
 
-    private float mPer85 = DEFAULT_PER85;
+    private float mThreshold = DEFAULT_THRESHOLD;
 
-    void startComputingPercentile85(Bitmap bitmap) {
-        new Per85ComputeTask(mHandler).execute(bitmap);
+    void start(Bitmap bitmap) {
+        new ThresholdComputeTask(mHandler).execute(bitmap);
     }
 
-    float getPercentile85() {
-        return mPer85;
+    float getThreshold() {
+        return Math.min(mThreshold, MAX_THRESHOLD);
     }
 
-    private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
+    private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> {
         private Handler mUpdateHandler;
 
-        Per85ComputeTask(Handler handler) {
+        ThresholdComputeTask(Handler handler) {
             super(handler);
             mUpdateHandler = handler;
         }
@@ -84,35 +86,55 @@
         protected Float doInBackground(Bitmap... bitmaps) {
             Bitmap bitmap = bitmaps[0];
             if (bitmap != null) {
-                int[] histogram = processHistogram(bitmap);
-                return computePercentile85(bitmap, histogram);
+                return new Threshold().compute(bitmap);
             }
-            Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
-            return DEFAULT_PER85;
+            Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
+            return DEFAULT_THRESHOLD;
         }
 
         @Override
         protected void onPostExecute(Float result) {
-            Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
+            Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result);
             mUpdateHandler.sendMessage(msg);
         }
+    }
 
-        private int[] processHistogram(Bitmap bitmap) {
+    private static class Threshold {
+        public float compute(Bitmap bitmap) {
+            Bitmap grayscale = toGrayscale(bitmap);
+            int[] histogram = getHistogram(grayscale);
+            boolean isSolidColor = isSolidColor(grayscale, histogram);
+
+            // We will see gray wallpaper during the transition if solid color wallpaper is set,
+            // please refer to b/130360362#comment16.
+            // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set.
+            ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus();
+            return algorithm.compute(grayscale, histogram);
+        }
+
+        private Bitmap toGrayscale(Bitmap bitmap) {
             int width = bitmap.getWidth();
             int height = bitmap.getHeight();
 
-            Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
-            Canvas canvas = new Canvas(target);
+            Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig());
+            Canvas canvas = new Canvas(grayscale);
             ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
             Paint paint = new Paint();
             paint.setColorFilter(new ColorMatrixColorFilter(cm));
             canvas.drawBitmap(bitmap, new Matrix(), paint);
 
+            return grayscale;
+        }
+
+        private int[] getHistogram(Bitmap grayscale) {
+            int width = grayscale.getWidth();
+            int height = grayscale.getHeight();
+
             // TODO: Fine tune the performance here, tracking on b/123615079.
             int[] histogram = new int[256];
             for (int row = 0; row < height; row++) {
                 for (int col = 0; col < width; col++) {
-                    int pixel = target.getPixel(col, row);
+                    int pixel = grayscale.getPixel(col, row);
                     int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
                     histogram[y]++;
                 }
@@ -121,8 +143,29 @@
             return histogram;
         }
 
-        private float computePercentile85(Bitmap bitmap, int[] histogram) {
-            float per85 = DEFAULT_PER85;
+        private boolean isSolidColor(Bitmap bitmap, int[] histogram) {
+            boolean solidColor = false;
+            int pixels = bitmap.getWidth() * bitmap.getHeight();
+
+            // In solid color case, only one element of histogram has value,
+            // which is pixel counts and the value of other elements should be 0.
+            for (int value : histogram) {
+                if (value != 0 && value != pixels) {
+                    break;
+                }
+                if (value == pixels) {
+                    solidColor = true;
+                    break;
+                }
+            }
+            return solidColor;
+        }
+    }
+
+    private static class Percentile85 implements ThresholdAlgorithm {
+        @Override
+        public float compute(Bitmap bitmap, int[] histogram) {
+            float per85 = DEFAULT_THRESHOLD;
             int pixelCount = bitmap.getWidth() * bitmap.getHeight();
             float[] acc = new float[256];
             for (int i = 0; i < acc.length; i++) {
@@ -141,4 +184,51 @@
             return per85;
         }
     }
+
+    private static class Otsus implements ThresholdAlgorithm {
+        @Override
+        public float compute(Bitmap bitmap, int[] histogram) {
+            float threshold = DEFAULT_OTSU_THRESHOLD;
+            float maxVariance = 0;
+            float pixelCount = bitmap.getWidth() * bitmap.getHeight();
+            float[] w = new float[2];
+            float[] m = new float[2];
+            float[] u = new float[2];
+
+            for (int i = 0; i < histogram.length; i++) {
+                m[1] += i * histogram[i];
+            }
+
+            w[1] = pixelCount;
+            for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) {
+                float dU;
+                float variance;
+                float numPixels = histogram[tonalValue];
+                float tmp = numPixels * tonalValue;
+                w[0] += numPixels;
+                w[1] -= numPixels;
+
+                if (w[0] == 0 || w[1] == 0) {
+                    continue;
+                }
+
+                m[0] += tmp;
+                m[1] -= tmp;
+                u[0] = m[0] / w[0];
+                u[1] = m[1] / w[1];
+                dU = u[0] - u[1];
+                variance = w[0] * w[1] * dU * dU;
+
+                if (variance > maxVariance) {
+                    threshold = (tonalValue + 1f) / histogram.length;
+                    maxVariance = variance;
+                }
+            }
+            return threshold;
+        }
+    }
+
+    private interface ThresholdAlgorithm {
+        float compute(Bitmap bitmap, int[] histogram);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 464cbe3..5bbfe84 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -78,8 +78,8 @@
             mBitmap = mWallpaperManager.getBitmap();
             mBitmapWidth = mBitmap.getWidth();
             mBitmapHeight = mBitmap.getHeight();
-            // Compute per85 as transition threshold, this is an async work.
-            mImageProcessHelper.startComputingPercentile85(mBitmap);
+            // Compute threshold of the image, this is an async work.
+            mImageProcessHelper.start(mBitmap);
             mWallpaperManager.forgetLoadedWallpaper();
         }
     }
@@ -108,13 +108,13 @@
 
     @Override
     public void onDrawFrame(GL10 gl) {
-        float per85 = mImageProcessHelper.getPercentile85();
+        float threshold = mImageProcessHelper.getThreshold();
         float reveal = mImageRevealHelper.getReveal();
 
         glClear(GL_COLOR_BUFFER_BIT);
 
         glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
-        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85);
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold);
         glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
 
         scaleViewport(reveal);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
index e352b58..4571ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -77,6 +77,7 @@
 
     @Override
     public void onClick(View v) {
+        if (!v.isVisibleToUser()) return;
         mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                 Settings.ACTION_WIRELESS_SETTINGS), 0);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 9431f20..42c616c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -524,7 +524,7 @@
         if (v == mClockView) {
             mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     AlarmClock.ACTION_SHOW_ALARMS), 0);
-        } else if (v == mNextAlarmContainer) {
+        } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
             if (mNextAlarm.getShowIntent() != null) {
                 mActivityStarter.postStartActivityDismissingKeyguard(
                         mNextAlarm.getShowIntent());
@@ -545,7 +545,7 @@
                         new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
                 mHost.collapsePanels();
             });
-        } else if (v == mRingerContainer) {
+        } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
             mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     Settings.ACTION_SOUND_SETTINGS), 0);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index ca04076..9282a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -144,7 +144,8 @@
                         mContext.getString(R.string.accessibility_bluetooth_name, state.label)
                                 + ", " + state.secondaryLabel;
             } else if (state.isTransient) {
-                state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
+                state.icon = ResourceIcon.get(
+                        com.android.internal.R.drawable.ic_bluetooth_transient_animation);
                 state.contentDescription = state.secondaryLabel;
             } else {
                 state.icon =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 5e6f18e..001e094 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -130,7 +130,8 @@
         state.isTransient = isTransient;
         state.slash.isSlashed = !state.value && !state.isTransient;
         if (state.isTransient) {
-            state.icon = ResourceIcon.get(R.drawable.ic_hotspot_transient_animation);
+            state.icon = ResourceIcon.get(
+                    com.android.internal.R.drawable.ic_hotspot_transient_animation);
         }
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.contentDescription = state.label;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 15df1f1..0e7362c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -186,7 +186,8 @@
         final StringBuffer minimalContentDescription = new StringBuffer();
         final Resources r = mContext.getResources();
         if (isTransient) {
-            state.icon = ResourceIcon.get(R.drawable.ic_signal_wifi_transient_animation);
+            state.icon = ResourceIcon.get(
+                    com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
             state.label = r.getString(R.string.quick_settings_wifi_label);
         } else if (!state.value) {
             state.slash.isSlashed = true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 4fb4999..78c7cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
@@ -29,7 +30,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import android.annotation.FloatRange;
 import android.app.ActivityTaskManager;
@@ -477,7 +477,12 @@
         }
     }
 
-    public void setSystemUiStateFlag(int flag, boolean enabled) {
+    public void setSystemUiStateFlag(int flag, boolean enabled, int displayId) {
+        if (displayId != DEFAULT_DISPLAY) {
+            // Ignore non-default displays for now
+            return;
+        }
+
         int newState = mSysUiStateFlags;
         if (enabled) {
             newState |= flag;
@@ -502,8 +507,6 @@
                 && statusBar.getPanel().isFullyExpanded();
         final boolean bouncerShowing = statusBar != null && statusBar.isBouncerShowing();
         mSysUiStateFlags = 0;
-        mSysUiStateFlags |= ActivityManagerWrapper.getInstance().isScreenPinningActive()
-                ? SYSUI_STATE_SCREEN_PINNING : 0;
         mSysUiStateFlags |= (navBarFragment != null && !navBarFragment.isNavBarWindowVisible())
                 ? SYSUI_STATE_NAV_BAR_HIDDEN : 0;
         mSysUiStateFlags |= panelExpanded
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index ade903d..c3c0d63 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.recents;
 
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
 
@@ -139,7 +138,6 @@
         if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {
             try {
                 ActivityTaskManager.getService().startSystemLockTaskMode(taskId);
-                mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, true);
             } catch (RemoteException e) {}
         }
         clearPrompt();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index a688f36..f97be1ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -78,7 +78,7 @@
     private static final int MSG_COLLAPSE_PANELS               = 4 << MSG_SHIFT;
     private static final int MSG_EXPAND_SETTINGS               = 5 << MSG_SHIFT;
     private static final int MSG_SET_SYSTEMUI_VISIBILITY       = 6 << MSG_SHIFT;
-    private static final int MSG_TOP_APP_WINDOW_CHANGED        = 7 << MSG_SHIFT;
+    private static final int MSG_DISPLAY_READY                 = 7 << MSG_SHIFT;
     private static final int MSG_SHOW_IME_BUTTON               = 8 << MSG_SHIFT;
     private static final int MSG_TOGGLE_RECENT_APPS            = 9 << MSG_SHIFT;
     private static final int MSG_PRELOAD_RECENT_APPS           = 10 << MSG_SHIFT;
@@ -115,7 +115,6 @@
     private static final int MSG_SHOW_CHARGING_ANIMATION       = 44 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
-    private static final int MSG_DISPLAY_READY                 = 47 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -175,21 +174,14 @@
          * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen
          *                              coordinates.
          * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates.
+         * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
          */
         default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+                boolean navbarColorManagedByIme) {
         }
 
         /**
-         * Called to notify top app window changes.
-         * @see IStatusBar#topAppWindowChanged(int, boolean)
-         *
-         * @param displayId The id of the display to notify.
-         * @param visible {@code true} to show menu button.
-         */
-        default void topAppWindowChanged(int displayId, boolean visible) { }
-
-        /**
          * Called to notify IME window status changes.
          *
          * @param displayId The id of the display to notify.
@@ -459,7 +451,8 @@
 
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         synchronized (mLock) {
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
@@ -469,6 +462,7 @@
             args.argi3 = fullscreenStackVis;
             args.argi4 = dockedStackVis;
             args.argi5 = mask;
+            args.argi6 = navbarColorManagedByIme ? 1 : 0;
             args.arg1 = fullscreenStackBounds;
             args.arg2 = dockedStackBounds;
             mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
@@ -476,13 +470,7 @@
     }
 
     @Override
-    public void topAppWindowChanged(int displayId, boolean menuVisible) {
-        synchronized (mLock) {
-            mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
-            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED,
-                    displayId, menuVisible ? 1 : 0, null).sendToTarget();
-        }
-    }
+    public void topAppWindowChanged(int displayId, boolean menuVisible) { }
 
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
@@ -879,15 +867,11 @@
                     args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
-                                args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2);
+                                args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2,
+                                args.argi6 == 1);
                     }
                     args.recycle();
                     break;
-                case MSG_TOP_APP_WINDOW_CHANGED:
-                    for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).topAppWindowChanged(msg.arg1, msg.arg2 != 0);
-                    }
-                    break;
                 case MSG_SHOW_IME_BUTTON:
                     args = (SomeArgs) msg.obj;
                     handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
index b7ae4ed..4b2d131 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar
 
-import android.annotation.ColorInt
 import android.content.Context
 import android.graphics.Bitmap
 import android.graphics.Canvas
@@ -28,6 +27,7 @@
 import android.renderscript.ScriptIntrinsicBlur
 import android.util.MathUtils
 import com.android.internal.graphics.ColorUtils
+import com.android.systemui.statusbar.notification.MediaNotificationProcessor
 
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -42,7 +42,7 @@
     private val mTmpSize = Point()
     private var mArtworkCache: Bitmap? = null
 
-    fun processArtwork(context: Context, artwork: Bitmap, @ColorInt color: Int): Bitmap {
+    fun processArtwork(context: Context, artwork: Bitmap): Bitmap {
         if (mArtworkCache != null) {
             return mArtworkCache!!
         }
@@ -71,13 +71,15 @@
         blur.forEach(output)
         output.copyTo(outBitmap)
 
+        val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork)
+
         input.destroy()
         output.destroy()
         inBitmap.recycle()
         blur.destroy()
 
         val canvas = Canvas(outBitmap)
-        canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
+        canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA))
         return outBitmap
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index aeaceb0..d59a5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -104,7 +104,7 @@
 
                     // Remove existing notification to avoid stale data.
                     if (isUpdate) {
-                        mEntryManager.removeNotification(key, rankingMap);
+                        mEntryManager.removeNotification(key, rankingMap, 0 /* reason */);
                     } else {
                         mEntryManager.getNotificationData()
                                 .updateRanking(rankingMap);
@@ -121,18 +121,23 @@
     }
 
     @Override
-    public void onNotificationRemoved(StatusBarNotification sbn,
-            final RankingMap rankingMap) {
-        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+            int reason) {
+        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn + " reason: " + reason);
         if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
             final String key = sbn.getKey();
             Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
-                mEntryManager.removeNotification(key, rankingMap);
+                mEntryManager.removeNotification(key, rankingMap, reason);
             });
         }
     }
 
     @Override
+    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+        onNotificationRemoved(sbn, rankingMap, 0 /* reason */);
+    }
+
+    @Override
     public void onNotificationRankingUpdate(final RankingMap rankingMap) {
         if (DEBUG) Log.d(TAG, "onRankingUpdate");
         if (rankingMap != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 1615e65..b9e0c60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -21,11 +21,11 @@
 import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
 import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
 
+import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -35,11 +35,13 @@
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -64,8 +66,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -108,6 +112,7 @@
     private final MediaSessionManager mMediaSessionManager;
     private final ArrayList<MediaListener> mMediaListeners;
     private final MediaArtworkProcessor mMediaArtworkProcessor;
+    private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
 
     protected NotificationPresenter mPresenter;
     private MediaController mMediaController;
@@ -449,28 +454,37 @@
                     + " state=" + mStatusBarStateController.getState());
         }
 
-        Drawable artworkDrawable = null;
+        Bitmap artworkBitmap = null;
         if (mediaMetadata != null) {
-            Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+            artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
             if (artworkBitmap == null) {
                 artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
-                // might still be null
             }
-            if (artworkBitmap != null) {
-                int notificationColor;
-                synchronized (mEntryManager.getNotificationData()) {
-                    NotificationEntry entry = mEntryManager.getNotificationData()
-                            .get(mMediaNotificationKey);
-                    if (entry == null || entry.getRow() == null) {
-                        notificationColor = Color.TRANSPARENT;
-                    } else {
-                        notificationColor = entry.getRow().calculateBgColor();
-                    }
-                }
-                Bitmap bmp = mMediaArtworkProcessor.processArtwork(mContext, artworkBitmap,
-                        notificationColor);
-                artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
+        }
+
+        // Process artwork on a background thread and send the resulting bitmap to
+        // finishUpdateMediaMetaData.
+        if (metaDataChanged) {
+            for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {
+                task.cancel(true);
             }
+            mProcessArtworkTasks.clear();
+        }
+        if (artworkBitmap != null) {
+            mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
+                    allowEnterAnimation).execute(artworkBitmap));
+        } else {
+            finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);
+        }
+
+        Trace.endSection();
+    }
+
+    private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
+            @Nullable Bitmap bmp) {
+        Drawable artworkDrawable = null;
+        if (bmp != null) {
+            artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
         }
         boolean allowWhenShade = false;
         if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
@@ -598,7 +612,6 @@
                 }
             }
         }
-        Trace.endSection();
     }
 
     public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
@@ -629,6 +642,61 @@
         }
     };
 
+    private Bitmap processArtwork(Bitmap artwork) {
+        return mMediaArtworkProcessor.processArtwork(mContext, artwork);
+    }
+
+    @MainThread
+    private void removeTask(AsyncTask<?, ?, ?> task) {
+        mProcessArtworkTasks.remove(task);
+    }
+
+    /**
+     * {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
+     */
+    private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> {
+
+        private final WeakReference<NotificationMediaManager> mManagerRef;
+        private final boolean mMetaDataChanged;
+        private final boolean mAllowEnterAnimation;
+
+        ProcessArtworkTask(NotificationMediaManager manager, boolean changed,
+                boolean allowAnimation) {
+            mManagerRef = new WeakReference<>(manager);
+            mMetaDataChanged = changed;
+            mAllowEnterAnimation = allowAnimation;
+        }
+
+        @Override
+        protected Bitmap doInBackground(Bitmap... bitmaps) {
+            NotificationMediaManager manager = mManagerRef.get();
+            if (manager == null || bitmaps.length == 0 || isCancelled()) {
+                return null;
+            }
+            return manager.processArtwork(bitmaps[0]);
+        }
+
+        @Override
+        protected void onPostExecute(@Nullable Bitmap result) {
+            NotificationMediaManager manager = mManagerRef.get();
+            if (manager != null && !isCancelled()) {
+                manager.removeTask(this);
+                manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result);
+            }
+        }
+
+        @Override
+        protected void onCancelled(Bitmap result) {
+            if (result != null) {
+                result.recycle();
+            }
+            NotificationMediaManager manager = mManagerRef.get();
+            if (manager != null) {
+                manager.removeTask(this);
+            }
+        }
+    }
+
     public interface MediaListener {
         void onMetadataChanged(MediaMetadata metadata);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 2793b2a..fe8c6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -51,6 +51,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -273,10 +274,20 @@
 
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
+            public void onPreEntryUpdated(NotificationEntry entry) {
+                // Mark smart replies as sent whenever a notification is updated - otherwise the
+                // smart replies are never marked as sent.
+                mSmartReplyController.stopSending(entry);
+            }
+
+            @Override
             public void onEntryRemoved(
                     @Nullable NotificationEntry entry,
                     NotificationVisibility visibility,
                     boolean removedByUser) {
+                // We're removing the notification, the smart controller can forget about it.
+                mSmartReplyController.stopSending(entry);
+
                 if (removedByUser && entry != null) {
                     onPerformRemoveNotification(entry, entry.key);
                 }
@@ -348,24 +359,18 @@
 
         ViewParent p = view.getParent();
         RemoteInputView riv = null;
+        ExpandableNotificationRow row = null;
         while (p != null) {
             if (p instanceof View) {
                 View pv = (View) p;
                 if (pv.isRootNamespace()) {
                     riv = findRemoteInputView(pv);
+                    row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view);
                     break;
                 }
             }
             p = p.getParent();
         }
-        ExpandableNotificationRow row = null;
-        while (p != null) {
-            if (p instanceof ExpandableNotificationRow) {
-                row = (ExpandableNotificationRow) p;
-                break;
-            }
-            p = p.getParent();
-        }
 
         if (row == null) {
             return false;
@@ -391,10 +396,13 @@
             if (riv == null) {
                 return false;
             }
-            if (!row.getPrivateLayout().getExpandedChild().isShown()) {
-                mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
-                return true;
-            }
+        }
+        if (riv == row.getPrivateLayout().getExpandedRemoteInput()
+                && !row.getPrivateLayout().getExpandedChild().isShown()) {
+            // The expanded layout is selected, but it's not shown yet, let's wait on it to
+            // show before we do the animation.
+            mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+            return true;
         }
 
         int width = view.getWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
new file mode 100644
index 0000000..930116e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.service.notification.NotificationListenerService;
+
+/**
+ * Interface for anything that may need to prevent notifications from being removed. This is
+ * similar to a {@link NotificationLifetimeExtender} in the sense that it extends the life of
+ * a notification by preventing the removal, however, unlike the extender, the remove interceptor
+ * gets first pick at intercepting any type of removal -- the life time extender is unable to
+ * extend the life of a user dismissed or force removed notification.
+ */
+public interface NotificationRemoveInterceptor {
+
+    /**
+     * Called when a notification has been removed.
+     *
+     * @param key the entry key of the notification being removed.
+     * @param removeReason why the notification is being removed, e.g.
+     * {@link NotificationListenerService#REASON_CANCEL} or 0 if unknown.
+     *
+     * @return true if the removal should be ignored, false otherwise.
+     */
+    boolean onNotificationRemoveRequested(String key, int removeReason);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
index 0044194..1ac8198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
@@ -37,8 +37,10 @@
      *
      * @param key Key identifying the notification to remove
      * @param ranking RankingMap to update with
+     * @param reason why the notification is being removed, e.g.
+     * {@link NotificationListenerService#REASON_CANCEL}.
      */
-    void removeNotification(String key, NotificationListenerService.RankingMap ranking);
+    void removeNotification(String key, NotificationListenerService.RankingMap ranking, int reason);
 
     /**
      * Update a given notification and the current notification ranking map.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 333239e..0d9f4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -30,6 +30,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.View;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Interpolators;
@@ -79,11 +80,12 @@
     }
 
     public RemoteAnimationAdapter getLaunchAnimation(
-            ExpandableNotificationRow sourceNotification, boolean occluded) {
-        if (!mCallback.areLaunchAnimationsEnabled() || occluded) {
+            View sourceView, boolean occluded) {
+        if (!(sourceView instanceof ExpandableNotificationRow) || !mCallback.areLaunchAnimationsEnabled() || occluded) {
             return null;
         }
-        AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
+        AnimationRunner animationRunner = new AnimationRunner(
+                (ExpandableNotificationRow) sourceView);
         return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
                 ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index ab94008..5d1ab4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -69,8 +69,7 @@
     private static final int RESIZE_BITMAP_AREA = 150 * 150;
     private final ImageGradientColorizer mColorizer;
     private final Context mContext;
-    private float[] mFilteredBackgroundHsl = null;
-    private Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl);
+    private final Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl);
 
     /**
      * The context of the notification. This is the app context of the package posting the
@@ -121,23 +120,21 @@
                 drawable.setBounds(0, 0, width, height);
                 drawable.draw(canvas);
 
-                // for the background we only take the left side of the image to ensure
-                // a smooth transition
-                Palette.Builder paletteBuilder = Palette.from(bitmap)
-                        .setRegion(0, 0, bitmap.getWidth() / 2, bitmap.getHeight())
-                        .clearFilters() // we want all colors, red / white / black ones too!
-                        .resizeBitmapArea(RESIZE_BITMAP_AREA);
+                Palette.Builder paletteBuilder = generateArtworkPaletteBuilder(bitmap);
                 Palette palette = paletteBuilder.generate();
-                backgroundColor = findBackgroundColorAndFilter(palette);
+                Palette.Swatch backgroundSwatch = findBackgroundSwatch(palette);
+                backgroundColor = backgroundSwatch.getRgb();
                 // we want most of the full region again, slightly shifted to the right
                 float textColorStartWidthFraction = 0.4f;
                 paletteBuilder.setRegion((int) (bitmap.getWidth() * textColorStartWidthFraction), 0,
                         bitmap.getWidth(),
                         bitmap.getHeight());
-                if (mFilteredBackgroundHsl != null) {
+                // We're not filtering on white or black
+                if (!isWhiteOrBlack(backgroundSwatch.getHsl())) {
+                    final float backgroundHue = backgroundSwatch.getHsl()[0];
                     paletteBuilder.addFilter((rgb, hsl) -> {
                         // at least 10 degrees hue difference
-                        float diff = Math.abs(hsl[0] - mFilteredBackgroundHsl[0]);
+                        float diff = Math.abs(hsl[0] - backgroundHue);
                         return diff > 10 && diff < 350;
                     });
                 }
@@ -244,18 +241,31 @@
                 && (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
     }
 
-    private int findBackgroundColorAndFilter(Palette palette) {
+    /**
+     * Finds an appropriate background swatch from media artwork.
+     *
+     * @param artwork Media artwork
+     * @return Swatch that should be used as the background of the media notification.
+     */
+    public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) {
+        return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate());
+    }
+
+    /**
+     * Finds an appropriate background swatch from the palette of media artwork.
+     *
+     * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
+     * @return Swatch that should be used as the background of the media notification.
+     */
+    private static Palette.Swatch findBackgroundSwatch(Palette palette) {
         // by default we use the dominant palette
         Palette.Swatch dominantSwatch = palette.getDominantSwatch();
         if (dominantSwatch == null) {
-            // We're not filtering on white or black
-            mFilteredBackgroundHsl = null;
-            return Color.WHITE;
+            return new Palette.Swatch(Color.WHITE, 100);
         }
 
         if (!isWhiteOrBlack(dominantSwatch.getHsl())) {
-            mFilteredBackgroundHsl = dominantSwatch.getHsl();
-            return dominantSwatch.getRgb();
+            return dominantSwatch;
         }
         // Oh well, we selected black or white. Lets look at the second color!
         List<Palette.Swatch> swatches = palette.getSwatches();
@@ -270,38 +280,51 @@
             }
         }
         if (second == null) {
-            // We're not filtering on white or black
-            mFilteredBackgroundHsl = null;
-            return dominantSwatch.getRgb();
+            return dominantSwatch;
         }
         if (dominantSwatch.getPopulation() / highestNonWhitePopulation
                 > POPULATION_FRACTION_FOR_WHITE_OR_BLACK) {
             // The dominant swatch is very dominant, lets take it!
             // We're not filtering on white or black
-            mFilteredBackgroundHsl = null;
-            return dominantSwatch.getRgb();
+            return dominantSwatch;
         } else {
-            mFilteredBackgroundHsl = second.getHsl();
-            return second.getRgb();
+            return second;
         }
     }
 
-    private boolean isWhiteOrBlack(float[] hsl) {
-        return isBlack(hsl) || isWhite(hsl);
+    /**
+     * Generate a palette builder for media artwork.
+     *
+     * For producing a smooth background transition, the palette is extracted from only the left
+     * side of the artwork.
+     *
+     * @param artwork Media artwork
+     * @return Builder that generates the {@link Palette} for the media artwork.
+     */
+    private static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
+        // for the background we only take the left side of the image to ensure
+        // a smooth transition
+        return Palette.from(artwork)
+                .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight())
+                .clearFilters() // we want all colors, red / white / black ones too!
+                .resizeBitmapArea(RESIZE_BITMAP_AREA);
     }
 
+    private static boolean isWhiteOrBlack(float[] hsl) {
+        return isBlack(hsl) || isWhite(hsl);
+    }
 
     /**
      * @return true if the color represents a color which is close to black.
      */
-    private boolean isBlack(float[] hslColor) {
+    private static boolean isBlack(float[] hslColor) {
         return hslColor[2] <= BLACK_MAX_LIGHTNESS;
     }
 
     /**
      * @return true if the color represents a color which is close to white.
      */
-    private boolean isWhite(float[] hslColor) {
+    private static boolean isWhite(float[] hslColor) {
         return hslColor[2] >= WHITE_MIN_LIGHTNESS;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7d224fb..d926f88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,6 +15,9 @@
  */
 package com.android.systemui.statusbar.notification;
 
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_ERROR;
+
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
@@ -30,6 +33,7 @@
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.NotificationUpdateHandler;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
@@ -82,6 +86,7 @@
     final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
             = new ArrayList<>();
     private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
+    private NotificationRemoveInterceptor mRemoveInterceptor;
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -115,6 +120,11 @@
         mNotificationEntryListeners.add(listener);
     }
 
+    /** Sets the {@link NotificationRemoveInterceptor}. */
+    public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
+        mRemoveInterceptor = interceptor;
+    }
+
     /**
      * Our dependencies can have cyclic references, so some need to be lazy
      */
@@ -146,7 +156,7 @@
     /** Adds a {@link NotificationLifetimeExtender}. */
     public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
         mNotificationLifetimeExtenders.add(extender);
-        extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
+        extender.setCallback(key -> removeNotification(key, mLatestRankingMap, 0));
     }
 
     public NotificationData getNotificationData() {
@@ -158,10 +168,18 @@
         updateNotifications();
     }
 
-    public void performRemoveNotification(StatusBarNotification n) {
+    /**
+     * Requests a notification to be removed.
+     *
+     * @param n the notification to remove.
+     * @param reason why it is being removed e.g. {@link NotificationListenerService#REASON_CANCEL},
+     *               or 0 if unknown.
+     */
+    public void performRemoveNotification(StatusBarNotification n, int reason) {
         final NotificationVisibility nv = obtainVisibility(n.getKey());
         removeNotificationInternal(
-                n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
+                n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
+                reason);
     }
 
     private NotificationVisibility obtainVisibility(String key) {
@@ -193,7 +211,8 @@
     @Override
     public void handleInflationException(StatusBarNotification n, Exception e) {
         removeNotificationInternal(
-                n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */);
+                n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
+                REASON_ERROR);
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onInflationError(n, e);
         }
@@ -228,9 +247,10 @@
     }
 
     @Override
-    public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
+    public void removeNotification(String key, NotificationListenerService.RankingMap ranking,
+            int reason) {
         removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
-                false /* removedByUser */);
+                false /* removedByUser */, reason);
     }
 
     private void removeNotificationInternal(
@@ -238,7 +258,15 @@
             @Nullable NotificationListenerService.RankingMap ranking,
             @Nullable NotificationVisibility visibility,
             boolean forceRemove,
-            boolean removedByUser) {
+            boolean removedByUser,
+            int reason) {
+
+        if (mRemoveInterceptor != null
+                && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) {
+            // Remove intercepted; skip
+            return;
+        }
+
         final NotificationEntry entry = mNotificationData.get(key);
 
         abortExistingInflation(key);
@@ -342,7 +370,8 @@
 
         Dependency.get(LeakDetector.class).trackInstance(entry);
         // Construct the expanded view.
-        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+                REASON_CANCEL));
 
         abortExistingInflation(key);
 
@@ -383,7 +412,8 @@
             listener.onPreEntryUpdated(entry);
         }
 
-        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+                REASON_CANCEL));
         updateNotifications();
 
         if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d89354b..a3e18ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -247,7 +247,7 @@
      */
     public boolean showInShadeWhenBubble() {
         // We always show it in the shade if non-clearable
-        return !isClearable() || mShowInShadeWhenBubble;
+        return !isRowDismissed() && (!isClearable() || mShowInShadeWhenBubble);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index c4ecb82..94f7e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -270,6 +270,8 @@
         }
     }
 
+    // TODO: This method has side effects, it is NOT just logging that a notification
+    // was cleared, it also actually removes the notification
     private void logNotificationClear(String key, StatusBarNotification notification,
             NotificationVisibility nv) {
         final String pkg = notification.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index b81d814..ad745f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -542,6 +542,12 @@
     }
 
     @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        child.setTag(R.id.row_tag_for_content_view, mContainingNotification);
+    }
+
+    @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
         updateVisibility();
@@ -1893,4 +1899,8 @@
         }
         pw.println();
     }
+
+    public RemoteInputView getExpandedRemoteInput() {
+        return mExpandedRemoteInput;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index b54de5a..f9a98ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -200,32 +200,40 @@
 
     private boolean canSeekMedia() {
         if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+            Log.d(TAG, "Cannot seek media because the controller is invalid");
             return false;
         }
 
         long actions = mMediaController.getPlaybackState().getActions();
+        Log.d(TAG, "Playback state actions are " + actions);
         return (actions == 0 || (actions & PlaybackState.ACTION_SEEK_TO) != 0);
     }
 
     protected final Runnable mUpdatePlaybackUi = new Runnable() {
         @Override
         public void run() {
-            if (mMediaController != null && mMediaController.getMetadata() != null
-                    && mSeekBar != null) {
-                long position = mMediaController.getPlaybackState().getPosition();
-                long duration = mMediaController.getMetadata().getLong(
-                        MediaMetadata.METADATA_KEY_DURATION);
+            if (mMediaController != null && mSeekBar != null) {
+                MediaMetadata metadata = mMediaController.getMetadata();
+                PlaybackState playbackState = mMediaController.getPlaybackState();
 
-                if (mDuration != duration) {
-                    mDuration = duration;
-                    mSeekBar.setMax((int) mDuration);
-                    mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
+                if (metadata != null && playbackState != null) {
+                    long position = playbackState.getPosition();
+                    long duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+
+                    if (mDuration != duration) {
+                        mDuration = duration;
+                        mSeekBar.setMax((int) mDuration);
+                        mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
+                    }
+                    mSeekBar.setProgress((int) position);
+
+                    mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
+                } else {
+                    Log.d(TAG, "Controller missing data " + metadata + " " + playbackState);
+                    clearTimer();
                 }
-                mSeekBar.setProgress((int) position);
-
-                mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
             } else {
-                // We no longer have a media session / notification
+                Log.d(TAG, "No longer have a valid media controller");
                 clearTimer();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7a9da6b..0e7feaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -46,6 +46,7 @@
 import android.os.Bundle;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -5586,7 +5587,8 @@
             setDismissAllInProgress(false);
             for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
                 if (canChildBeDismissed(rowToRemove)) {
-                    mEntryManager.removeNotification(rowToRemove.getEntry().key, null);
+                    mEntryManager.removeNotification(rowToRemove.getEntry().key, null /* ranking */,
+                            NotificationListenerService.REASON_CANCEL_ALL);
                 } else {
                     rowToRemove.resetTranslation();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index fdf8cce..5912cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -95,7 +95,8 @@
 
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -119,7 +120,7 @@
             if (mSystemUiVisibility != newVal) {
                 mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
                         fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
-                        dockedStackBounds);
+                        dockedStackBounds, navbarColorManagedByIme);
             }
 
             notifySystemUiVisibilityChanged(mSystemUiVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 1b86693..bc2d00f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -117,8 +117,13 @@
     public void loadDimens(Resources res) {
         mClockNotificationsMargin = res.getDimensionPixelSize(
                 R.dimen.keyguard_clock_notifications_margin);
-        mContainerTopPadding = res.getDimensionPixelSize(
-                R.dimen.keyguard_clock_top_margin);
+        // Consider the lock icon when determining the minimum top padding between the status bar
+        // and top of the clock.
+        mContainerTopPadding = Math.max(res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_top_margin),
+                res.getDimensionPixelSize(R.dimen.keyguard_lock_height)
+                        + res.getDimensionPixelSize(R.dimen.keyguard_lock_padding)
+                        + res.getDimensionPixelSize(R.dimen.keyguard_clock_lock_margin));
         mBurnInPreventionOffsetX = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_x);
         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index b590ca7..b0b656a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -79,6 +79,9 @@
     private final Rect mLastDockedBounds = new Rect();
     private boolean mQsCustomizing;
 
+    private boolean mDirectReplying;
+    private boolean mNavbarColorManagedByIme;
+
     @Inject
     public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher,
             BatteryController batteryController) {
@@ -100,7 +103,7 @@
 
     public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
             int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
-            int statusBarMode) {
+            int statusBarMode, boolean navbarColorManagedByIme) {
         int oldFullscreen = mFullscreenStackVisibility;
         int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
         int diffFullscreen = newFullscreen ^ oldFullscreen;
@@ -122,12 +125,13 @@
         mFullscreenStackVisibility = newFullscreen;
         mDockedStackVisibility = newDocked;
         mLastStatusBarMode = statusBarMode;
+        mNavbarColorManagedByIme = navbarColorManagedByIme;
         mLastFullscreenBounds.set(fullscreenStackBounds);
         mLastDockedBounds.set(dockedStackBounds);
     }
 
     public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged,
-            int navigationBarMode) {
+            int navigationBarMode, boolean navbarColorManagedByIme) {
         int oldVis = mSystemUiVisibility;
         int newVis = (oldVis & ~mask) | (vis & mask);
         int diffVis = newVis ^ oldVis;
@@ -136,21 +140,24 @@
             boolean last = mNavigationLight;
             mHasLightNavigationBar = isLight(vis, navigationBarMode,
                     View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
-            mNavigationLight = mHasLightNavigationBar && !mForceDarkForScrim && !mQsCustomizing;
+            mNavigationLight = mHasLightNavigationBar
+                    && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
+                    && !mQsCustomizing;
             if (mNavigationLight != last) {
                 updateNavigation();
             }
         }
         mSystemUiVisibility = newVis;
         mLastNavigationBarMode = navigationBarMode;
+        mNavbarColorManagedByIme = navbarColorManagedByIme;
     }
 
     private void reevaluate() {
         onSystemUiVisibilityChanged(mFullscreenStackVisibility,
                 mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds,
-                true /* sbModeChange*/, mLastStatusBarMode);
+                true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme);
         onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */,
-                mLastNavigationBarMode);
+                mLastNavigationBarMode, mNavbarColorManagedByIme);
     }
 
     public void setQsCustomizing(boolean customizing) {
@@ -159,6 +166,16 @@
         reevaluate();
     }
 
+    /**
+     * Sets whether the direct-reply is in use or not.
+     * @param directReplying {@code true} when the direct-reply is in-use.
+     */
+    public void setDirectReplying(boolean directReplying) {
+        if (mDirectReplying == directReplying) return;
+        mDirectReplying = directReplying;
+        reevaluate();
+    }
+
     public void setScrimState(ScrimState scrimState, float scrimBehindAlpha,
             GradientColors scrimInFrontColor) {
         boolean forceDarkForScrimLast = mForceDarkForScrim;
@@ -260,7 +277,9 @@
         pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode);
 
         pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim);
-        pw.print(" mQsCustomizing="); pw.println(mQsCustomizing);
+        pw.print(" mQsCustomizing="); pw.print(mQsCustomizing);
+        pw.print(" mDirectReplying="); pw.println(mDirectReplying);
+        pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme);
 
         pw.println();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index a831a5d..3b3bd21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.content.Context;
@@ -27,6 +28,7 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
 import android.util.AttributeSet;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -66,6 +68,7 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final AccessibilityController mAccessibilityController;
     private final DockManager mDockManager;
+    private final Handler mMainHandler;
 
     private int mLastState = 0;
     private boolean mTransientBiometricsError;
@@ -82,6 +85,8 @@
     private boolean mLastBouncerVisible;
     private int mIconColor;
     private float mDozeAmount;
+    private int mIconRes;
+    private boolean mWasPulsingOnThisFrame;
 
     private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
     private final DockManager.DockEventListener mDockEventListener =
@@ -133,7 +138,8 @@
             StatusBarStateController statusBarStateController,
             ConfigurationController configurationController,
             AccessibilityController accessibilityController,
-            @Nullable DockManager dockManager) {
+            @Nullable DockManager dockManager,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
         super(context, attrs);
         mContext = context;
         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
@@ -142,6 +148,7 @@
         mConfigurationController = configurationController;
         mStatusBarStateController = statusBarStateController;
         mDockManager = dockManager;
+        mMainHandler = mainHandler;
     }
 
     @Override
@@ -214,39 +221,36 @@
                     mPulsing, mLastDozing, mDozing, mBouncerVisible);
             boolean isAnim = iconAnimRes != -1;
 
-            Drawable icon;
-            if (isAnim) {
-                // Load the animation resource.
-                icon = mContext.getDrawable(iconAnimRes);
-            } else {
-                // Load the static icon resource based on the current state.
-                icon = getIconForState(state);
-            }
+            int iconRes = isAnim ? iconAnimRes : getIconForState(state);
+            if (iconRes != mIconRes) {
+                mIconRes = iconRes;
 
-            final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
-                    ? (AnimatedVectorDrawable) icon
-                    : null;
-            setImageDrawable(icon, false);
-            updateDarkTint();
-            if (mIsFaceUnlockState) {
-                announceForAccessibility(getContext().getString(
-                    R.string.accessibility_scanning_face));
-            }
+                Drawable icon = mContext.getDrawable(iconRes);
+                final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
+                        ? (AnimatedVectorDrawable) icon
+                        : null;
+                setImageDrawable(icon, false);
+                if (mIsFaceUnlockState) {
+                    announceForAccessibility(getContext().getString(
+                            R.string.accessibility_scanning_face));
+                }
 
-            if (animation != null && isAnim) {
-                animation.forceAnimationOnUI();
-                animation.clearAnimationCallbacks();
-                animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
-                    @Override
-                    public void onAnimationEnd(Drawable drawable) {
-                        if (getDrawable() == animation && state == getState()
-                                && doesAnimationLoop(iconAnimRes)) {
-                            animation.start();
+                if (animation != null && isAnim) {
+                    animation.forceAnimationOnUI();
+                    animation.clearAnimationCallbacks();
+                    animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
+                        @Override
+                        public void onAnimationEnd(Drawable drawable) {
+                            if (getDrawable() == animation && state == getState()
+                                    && doesAnimationLoop(iconAnimRes)) {
+                                animation.start();
+                            }
                         }
-                    }
-                });
-                animation.start();
+                    });
+                    animation.start();
+                }
             }
+            updateDarkTint();
 
             if (isAnim && !mLastScreenOn) {
                 removeCallbacks(mDrawOffTimeout);
@@ -300,7 +304,7 @@
         }
     }
 
-    private Drawable getIconForState(int state) {
+    private int getIconForState(int state) {
         int iconRes;
         switch (state) {
             case STATE_LOCKED:
@@ -318,25 +322,27 @@
                 throw new IllegalArgumentException();
         }
 
-        return mContext.getDrawable(iconRes);
+        return iconRes;
     }
 
     private boolean doesAnimationLoop(int resourceId) {
         return resourceId == com.android.internal.R.anim.lock_scanning;
     }
 
-    private static int getAnimationResForTransition(int oldState, int newState,
+    private int getAnimationResForTransition(int oldState, int newState,
             boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing,
             boolean bouncerVisible) {
 
         // Never animate when screen is off
-        if (dozing && !pulsing) {
+        if (dozing && !pulsing && !mWasPulsingOnThisFrame) {
             return -1;
         }
 
         boolean isError = oldState != STATE_BIOMETRICS_ERROR && newState == STATE_BIOMETRICS_ERROR;
         boolean justUnlocked = oldState != STATE_LOCK_OPEN && newState == STATE_LOCK_OPEN;
         boolean justLocked = oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED;
+        boolean nowPulsing = !wasPulsing && pulsing;
+        boolean turningOn = wasDozing && !dozing && !mWasPulsingOnThisFrame;
 
         if (isError) {
             return com.android.internal.R.anim.lock_to_error;
@@ -346,7 +352,7 @@
             return com.android.internal.R.anim.lock_lock;
         } else if (newState == STATE_SCANNING_FACE && bouncerVisible) {
             return com.android.internal.R.anim.lock_scanning;
-        } else if (!wasPulsing && pulsing && newState != STATE_LOCK_OPEN) {
+        } else if ((nowPulsing || turningOn) && newState != STATE_LOCK_OPEN) {
             return com.android.internal.R.anim.lock_in;
         }
         return -1;
@@ -377,6 +383,12 @@
      */
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
+        if (!mPulsing) {
+            mWasPulsingOnThisFrame = true;
+            mMainHandler.post(() -> {
+                mWasPulsingOnThisFrame = false;
+            });
+        }
         update();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index fa3377a..8aa4f03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -27,7 +27,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -328,7 +327,10 @@
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
         notifyNavigationBarScreenOn();
+
         mOverviewProxyService.addCallback(mOverviewProxyListener);
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN,
+                !isNavBarWindowVisible(), mDisplayId);
 
         // Currently there is no accelerometer sensor on non-default display.
         if (mIsOnDefaultDisplay) {
@@ -447,13 +449,6 @@
     }
 
     @Override
-    public void topAppWindowChanged(int displayId, boolean showMenu) {
-        if (displayId == mDisplayId && mNavigationBarView != null) {
-            mNavigationBarView.setMenuVisibility(showMenu);
-        }
-    }
-
-    @Override
     public void setWindowState(
             int displayId, @WindowType int window, @WindowVisibleState int state) {
         if (displayId == mDisplayId
@@ -464,7 +459,7 @@
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
 
             mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN,
-                    !isNavBarWindowVisible());
+                    !isNavBarWindowVisible(), mDisplayId);
             mNavigationBarView.getRotateSuggestionButton()
                     .onNavigationBarWindowVisibilityChange(isNavBarWindowVisible());
         }
@@ -505,12 +500,13 @@
         mAutoHideController.touchAutoHide();
 
         mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
-                true /* nbModeChanged */, mNavigationBarMode);
+                true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */);
     }
 
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -538,7 +534,7 @@
             }
         }
         mLightBarController.onNavigationVisibilityChanged(
-                vis, mask, nbModeChanged, mNavigationBarMode);
+                vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme);
     }
 
     private @TransitionMode int computeBarMode(int oldVis, int newVis) {
@@ -824,7 +820,6 @@
                     activityManager.stopSystemLockTaskMode();
                     // When exiting refresh disabled flags.
                     mNavigationBarView.updateNavButtonIcons();
-                    mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, false);
                 }
             }
 
@@ -876,9 +871,10 @@
         boolean clickable = (flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
         boolean longClickable = (flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
         mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
-        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable);
         mOverviewProxyService.setSystemUiStateFlag(
-                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable);
+                SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable, mDisplayId);
+        mOverviewProxyService.setSystemUiStateFlag(
+                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable, mDisplayId);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 22636de..831d882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -268,8 +268,6 @@
 
         // Set up the context group of buttons
         mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
-        final ContextualButton menuButton = new ContextualButton(R.id.menu,
-                R.drawable.ic_sysbar_menu);
         final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
                 R.drawable.ic_ime_switcher_default);
         final RotationContextButton rotateSuggestionButton = new RotationContextButton(
@@ -278,7 +276,6 @@
         final ContextualButton accessibilityButton =
                 new ContextualButton(R.id.accessibility_button,
                         R.drawable.ic_sysbar_accessibility_button);
-        mContextualButtonGroup.addButton(menuButton);
         mContextualButtonGroup.addButton(imeSwitcherButton);
         if (!isGesturalMode) {
             mContextualButtonGroup.addButton(rotateSuggestionButton);
@@ -306,7 +303,6 @@
         mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
         mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
         mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
-        mButtonDispatchers.put(R.id.menu, menuButton);
         mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
         mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
         mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
@@ -406,10 +402,6 @@
         return mButtonDispatchers.get(R.id.recent_apps);
     }
 
-    public ButtonDispatcher getMenuButton() {
-        return mButtonDispatchers.get(R.id.menu);
-    }
-
     public ButtonDispatcher getBackButton() {
         return mButtonDispatchers.get(R.id.back);
     }
@@ -722,10 +714,10 @@
         }
     }
 
-    public void onPanelExpandedChange(boolean expanded) {
+    public void onPanelExpandedChange() {
         updateSlippery();
         mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
-                expanded);
+                mPanelView.isFullyExpanded(), getContext().getDisplayId());
     }
 
     public void updateStates() {
@@ -795,10 +787,6 @@
         }
     }
 
-    public void setMenuVisibility(final boolean show) {
-        mContextualButtonGroup.setButtonVisibility(R.id.menu, show);
-    }
-
     public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
         mLongClickableAccessibilityButton = longClickable;
         getAccessibilityButton().setLongClickable(longClickable);
@@ -1154,16 +1142,14 @@
                         visibilityToString(getCurrentView().getVisibility()),
                         getCurrentView().getAlpha()));
 
-        pw.println(String.format("      disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f",
+        pw.println(String.format("      disabled=0x%08x vertical=%s darkIntensity=%.2f",
                         mDisabledFlags,
                         mIsVertical ? "true" : "false",
-                        getMenuButton().isVisible() ? "true" : "false",
                         getLightTransitionsController().getCurrentDarkIntensity()));
 
         dumpButton(pw, "back", getBackButton());
         dumpButton(pw, "home", getHomeButton());
         dumpButton(pw, "rcnt", getRecentsButton());
-        dumpButton(pw, "menu", getMenuButton());
         dumpButton(pw, "rota", getRotateSuggestionButton());
         dumpButton(pw, "a11y", getAccessibilityButton());
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 7b3ddf7..d521809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -833,6 +833,11 @@
             return false;
         }
         initDownStates(event);
+        // Do not let touches go to shade or QS if the bouncer is visible,
+        // but still let user swipe down to expand the panel, dismissing the bouncer.
+        if (mStatusBar.isBouncerShowing()) {
+            return true;
+        }
         if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
             mIsExpansionFromHeadsUp = true;
             MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
@@ -1000,6 +1005,13 @@
         if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
             return false;
         }
+
+        // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to
+        // pull down QS or expand the shade.
+        if (mStatusBar.isBouncerShowingScrimmed()) {
+            return false;
+        }
+
         initDownStates(event);
         // Make sure the next touch won't the blocked after the current ends.
         if (event.getAction() == MotionEvent.ACTION_UP
@@ -1304,7 +1316,11 @@
         } else if (oldState == StatusBarState.SHADE_LOCKED
                 && statusBarState == StatusBarState.KEYGUARD) {
             animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mQs.animateHeaderSlidingOut();
+            // Only animate header if the header is visible. If not, it will partially animate out
+            // the top of QS
+            if (!mQsExpanded) {
+                mQs.animateHeaderSlidingOut();
+            }
         } else {
             mKeyguardStatusBar.setAlpha(1f);
             mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index ce1d638..68eba50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -277,7 +277,7 @@
         super.panelExpansionChanged(frac, expanded);
         updateScrimFraction();
         if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
-            mBar.getNavigationBarView().onPanelExpandedChange(expanded);
+            mBar.getNavigationBarView().onPanelExpandedChange();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 93db82d..1fc40b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -436,7 +436,6 @@
         public void onUserSetupChanged() {
             final boolean userSetup = mDeviceProvisionedController.isUserSetup(
                     mDeviceProvisionedController.getCurrentUser());
-            // STOPSHIP(kozynski, b/129405675) Remove log
             Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
                     + mDeviceProvisionedController.getCurrentUser());
             if (MULTIUSER_DEBUG) {
@@ -593,6 +592,7 @@
                 updateScrimController();
             };
     private ActivityIntentHelper mActivityIntentHelper;
+    private ShadeController mShadeController;
 
     @Override
     public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -710,8 +710,8 @@
 
         setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility,
                 result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility,
-                0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds);
-        topAppWindowChanged(mDisplayId, result.mMenuVisible);
+                0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds,
+                result.mNavbarColorManagedByIme);
         // StatusBarManagerService has a back up of IME token and it's restored here.
         setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
                 result.mImeBackDisposition, result.mShowImeSwitcher);
@@ -725,11 +725,10 @@
 
         if (DEBUG) {
             Log.d(TAG, String.format(
-                    "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
+                    "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
                     numIcons,
                     result.mDisabledFlags1,
                     result.mSystemUiVisibility,
-                    result.mMenuVisible ? 1 : 0,
                     result.mImeWindowVis));
         }
 
@@ -1062,7 +1061,7 @@
         final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
                 (StatusBarRemoteInputCallback) Dependency.get(
                         NotificationRemoteInputManager.Callback.class);
-        final ShadeController shadeController = Dependency.get(ShadeController.class);
+        mShadeController = Dependency.get(ShadeController.class);
         final ActivityStarter activityStarter = Dependency.get(ActivityStarter.class);
 
         mNotificationActivityStarter = new StatusBarNotificationActivityStarter(mContext,
@@ -1070,7 +1069,7 @@
                 mHeadsUpManager, activityStarter, mActivityLaunchAnimator,
                 mBarService, mStatusBarStateController, mKeyguardManager, mDreamManager,
                 mRemoteInputManager, mStatusBarRemoteInputCallback, mGroupManager,
-                mLockscreenUserManager, shadeController, mKeyguardMonitor,
+                mLockscreenUserManager, mShadeController, mKeyguardMonitor,
                 mNotificationInterruptionStateProvider, mMetricsLogger,
                 new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER),
                 Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController);
@@ -2077,7 +2076,8 @@
 
     @Override // CommandQueue
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -2114,7 +2114,8 @@
             }
         }
         mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
-                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
+                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode,
+                navbarColorManagedByIme);
     }
 
     @Override
@@ -2231,30 +2232,6 @@
         return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
     }
 
-    public void setLightsOn(boolean on) {
-        Log.v(TAG, "setLightsOn(" + on + ")");
-        if (on) {
-            setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
-                    mLastFullscreenStackBounds, mLastDockedStackBounds);
-        } else {
-            setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
-                    View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
-                    mLastDockedStackBounds);
-        }
-    }
-
-    @Override
-    public void topAppWindowChanged(int displayId, boolean showMenu) {
-        if (mDisplayId != displayId) return;
-        if (SPEW) {
-            Log.d(TAG, "display#" + displayId + ": "
-                    + (showMenu ? "showing" : "hiding") + " the MENU button");
-        }
-
-        // See above re: lights-out policy for legacy apps.
-        if (showMenu) setLightsOn(true);
-    }
-
     public static String viewInfo(View v) {
         return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
                 + ") " + v.getWidth() + "x" + v.getHeight() + "]";
@@ -3584,7 +3561,7 @@
 
         // Notify overview proxy service of the new states
         Dependency.get(OverviewProxyService.class).setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING,
-                isBouncerShowing());
+                isBouncerShowing(), mContext.getDisplayId());
     }
 
     /**
@@ -4344,6 +4321,13 @@
     @Override
     public void startPendingIntentDismissingKeyguard(
             final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
+        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, null /* row */);
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(
+            final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
+            View associatedView) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
@@ -4351,7 +4335,8 @@
         executeActionDismissingKeyguard(() -> {
             try {
                 intent.send(null, 0, null, null, null, null, getActivityOptions(
-                        null /* animationAdapter */));
+                        mActivityLaunchAnimator.getLaunchAnimation(associatedView,
+                                mShadeController.isOccluded())));
             } catch (PendingIntent.CanceledException e) {
                 // the stack trace isn't very helpful here.
                 // Just log the exception message.
@@ -4428,6 +4413,13 @@
     }
 
     /**
+     * @return Whether the security bouncer from Keyguard is showing.
+     */
+    public boolean isBouncerShowingScrimmed() {
+        return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
+    }
+
+    /**
      * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
      *         return PackageManager for mContext
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7fe8906..93168db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -40,7 +40,6 @@
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
-import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -192,9 +191,9 @@
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
         mStatusBarStateController.addCallback(this);
         Dependency.get(ConfigurationController.class).addCallback(this);
-        mLastGesturalNav = QuickStepContract.isGesturalMode(
+        mGesturalNav = QuickStepContract.isGesturalMode(
                 Dependency.get(NavigationModeController.class).addListener(this));
-        mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
+        mDockManager = Dependency.get(DockManager.class);
         if (mDockManager != null) {
             mDockManager.addListener(mDockEventListener);
             mIsDocked = mDockManager.isDocked();
@@ -484,7 +483,7 @@
             // by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
             reset(isOccluding /* hideBouncerWhenShowing*/);
         }
-        if (animate && !occluded && mShowing) {
+        if (animate && !occluded && mShowing && !mBouncer.isShowing()) {
             mStatusBar.animateKeyguardUnoccluding();
         }
     }
@@ -662,7 +661,13 @@
     public boolean onBackPressed(boolean hideImmediately) {
         if (mBouncer.isShowing()) {
             mStatusBar.endAffordanceLaunch();
-            reset(hideImmediately);
+            // The second condition is for SIM card locked bouncer
+            if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) {
+                hideBouncer(false);
+                updateStates();
+            } else {
+                reset(hideImmediately);
+            }
             return true;
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index e4af15c..e00d439 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+
 import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
 
 import android.app.ActivityManager;
@@ -483,7 +485,7 @@
         // We have to post it to the UI thread for synchronization
         mMainThreadHandler.post(() -> {
             Runnable removeRunnable =
-                    () -> mEntryManager.performRemoveNotification(notification);
+                    () -> mEntryManager.performRemoveNotification(notification, REASON_CLICK);
             if (mPresenter.isCollapsing()) {
                 // To avoid lags we're only performing the remove
                 // after the shade was collapsed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 471d511..0865eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.view.View;
@@ -65,6 +66,7 @@
     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
     private final Context mContext;
     private final ActivityIntentHelper mActivityIntentHelper;
+    private final NotificationGroupManager mGroupManager;
     private View mPendingWorkRemoteInputView;
     private View mPendingRemoteInputView;
     private final ShadeController mShadeController = Dependency.get(ShadeController.class);
@@ -72,11 +74,12 @@
     private final CommandQueue mCommandQueue;
     private int mDisabled2;
     protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
+    private Handler mMainHandler = new Handler();
 
     /**
      */
     @Inject
-    public StatusBarRemoteInputCallback(Context context) {
+    public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) {
         mContext = context;
         mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
                 new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
@@ -85,15 +88,15 @@
         mCommandQueue = getComponent(context, CommandQueue.class);
         mCommandQueue.addCallback(this);
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
+        mGroupManager = groupManager;
     }
 
     @Override
     public void onStateChanged(int state) {
         if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) {
             if (!mStatusBarStateController.isKeyguardRequested()) {
-                if (mPendingRemoteInputView != null
-                        && mPendingRemoteInputView.isAttachedToWindow()) {
-                    mPendingRemoteInputView.post(mPendingRemoteInputView::callOnClick);
+                if (mPendingRemoteInputView != null) {
+                    mMainHandler.post(mPendingRemoteInputView::callOnClick);
                 }
                 mPendingRemoteInputView = null;
             }
@@ -159,6 +162,10 @@
         if (mKeyguardMonitor.isShowing()) {
             onLockedRemoteInput(row, clickedView);
         } else {
+            if (row.isChildInGroup() && !row.areChildrenExpanded()) {
+                // The group isn't expanded, let's make sure it's visible!
+                mGroupManager.toggleGroupExpansion(row.getStatusBarNotification());
+            }
             row.setUserExpanded(true);
             row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 9c7a1e3..3fc9b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -212,13 +212,15 @@
 
     @Nullable
     private String generateTimeRemainingString() {
-        if (mEstimate == null) {
-            return null;
-        }
+        synchronized (mFetchCallbacks) {
+            if (mEstimate == null) {
+                return null;
+            }
 
-        String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
-        return PowerUtil.getBatteryRemainingShortStringFormatted(
-                mContext, mEstimate.getEstimateMillis());
+            String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
+            return PowerUtil.getBatteryRemainingShortStringFormatted(
+                    mContext, mEstimate.getEstimateMillis());
+        }
     }
 
     private void updateEstimateInBackground() {
@@ -230,9 +232,11 @@
         mFetchingEstimate = true;
         Dependency.get(Dependency.BG_HANDLER).post(() -> {
             // Only fetch the estimate if they are enabled
-            mEstimate = null;
-            if (mEstimates.isHybridNotificationEnabled()) {
-                updateEstimate();
+            synchronized (mFetchCallbacks) {
+                mEstimate = null;
+                if (mEstimates.isHybridNotificationEnabled()) {
+                    updateEstimate();
+                }
             }
             mFetchingEstimate = false;
             Dependency.get(Dependency.MAIN_HANDLER).post(this::notifyEstimateFetchCallbacks);
@@ -240,9 +244,8 @@
     }
 
     private void notifyEstimateFetchCallbacks() {
-        String estimate = generateTimeRemainingString();
-
         synchronized (mFetchCallbacks) {
+            String estimate = generateTimeRemainingString();
             for (EstimateFetchCompletion completion : mFetchCallbacks) {
                 completion.onBatteryRemainingEstimateRetrieved(estimate);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
index db2523e..98ab3e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -61,7 +61,6 @@
         mSettingsObserver = new ContentObserver(mainHandler) {
             @Override
             public void onChange(boolean selfChange, Uri uri, int userId) {
-                // STOPSHIP(kozynski, b/129405675) Remove log
                 Log.d(TAG, "Setting change: " + uri);
                 if (mUserSetupUri.equals(uri)) {
                     notifySetupChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index c1950a2..43795dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -63,6 +63,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.LightBarController;
 
 import java.util.function.Consumer;
 
@@ -516,10 +517,12 @@
         private final Drawable mBackground;
         private RemoteInputView mRemoteInputView;
         boolean mShowImeOnInputConnection;
+        private LightBarController mLightBarController;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
             mBackground = getBackground();
+            mLightBarController = Dependency.get(LightBarController.class);
         }
 
         private void defocusIfNeeded(boolean animate) {
@@ -558,6 +561,9 @@
             if (!focused) {
                 defocusIfNeeded(true /* animate */);
             }
+            if (!mRemoteInputView.mRemoved) {
+                mLightBarController.setDirectReplying(focused);
+            }
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 0f7a0f0..640f0f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -349,7 +349,7 @@
                             smartReplyController.smartActionClicked(
                                     entry, actionIndex, action, smartActions.fromAssistant);
                             headsUpManager.removeNotification(entry.key, true);
-                        });
+                        }, entry.getRow());
         if (useDelayedOnClickListener) {
             onClickListener = new DelayedOnClickListener(onClickListener,
                     smartReplyView.mConstants.getOnClickInitDelay());
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 8380b19..89aa797 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -68,7 +68,8 @@
         mThemeManager = new ThemeOverlayManager(
                 mContext.getSystemService(OverlayManager.class),
                 AsyncTask.THREAD_POOL_EXECUTOR,
-                mContext.getString(R.string.launcher_overlayable_package));
+                mContext.getString(R.string.launcher_overlayable_package),
+                mContext.getString(R.string.themepicker_overlayable_package));
         final Handler bgHandler = Dependency.get(Dependency.BG_HANDLER);
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_SWITCHED);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
index 27e3b2b..930016b 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
@@ -64,6 +64,9 @@
     @VisibleForTesting
     static final String OVERLAY_CATEGORY_ICON_LAUNCHER =
             "android.theme.customization.icon_pack.launcher";
+    @VisibleForTesting
+    static final String OVERLAY_CATEGORY_ICON_THEME_PICKER =
+            "android.theme.customization.icon_pack.themepicker";
 
     /*
      * All theme customization categories used by the system, in order that they should be applied,
@@ -76,7 +79,8 @@
             OVERLAY_CATEGORY_COLOR,
             OVERLAY_CATEGORY_ICON_ANDROID,
             OVERLAY_CATEGORY_ICON_SYSUI,
-            OVERLAY_CATEGORY_ICON_SETTINGS);
+            OVERLAY_CATEGORY_ICON_SETTINGS,
+            OVERLAY_CATEGORY_ICON_THEME_PICKER);
 
     /* Categories that need to applied to the current user as well as the system user. */
     @VisibleForTesting
@@ -94,12 +98,14 @@
     private final OverlayManager mOverlayManager;
     private final Executor mExecutor;
     private final String mLauncherPackage;
+    private final String mThemePickerPackage;
 
     ThemeOverlayManager(OverlayManager overlayManager, Executor executor,
-            String launcherPackage) {
+            String launcherPackage, String themePickerPackage) {
         mOverlayManager = overlayManager;
         mExecutor = executor;
         mLauncherPackage = launcherPackage;
+        mThemePickerPackage = themePickerPackage;
         mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
                 OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_FONT,
                 OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID));
@@ -109,6 +115,8 @@
                 Sets.newHashSet(OVERLAY_CATEGORY_ICON_SETTINGS));
         mTargetPackageToCategories.put(mLauncherPackage,
                 Sets.newHashSet(OVERLAY_CATEGORY_ICON_LAUNCHER));
+        mTargetPackageToCategories.put(mThemePickerPackage,
+                Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER));
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_COLOR, ANDROID_PACKAGE);
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE);
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE);
@@ -116,6 +124,7 @@
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SYSUI, SYSUI_PACKAGE);
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE);
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage);
+        mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage);
     }
 
     /**
diff --git a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
index 5cc7976..4dc8d45 100644
--- a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
+++ b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
@@ -42,7 +42,6 @@
     <item>@drawable/ic_power_low</item>
     <item>@drawable/ic_power_saver</item>
     <item>@drawable/ic_qs_bluetooth_connecting</item>
-    <item>@drawable/ic_qs_bluetooth_connecting</item>
     <item>@drawable/ic_qs_cancel</item>
     <item>@drawable/ic_qs_no_sim</item>
     <item>@drawable/ic_qs_wifi_0</item>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 76f1684..3330d1e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -84,7 +84,6 @@
         when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater);
 
         mFakeDockManager = new DockManagerFake();
-        getContext().putComponent(DockManager.class, mFakeDockManager);
 
         mCurrentUser = new MutableLiveData<>();
         mCurrentUser.setValue(MAIN_USER_ID);
@@ -92,7 +91,7 @@
 
         mClockManager = new ClockManager(getContext(), mMockInjectionInflationController,
                 mMockPluginManager, mMockColorExtractor, mMockContentResolver,
-                mMockCurrentUserObserable, mMockSettingsWrapper);
+                mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager);
 
         mClockManager.addOnClockChangedListener(mMockListener1);
         mClockManager.addOnClockChangedListener(mMockListener2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 8b0ba94..ec8dae2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -18,13 +18,18 @@
 
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -50,6 +55,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -95,10 +101,13 @@
     private FrameLayout mStatusBarView;
     @Captor
     private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
+    @Captor
+    private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
 
     private TestableBubbleController mBubbleController;
     private StatusBarWindowController mStatusBarWindowController;
     private NotificationEntryListener mEntryListener;
+    private NotificationRemoveInterceptor mRemoveInterceptor;
 
     private NotificationTestHelper mNotificationTestHelper;
     private ExpandableNotificationRow mRow;
@@ -167,6 +176,10 @@
         verify(mNotificationEntryManager, atLeastOnce())
                 .addNotificationEntryListener(mEntryListenerCaptor.capture());
         mEntryListener = mEntryListenerCaptor.getValue();
+        // And the remove interceptor
+        verify(mNotificationEntryManager, atLeastOnce())
+                .setNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
+        mRemoveInterceptor = mRemoveInterceptorCaptor.getValue();
     }
 
     @Test
@@ -199,6 +212,27 @@
     }
 
     @Test
+    public void testRemoveBubble_withDismissedNotif() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+        // Make it look like dismissed notif
+        mRow.getEntry().setShowInShadeWhenBubble(false);
+
+        // Now remove the bubble
+        mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+
+        // Since the notif is dismissed, once the bubble is removed, performRemoveNotification gets
+        // called to really remove the notif
+        verify(mNotificationEntryManager, times(1)).performRemoveNotification(
+                mRow.getEntry().notification, 0);
+        assertFalse(mBubbleController.hasBubbles());
+    }
+
+    @Test
     public void testDismissStack() {
         mBubbleController.updateBubble(mRow.getEntry());
         verify(mNotificationEntryManager, times(1)).updateNotifications();
@@ -455,8 +489,7 @@
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Simulate notification cancellation.
-        mEntryListener.onEntryRemoved(mRow.getEntry(), null /* notificationVisibility (unused) */,
-                false /* removedbyUser */);
+        mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().key, REASON_APP_CANCEL);
 
         mBubbleController.expandStackAndSelectBubble(key);
     }
@@ -511,6 +544,83 @@
         verify(mDeleteIntent, never()).send();
     }
 
+    @Test
+    public void testRemoveBubble_succeeds_appCancel() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+
+        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+                mRow.getEntry().key, REASON_APP_CANCEL);
+
+        // Cancels always remove so no need to intercept
+        assertFalse(intercepted);
+        assertFalse(mBubbleController.hasBubbles());
+    }
+
+    @Test
+    public void removeBubble_fails_clearAll()  {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+                mRow.getEntry().key, REASON_CANCEL_ALL);
+
+        // Intercept!
+        assertTrue(intercepted);
+        // Should update show in shade state
+        assertFalse(mRow.getEntry().showInShadeWhenBubble());
+
+        verify(mNotificationEntryManager, never()).performRemoveNotification(
+                any(), anyInt());
+        assertTrue(mBubbleController.hasBubbles());
+    }
+
+    @Test
+    public void removeBubble_fails_userDismissNotif() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+                mRow.getEntry().key, REASON_CANCEL);
+
+        // Intercept!
+        assertTrue(intercepted);
+        // Should update show in shade state
+        assertFalse(mRow.getEntry().showInShadeWhenBubble());
+
+        verify(mNotificationEntryManager, never()).performRemoveNotification(
+                any(), anyInt());
+        assertTrue(mBubbleController.hasBubbles());
+    }
+
+    @Test
+    public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+        // Dismiss the bubble
+        mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+        assertFalse(mBubbleController.hasBubbles());
+
+        // Dismiss the notification
+        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+                mRow.getEntry().key, REASON_CANCEL);
+
+        // It's no longer a bubble so we shouldn't intercept
+        assertFalse(intercepted);
+    }
+
     static class TestableBubbleController extends BubbleController {
         // Let's assume surfaces can be synchronized immediately.
         TestableBubbleController(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index df014a4..af2de1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -73,8 +73,6 @@
         doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
 
         mDockManagerFake = spy(new DockManagerFake());
-        mContext.putComponent(DockManager.class, mDockManagerFake);
-
         mDockHandler = new DozeDockHandler(mContext, mMachine, mHost, mConfig,
                 Handler.createAsync(Looper.myLooper()), mDockManagerFake);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 1ac6bef..6979fd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -38,7 +38,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerFake;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -84,7 +83,6 @@
         mSensors = new FakeSensorManager(mContext);
         mWakeLock = new WakeLockFake();
         mDockManagerFake = spy(new DockManagerFake());
-        mContext.putComponent(DockManager.class, mDockManagerFake);
 
         mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters,
                 mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
new file mode 100644
index 0000000..16d665c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.leak.RotationUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link ListGridLayout}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GlobalActionsColumnLayoutTest extends SysuiTestCase {
+
+    private GlobalActionsColumnLayout mColumnLayout;
+
+    @Before
+    public void setUp() throws Exception {
+        mColumnLayout = spy((GlobalActionsColumnLayout)
+                LayoutInflater.from(mContext).inflate(R.layout.global_actions_column, null));
+    }
+
+    @Test
+    public void testShouldReverseListItems() {
+        doReturn(View.LAYOUT_DIRECTION_LTR).when(mColumnLayout).getCurrentLayoutDirection();
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(true, mColumnLayout.shouldReverseListItems());
+
+        doReturn(View.LAYOUT_DIRECTION_RTL).when(mColumnLayout).getCurrentLayoutDirection();
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(true, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+    }
+
+    @Test
+    public void testGetAnimationOffsetX() {
+        doReturn(50f).when(mColumnLayout).getAnimationDistance();
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(50f, mColumnLayout.getAnimationOffsetX(), .01);
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
+    }
+
+    @Test
+    public void testGetAnimationOffsetY() {
+        doReturn(50f).when(mColumnLayout).getAnimationDistance();
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(0, mColumnLayout.getAnimationOffsetY(), .01);
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(-50f, mColumnLayout.getAnimationOffsetY(), .01);
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(50f, mColumnLayout.getAnimationOffsetY(), .01);
+    }
+
+    @Test
+    public void testSnapToPowerButton_portrait() {
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        doReturn(50).when(mColumnLayout).getPowerButtonOffsetDistance();
+
+        mColumnLayout.snapToPowerButton();
+        assertEquals(Gravity.TOP | Gravity.RIGHT, mColumnLayout.getGravity());
+        assertEquals(50, mColumnLayout.getPaddingTop(), .01);
+    }
+
+    @Test
+    public void testCenterAlongEdge_portrait() {
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+
+        mColumnLayout.centerAlongEdge();
+        assertEquals(Gravity.CENTER_VERTICAL | Gravity.RIGHT, mColumnLayout.getGravity());
+        assertEquals(0, mColumnLayout.getPaddingTop(), .01);
+    }
+
+    @Test
+    public void testUpdateSnap_initialState() {
+        doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should do nothing, since snap has not changed from init state
+
+        verify(mColumnLayout, times(0)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+    }
+
+    @Test
+    public void testUpdateSnap_snapThenSnap() {
+        doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should snap to power button
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+
+        mColumnLayout.updateSnap(); // should do nothing, since this is the same state as last time
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+    }
+
+    @Test
+    public void testUpdateSnap_snapThenCenter() {
+        doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should snap to power button
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+
+        doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should center to edge
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(1)).centerAlongEdge();
+    }
+
+    @Test
+    public void testShouldSnapToPowerButton_vertical() {
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
+        doReturn(1000).when(mColumnLayout).getMeasuredHeight();
+        View wrapper = spy(new View(mContext, null));
+        doReturn(wrapper).when(mColumnLayout).getWrapper();
+        doReturn(500).when(wrapper).getMeasuredHeight();
+
+        assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
+
+        doReturn(600).when(mColumnLayout).getMeasuredHeight();
+
+        assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
+    }
+
+    @Test
+    public void testShouldSnapToPowerButton_horizontal() {
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
+        doReturn(1000).when(mColumnLayout).getMeasuredWidth();
+        View wrapper = spy(new View(mContext, null));
+        doReturn(wrapper).when(mColumnLayout).getWrapper();
+        doReturn(500).when(wrapper).getMeasuredWidth();
+
+        assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
+
+        doReturn(600).when(mColumnLayout).getMeasuredWidth();
+
+        assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
index 3c52e9d..a396f3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
@@ -18,12 +18,8 @@
 
 import static junit.framework.Assert.assertEquals;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
@@ -32,7 +28,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.MultiListLayout;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.leak.RotationUtils;
@@ -49,61 +44,12 @@
 public class GlobalActionsGridLayoutTest extends SysuiTestCase {
 
     private GlobalActionsGridLayout mGridLayout;
-    private TestAdapter mAdapter;
     private ListGridLayout mListGrid;
 
-    private class TestAdapter extends MultiListLayout.MultiListAdapter {
-        @Override
-        public void onClickItem(int index) { }
-
-        @Override
-        public boolean onLongClickItem(int index) {
-            return true;
-        }
-
-        @Override
-        public int countSeparatedItems() {
-            return -1;
-        }
-
-        @Override
-        public int countListItems() {
-            return -1;
-        }
-
-        @Override
-        public boolean shouldBeSeparated(int position) {
-            return false;
-        }
-
-        @Override
-        public int getCount() {
-            return countSeparatedItems() + countListItems();
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return null;
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return -1;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            return null;
-        }
-    }
-
-
     @Before
     public void setUp() throws Exception {
         mGridLayout = spy((GlobalActionsGridLayout)
                 LayoutInflater.from(mContext).inflate(R.layout.global_actions_grid, null));
-        mAdapter = spy(new TestAdapter());
-        mGridLayout.setAdapter(mAdapter);
         mListGrid = spy(mGridLayout.getListView());
         doReturn(mListGrid).when(mGridLayout).getListView();
     }
@@ -122,7 +68,7 @@
 
     @Test
     public void testShouldReverseListItems() {
-        doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getLayoutDirection();
+        doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getCurrentLayoutDirection();
 
         doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
         assertEquals(false, mGridLayout.shouldReverseListItems());
@@ -133,7 +79,7 @@
         doReturn(RotationUtils.ROTATION_SEASCAPE).when(mGridLayout).getCurrentRotation();
         assertEquals(true, mGridLayout.shouldReverseListItems());
 
-        doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getLayoutDirection();
+        doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getCurrentLayoutDirection();
 
         doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
         assertEquals(true, mGridLayout.shouldReverseListItems());
@@ -185,123 +131,26 @@
         assertEquals(0f, mGridLayout.getAnimationOffsetY(), .01);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testOnUpdateList_noAdapter() {
-        mGridLayout.setAdapter(null);
-        mGridLayout.updateList();
-    }
-
     @Test
-    public void testOnUpdateList_noItems() {
-        doReturn(0).when(mAdapter).countSeparatedItems();
-        doReturn(0).when(mAdapter).countListItems();
-        mGridLayout.updateList();
-
-        ViewGroup separatedView = mGridLayout.getSeparatedView();
-        ListGridLayout listView = mGridLayout.getListView();
-
-        assertEquals(0, separatedView.getChildCount());
-        assertEquals(View.GONE, separatedView.getVisibility());
-
-        verify(mListGrid, times(0)).addItem(any());
-    }
-
-    @Test
-    public void testOnUpdateList_resizesFirstSeparatedItem() {
-        doReturn(1).when(mAdapter).countSeparatedItems();
-        doReturn(0).when(mAdapter).countListItems();
+    public void testUpdateSeparatedItemSize() {
         View firstView = new View(mContext, null);
         View secondView = new View(mContext, null);
 
-        doReturn(firstView).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+        ViewGroup separatedView = mGridLayout.getSeparatedView();
+        separatedView.addView(firstView);
 
-        mGridLayout.updateList();
+        mGridLayout.updateSeparatedItemSize();
 
         ViewGroup.LayoutParams childParams = firstView.getLayoutParams();
         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.width);
         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.height);
 
-        doReturn(2).when(mAdapter).countSeparatedItems();
-        doReturn(secondView).when(mAdapter).getView(eq(1), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(1);
+        separatedView.addView(secondView);
 
-        mGridLayout.updateList();
+        mGridLayout.updateSeparatedItemSize();
 
         childParams = firstView.getLayoutParams();
         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.width);
         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.height);
-
-
-    }
-
-    @Test
-    public void testOnUpdateList_onlySeparatedItems() {
-        doReturn(1).when(mAdapter).countSeparatedItems();
-        doReturn(0).when(mAdapter).countListItems();
-        View testView = new View(mContext, null);
-        doReturn(testView).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(0);
-
-        mGridLayout.updateList();
-
-        verify(mListGrid, times(0)).addItem(any());
-    }
-
-    @Test
-    public void testOnUpdateList_oneSeparatedOneList() {
-        doReturn(1).when(mAdapter).countSeparatedItems();
-        doReturn(1).when(mAdapter).countListItems();
-        View view1 = new View(mContext, null);
-        View view2 = new View(mContext, null);
-
-        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(0);
-
-        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(1);
-
-        mGridLayout.updateList();
-
-        ViewGroup separatedView = mGridLayout.getSeparatedView();
-
-        assertEquals(1, separatedView.getChildCount());
-        assertEquals(View.VISIBLE, separatedView.getVisibility());
-        assertEquals(view1, separatedView.getChildAt(0));
-
-        verify(mListGrid, times(1)).addItem(view2);
-    }
-
-    @Test
-    public void testOnUpdateList_fourInList() {
-        doReturn(0).when(mAdapter).countSeparatedItems();
-        doReturn(4).when(mAdapter).countListItems();
-        View view1 = new View(mContext, null);
-        View view2 = new View(mContext, null);
-        View view3 = new View(mContext, null);
-        View view4 = new View(mContext, null);
-
-        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(0);
-
-        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(1);
-
-        doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(2);
-
-        doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(3);
-
-        mGridLayout.updateList();
-
-        ViewGroup separatedView = mGridLayout.getSeparatedView();
-        assertEquals(0, separatedView.getChildCount());
-        assertEquals(View.GONE, separatedView.getVisibility());
-
-        verify(mListGrid, times(1)).addItem(view1);
-        verify(mListGrid, times(1)).addItem(view2);
-        verify(mListGrid, times(1)).addItem(view3);
-        verify(mListGrid, times(1)).addItem(view4);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
new file mode 100644
index 0000000..16dcd65
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.MultiListLayout;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for {@link ListGridLayout}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GlobalActionsLayoutTest extends SysuiTestCase {
+
+    private TestLayout mLayout;
+    private TestAdapter mAdapter;
+
+    private class TestAdapter extends MultiListLayout.MultiListAdapter {
+        @Override
+        public void onClickItem(int index) { }
+
+        @Override
+        public boolean onLongClickItem(int index) {
+            return true;
+        }
+
+        @Override
+        public int countSeparatedItems() {
+            return -1;
+        }
+
+        @Override
+        public int countListItems() {
+            return -1;
+        }
+
+        @Override
+        public boolean shouldBeSeparated(int position) {
+            return false;
+        }
+
+        @Override
+        public int getCount() {
+            return countSeparatedItems() + countListItems();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return null;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return -1;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            return null;
+        }
+    }
+
+    private class TestLayout extends GlobalActionsLayout {
+        ArrayList<View> mSeparatedViews = new ArrayList<>();
+        ArrayList<View> mListViews = new ArrayList<>();
+        boolean mSeparatedViewVisible = false;
+
+        TestLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        protected boolean shouldReverseListItems() {
+            return false;
+        }
+
+        @Override
+        public float getAnimationOffsetX() {
+            return 0;
+        }
+
+        @Override
+        public float getAnimationOffsetY() {
+            return 0;
+        }
+
+        @Override
+        protected void addToListView(View v, boolean reverse) {
+            if (reverse) {
+                mListViews.add(0, v);
+            } else {
+                mListViews.add(v);
+            }
+        }
+
+        @Override
+        protected void addToSeparatedView(View v, boolean reverse) {
+            if (reverse) {
+                mSeparatedViews.add(0, v);
+            } else {
+                mSeparatedViews.add(v);
+            }
+        }
+
+        @Override
+        protected void setSeparatedViewVisibility(boolean visible) {
+            mSeparatedViewVisible = visible;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mLayout = spy(new TestLayout(mContext, null));
+        mAdapter = spy(new TestAdapter());
+        mLayout.setAdapter(mAdapter);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testOnUpdateList_noAdapter() {
+        mLayout.setAdapter(null);
+        mLayout.updateList();
+    }
+
+    @Test
+    public void testOnUpdateList_noItems() {
+        doReturn(0).when(mAdapter).countSeparatedItems();
+        doReturn(0).when(mAdapter).countListItems();
+        mLayout.updateList();
+
+        assertEquals(0, mLayout.mSeparatedViews.size());
+        assertEquals(0, mLayout.mListViews.size());
+
+        assertEquals(false, mLayout.mSeparatedViewVisible);
+    }
+
+    @Test
+    public void testOnUpdateList_oneSeparatedOneList() {
+        doReturn(1).when(mAdapter).countSeparatedItems();
+        doReturn(1).when(mAdapter).countListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+        mLayout.updateList();
+
+        assertEquals(1, mLayout.mSeparatedViews.size());
+        assertEquals(1, mLayout.mListViews.size());
+        assertEquals(view1, mLayout.mSeparatedViews.get(0));
+        assertEquals(view2, mLayout.mListViews.get(0));
+    }
+
+
+    @Test
+    public void testOnUpdateList_twoSeparatedItems() {
+        doReturn(2).when(mAdapter).countSeparatedItems();
+        doReturn(0).when(mAdapter).countListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(1);
+
+        mLayout.updateList();
+
+        assertEquals(2, mLayout.mSeparatedViews.size());
+        assertEquals(0, mLayout.mListViews.size());
+
+        assertEquals(view1, mLayout.mSeparatedViews.get(0));
+        assertEquals(view2, mLayout.mSeparatedViews.get(1));
+
+        // if separated view has items in it, should be made visible
+        assertEquals(true, mLayout.mSeparatedViewVisible);
+    }
+
+    @Test
+    public void testOnUpdateList_twoSeparatedItems_reverse() {
+        doReturn(2).when(mAdapter).countSeparatedItems();
+        doReturn(0).when(mAdapter).countListItems();
+        doReturn(true).when(mLayout).shouldReverseListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(1);
+
+        mLayout.updateList();
+
+        assertEquals(2, mLayout.mSeparatedViews.size());
+        assertEquals(0, mLayout.mListViews.size());
+
+        // separated view items are not reversed in current implementation, and this is intentional!
+        assertEquals(view1, mLayout.mSeparatedViews.get(0));
+        assertEquals(view2, mLayout.mSeparatedViews.get(1));
+    }
+
+    @Test
+    public void testOnUpdateList_fourInList() {
+        doReturn(0).when(mAdapter).countSeparatedItems();
+        doReturn(4).when(mAdapter).countListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+        View view3 = new View(mContext, null);
+        View view4 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+        doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(2);
+
+        doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(3);
+
+        mLayout.updateList();
+
+        assertEquals(0, mLayout.mSeparatedViews.size());
+        assertEquals(4, mLayout.mListViews.size());
+        assertEquals(view1, mLayout.mListViews.get(0));
+        assertEquals(view2, mLayout.mListViews.get(1));
+        assertEquals(view3, mLayout.mListViews.get(2));
+        assertEquals(view4, mLayout.mListViews.get(3));
+    }
+
+    @Test
+    public void testOnUpdateList_fourInList_reverse() {
+        doReturn(0).when(mAdapter).countSeparatedItems();
+        doReturn(4).when(mAdapter).countListItems();
+        doReturn(true).when(mLayout).shouldReverseListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+        View view3 = new View(mContext, null);
+        View view4 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+        doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(2);
+
+        doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(3);
+
+        mLayout.updateList();
+
+        assertEquals(0, mLayout.mSeparatedViews.size());
+        assertEquals(4, mLayout.mListViews.size());
+        assertEquals(view1, mLayout.mListViews.get(3));
+        assertEquals(view2, mLayout.mListViews.get(2));
+        assertEquals(view3, mLayout.mListViews.get(1));
+        assertEquals(view4, mLayout.mListViews.get(0));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index c2f55e2..b049632 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -112,33 +112,19 @@
     @Test
     public void testSetSystemUiVisibility() {
         Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
+        mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r, false);
         waitForIdleSync();
         verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4),
-                eq(null), eq(r));
+                eq(null), eq(r), eq(false));
     }
 
     @Test
     public void testSetSystemUiVisibilityForSecondaryDisplay() {
         Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r);
+        mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r, false);
         waitForIdleSync();
         verify(mCallbacks).setSystemUiVisibility(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(3), eq(4),
-                eq(null), eq(r));
-    }
-
-    @Test
-    public void testTopAppWindowChanged() {
-        mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
-        waitForIdleSync();
-        verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true));
-    }
-
-    @Test
-    public void testTopAppWindowChangedForSecondaryDisplay() {
-        mCommandQueue.topAppWindowChanged(SECONDARY_DISPLAY, true);
-        waitForIdleSync();
-        verify(mCallbacks).topAppWindowChanged(eq(SECONDARY_DISPLAY), eq(true));
+                eq(null), eq(r), eq(false));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 1a1acdf..0800cb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -99,7 +101,7 @@
     public void testNotificationRemovalCallsRemoveNotification() {
         mListener.onNotificationRemoved(mSbn, mRanking);
         TestableLooper.get(this).processAllMessages();
-        verify(mEntryManager).removeNotification(mSbn.getKey(), mRanking);
+        verify(mEntryManager).removeNotification(eq(mSbn.getKey()), eq(mRanking), anyInt());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
index b03abec..7eeae67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertNotSame;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -24,17 +26,22 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.RemoteViews;
 
+import androidx.palette.graphics.Palette;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -43,9 +50,18 @@
 @RunWith(AndroidJUnit4.class)
 public class MediaNotificationProcessorTest extends SysuiTestCase {
 
+    private static final int BITMAP_WIDTH = 10;
+    private static final int BITMAP_HEIGHT = 10;
+
+    /**
+     * Color tolerance is borrowed from the AndroidX test utilities for Palette.
+     */
+    private static final int COLOR_TOLERANCE = 8;
+
     private MediaNotificationProcessor mProcessor;
     private Bitmap mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
     private ImageGradientColorizer mColorizer;
+    @Nullable private Bitmap mArtwork;
 
     @Before
     public void setUp() {
@@ -53,6 +69,14 @@
         mProcessor = new MediaNotificationProcessor(getContext(), getContext(), mColorizer);
     }
 
+    @After
+    public void tearDown() {
+        if (mArtwork != null) {
+            mArtwork.recycle();
+            mArtwork = null;
+        }
+    }
+
     @Test
     public void testColorizedWithLargeIcon() {
         Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
@@ -100,6 +124,36 @@
         assertNotSame(contentView, remoteViews);
     }
 
+    @Test
+    public void findBackgroundSwatch_white() {
+        // Given artwork that is completely white.
+        mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(mArtwork);
+        canvas.drawColor(Color.WHITE);
+        // WHEN the background swatch is computed
+        Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
+        // THEN the swatch color is white
+        assertCloseColors(swatch.getRgb(), Color.WHITE);
+    }
+
+    @Test
+    public void findBackgroundSwatch_red() {
+        // Given artwork that is completely red.
+        mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(mArtwork);
+        canvas.drawColor(Color.RED);
+        // WHEN the background swatch is computed
+        Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
+        // THEN the swatch color is red
+        assertCloseColors(swatch.getRgb(), Color.RED);
+    }
+
+    static void assertCloseColors(int expected, int actual) {
+        assertThat((float) Color.red(expected)).isWithin(COLOR_TOLERANCE).of(Color.red(actual));
+        assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual));
+        assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual));
+    }
+
     public static class TestableColorizer extends ImageGradientColorizer {
         private final Bitmap mBitmap;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index c8005dd..70941d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -16,13 +16,18 @@
 
 package com.android.systemui.statusbar.notification;
 
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
@@ -51,6 +56,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.InitController;
@@ -61,6 +67,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -105,6 +112,7 @@
     @Mock private ExpandableNotificationRow mRow;
     @Mock private NotificationListContainer mListContainer;
     @Mock private NotificationEntryListener mEntryListener;
+    @Mock private NotificationRemoveInterceptor mRemoveInterceptor;
     @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationListenerService.RankingMap mRankingMap;
@@ -234,6 +242,7 @@
         Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
         mEntryManager.addNotificationEntryListener(mEntryListener);
+        mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
 
         NotificationRowBinderImpl notificationRowBinder =
                 new NotificationRowBinderImpl(mContext, true /* allowLongPress */);
@@ -341,7 +350,7 @@
         mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
-        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, 0 /* reason */);
 
         verify(mEntryListener, never()).onInflationError(any(), any());
 
@@ -357,7 +366,7 @@
     public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
         com.android.systemui.util.Assert.isNotMainThread();
 
-        mEntryManager.removeNotification("not_a_real_key", mRankingMap);
+        mEntryManager.removeNotification("not_a_real_key", mRankingMap, 0 /* reason */);
 
         verify(mEntryListener, never()).onEntryRemoved(
                 eq(mEntry), any(), eq(false) /* removedByUser */);
@@ -370,7 +379,7 @@
         mEntryManager.setRowBinder(mMockedRowBinder);
 
         mEntryManager.addNotification(mSbn, mRankingMap);
-        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, 0 /* reason */);
 
         verify(mEntryListener, never()).onEntryRemoved(
                 eq(mEntry), any(), eq(false /* removedByUser */));
@@ -449,7 +458,7 @@
         mEntryManager.addNotificationLifetimeExtender(extender);
 
         // WHEN the notification is removed
-        mEntryManager.removeNotification(mEntry.key, mRankingMap);
+        mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
 
         // THEN the extender is asked to manage the lifetime
         verify(extender).setShouldManageLifetime(mEntry, true);
@@ -465,7 +474,7 @@
         mEntryManager.getNotificationData().add(mEntry);
         final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
         mEntryManager.addNotificationLifetimeExtender(extender);
-        mEntryManager.removeNotification(mEntry.key, mRankingMap);
+        mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
         assertTrue(extender.isManaging(mEntry.key));
 
         // WHEN the extender finishes its extension
@@ -485,7 +494,7 @@
         NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
         when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
         mEntryManager.addNotificationLifetimeExtender(extender);
-        mEntryManager.removeNotification(mEntry.key, mRankingMap);
+        mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
 
         // WHEN the notification is updated
         mEntryManager.updateNotification(mEntry.notification, mRankingMap);
@@ -510,13 +519,13 @@
         mEntryManager.addNotificationLifetimeExtender(extender2);
 
         // GIVEN a notification was lifetime-extended and extender2 is managing it
-        mEntryManager.removeNotification(mEntry.key, mRankingMap);
+        mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
         verify(extender1, never()).setShouldManageLifetime(mEntry, true);
         verify(extender2).setShouldManageLifetime(mEntry, true);
 
         // WHEN the extender1 changes its mind and wants to extend the lifetime of the notif
         when(extender1.shouldExtendLifetime(mEntry)).thenReturn(true);
-        mEntryManager.removeNotification(mEntry.key, mRankingMap);
+        mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
 
         // THEN extender2 stops managing the notif and extender1 starts managing it
         verify(extender1).setShouldManageLifetime(mEntry, true);
@@ -530,7 +539,45 @@
     @Test
     public void testPerformRemoveNotification_removedEntry() {
         mEntryManager.getNotificationData().remove(mSbn.getKey(), null /* ranking */);
-        mEntryManager.performRemoveNotification(mSbn);
+        mEntryManager.performRemoveNotification(mSbn, REASON_CANCEL);
+    }
+
+    @Test
+    public void testRemoveInterceptor_interceptsDontGetRemoved() {
+        // GIVEN an entry manager with a notification
+        mEntryManager.setRowBinder(mMockedRowBinder);
+        mEntryManager.getNotificationData().add(mEntry);
+
+        // GIVEN interceptor that intercepts that entry
+        when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.key), anyInt()))
+                .thenReturn(true);
+
+        // WHEN the notification is removed
+        mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
+
+        // THEN the interceptor intercepts & the entry is not removed & no listeners are called
+        assertNotNull(mEntryManager.getNotificationData().get(mEntry.key));
+        verify(mEntryListener, never()).onEntryRemoved(eq(mEntry),
+                any(NotificationVisibility.class), anyBoolean());
+    }
+
+    @Test
+    public void testRemoveInterceptor_notInterceptedGetsRemoved() {
+        // GIVEN an entry manager with a notification
+        mEntryManager.setRowBinder(mMockedRowBinder);
+        mEntryManager.getNotificationData().add(mEntry);
+
+        // GIVEN interceptor that doesn't intercept
+        when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.key), anyInt()))
+                .thenReturn(false);
+
+        // WHEN the notification is removed
+        mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
+
+        // THEN the interceptor intercepts & the entry is not removed & no listeners are called
+        assertNull(mEntryManager.getNotificationData().get(mEntry.key));
+        verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry),
+                any(NotificationVisibility.class), anyBoolean());
     }
 
     private Notification.Action createAction() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
index 1b34a75..f614354 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
@@ -70,7 +70,7 @@
 
     @Test
     public void testSetSystemUiVisibilityEarlyReturnWithDifferentDisplay() {
-        mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect());
+        mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect(), false);
 
         verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
     }
@@ -78,7 +78,8 @@
     @Test
     public void testSetSystemUiVisibilityEarlyReturnWithSameVisibility() {
         mAutoHideController
-                .setSystemUiVisibility(DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect());
+                .setSystemUiVisibility(
+                        DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect(), false);
 
         verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
     }
@@ -92,7 +93,7 @@
                 View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_UNHIDE;
 
         mAutoHideController.setSystemUiVisibility(
-                DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect());
+                DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect(), false);
 
         assertEquals("System UI visibility should not be changed",
                 expectedStatus, mAutoHideController.mSystemUiVisibility);
@@ -109,7 +110,7 @@
 
         mAutoHideController.setSystemUiVisibility(
                 DEFAULT_DISPLAY, View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE,
-                2, 3, FULL_MASK, null, new Rect());
+                2, 3, FULL_MASK, null, new Rect(), false);
 
         int expectedStatus = View.VISIBLE;
         assertEquals(expectedStatus, mAutoHideController.mSystemUiVisibility);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1c6e3b0..f50cf5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -196,6 +197,17 @@
         verify(mBouncer, never()).setExpansion(anyFloat());
     }
 
+    @Test
+    public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
+        mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
+        verify(mStatusBar).animateKeyguardUnoccluding();
+
+        when(mBouncer.isShowing()).thenReturn(true);
+        clearInvocations(mStatusBar);
+        mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
+        verify(mStatusBar, never()).animateKeyguardUnoccluding();
+    }
+
     private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
 
         public TestableStatusBarKeyguardViewManager(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 51fb47b..06d76eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+
 import static org.mockito.AdditionalAnswers.answerVoid;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -224,7 +226,7 @@
                 eq(sbn.getKey()), any(NotificationVisibility.class));
 
         // Notification is removed due to FLAG_AUTO_CANCEL
-        verify(mEntryManager).performRemoveNotification(eq(sbn));
+        verify(mEntryManager).performRemoveNotification(eq(sbn), eq(REASON_CLICK));
     }
 
     @Test
@@ -253,7 +255,7 @@
         verifyZeroInteractions(mContentIntent);
 
         // Notification should not be cancelled.
-        verify(mEntryManager, never()).performRemoveNotification(eq(sbn));
+        verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
     }
 
     @Test
@@ -283,7 +285,7 @@
         verifyZeroInteractions(mContentIntent);
 
         // Notification should not be cancelled.
-        verify(mEntryManager, never()).performRemoveNotification(eq(sbn));
+        verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
     }
 
     @Test
@@ -315,6 +317,6 @@
         verifyNoMoreInteractions(mContentIntent);
 
         // Notification should not be cancelled.
-        verify(mEntryManager, never()).performRemoveNotification(eq(sbn));
+        verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index a88a595..a97832f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -70,7 +70,8 @@
                 mNotificationLockscreenUserManager);
         mDependency.putComponent(CommandQueue.class, mock(CommandQueue.class));
 
-        mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext));
+        mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
+                mock(NotificationGroupManager.class)));
         mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 8bf1606..bc468bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.util.Assert;
 
 import org.junit.After;
@@ -67,6 +68,7 @@
     @Mock private RemoteInputController mController;
     @Mock private ShortcutManager mShortcutManager;
     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+    @Mock private LightBarController mLightBarController;
     private BlockingQueueIntentReceiver mReceiver;
     private RemoteInputView mView;
 
@@ -77,6 +79,8 @@
 
         mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class,
                 mRemoteInputQuickSettingsDisabler);
+        mDependency.injectTestDependency(LightBarController.class,
+                mLightBarController);
 
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 01f3c92..8c5fac4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -634,7 +634,8 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
+                any());
     }
 
     @Test
@@ -645,7 +646,7 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any());
     }
 
     @Test
@@ -657,7 +658,8 @@
         Thread.sleep(delayMs);
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
+                any());
     }
 
     @Test
@@ -668,7 +670,8 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
+                any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java
index 4659afc..c99deb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_LAUNCHER;
 import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SETTINGS;
 import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SYSUI;
+import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_THEME_PICKER;
 import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_SHAPE;
 import static com.android.systemui.theme.ThemeOverlayManager.SETTINGS_PACKAGE;
 import static com.android.systemui.theme.ThemeOverlayManager.SYSTEM_USER_CATEGORIES;
@@ -74,6 +75,7 @@
         }
     }
 
+    private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper";
     private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
     private static final UserHandle TEST_USER = UserHandle.of(5);
     private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER);
@@ -87,7 +89,7 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         mManager = new ThemeOverlayManager(mOverlayManager, MoreExecutors.directExecutor(),
-                LAUNCHER_PACKAGE);
+                LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE);
         when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM))
                 .thenReturn(Lists.newArrayList(
                         createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_COLOR,
@@ -124,6 +126,12 @@
                                 LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, false),
                         createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER,
                                 LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, true)));
+        when(mOverlayManager.getOverlayInfosForTarget(THEMEPICKER_PACKAGE, UserHandle.SYSTEM))
+                .thenReturn(Lists.newArrayList(
+                        createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER,
+                                THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false),
+                        createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER,
+                                THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true)));
     }
 
     @Test
@@ -222,6 +230,8 @@
         verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM);
         verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE,
                 UserHandle.SYSTEM);
+        verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE,
+                UserHandle.SYSTEM);
     }
 
     private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName,
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index b9b3a61..2675e90 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -32,14 +32,17 @@
 	IconPackCircularLauncherOverlay \
 	IconPackCircularSettingsOverlay \
 	IconPackCircularSystemUIOverlay \
+	IconPackCircularThemePickerOverlay \
 	IconPackFilledAndroidOverlay \
 	IconPackFilledLauncherOverlay \
 	IconPackFilledSettingsOverlay \
 	IconPackFilledSystemUIOverlay \
+	IconPackFilledThemePickerOverlay \
 	IconPackRoundedAndroidOverlay \
 	IconPackRoundedLauncherOverlay \
 	IconPackRoundedSettingsOverlay \
 	IconPackRoundedSystemUIOverlay \
+	IconPackRoundedThemePickerUIOverlay \
 	IconShapeRoundedRectOverlay \
 	IconShapeSquareOverlay \
 	IconShapeSquircleOverlay \
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml
new file mode 100644
index 0000000..1be546840e
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="150"
+                    android:startOffset="0" android:valueFrom="0.3"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="150" android:valueFrom="0.3"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+                    android:startOffset="167" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="650" android:valueFrom="1"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml
new file mode 100644
index 0000000..c9fd424
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="317"
+                    android:startOffset="0" android:valueFrom="0.3"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="317" android:valueFrom="0.3"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+                    android:startOffset="333" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="817" android:valueFrom="1"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml
new file mode 100644
index 0000000..b34d308
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+                    android:startOffset="0" android:valueFrom="0.3"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="483" android:valueFrom="0.3"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+                    android:startOffset="500" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="983" android:valueFrom="1"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml
new file mode 100644
index 0000000..9d2b3a4f
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="fillAlpha" android:duration="650"
+                    android:startOffset="0" android:valueFrom="0.3"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="650" android:valueFrom="0.3"
+                    android:valueTo="1" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+                    android:startOffset="667" android:valueFrom="1" android:valueTo="1"
+                    android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+    <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+                    android:startOffset="1150" android:valueFrom="1"
+                    android:valueTo="0.3" android:valueType="floatType"
+                    android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml
new file mode 100644
index 0000000..943893d
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+    <objectAnimator android:propertyName="translateX" android:duration="1250"
+                    android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                    android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..8ca4520
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable">
+    <target android:name="_R_G_L_1_G_D_0_P_0"
+            android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/>
+    <target android:name="_R_G_L_0_G_D_0_P_0"
+            android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/>
+    <target android:name="time_group"
+            android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..66ac8fe
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+        android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_2_G" android:translateX="3.75"
+               android:translateY="1.75">
+            <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M1.22 3.22 C0.93,3.51 0.93,3.99 1.22,4.28 C1.22,4.28 7.19,10.25 7.19,10.25 C7.19,10.25 1.22,16.22 1.22,16.22 C0.93,16.51 0.93,16.99 1.22,17.28 C1.37,17.43 1.56,17.5 1.75,17.5 C1.94,17.5 2.13,17.43 2.28,17.28 C2.28,17.28 7.25,12.31 7.25,12.31 C7.25,12.31 7.25,20.25 7.25,20.25 C7.25,20.25 8,20.25 8,20.25 C10.96,20.25 13.37,17.84 13.37,14.88 C13.37,12.91 12.31,11.19 10.73,10.25 C12.31,9.31 13.37,7.59 13.37,5.63 C13.37,2.66 10.96,0.25 8,0.25 C8,0.25 7.25,0.25 7.25,0.25 C7.25,0.25 7.25,8.19 7.25,8.19 C7.25,8.19 2.28,3.22 2.28,3.22 C1.99,2.93 1.51,2.93 1.22,3.22c  M8.75 1.82 C10.52,2.17 11.87,3.75 11.87,5.63 C11.87,7.5 10.52,9.08 8.75,9.43 C8.75,9.43 8.75,1.82 8.75,1.82c  M8.75 11.07 C10.52,11.42 11.87,13 11.87,14.88 C11.87,16.75 10.52,18.33 8.75,18.68 C8.75,18.68 8.75,11.07 8.75,11.07c "/>
+        </group>
+        <group android:name="_R_G_L_1_G" android:translateX="3.75"
+               android:translateY="1.75">
+            <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M15.25 9.25 C14.7,9.25 14.25,9.7 14.25,10.25 C14.25,10.8 14.7,11.25 15.25,11.25 C15.8,11.25 16.25,10.8 16.25,10.25 C16.25,9.7 15.8,9.25 15.25,9.25c "/>
+        </group>
+        <group android:name="_R_G_L_0_G" android:translateX="3.75"
+               android:translateY="1.75">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M0.25 10.25 C0.25,10.8 0.7,11.25 1.25,11.25 C1.8,11.25 2.25,10.8 2.25,10.25 C2.25,9.7 1.8,9.25 1.25,9.25 C0.7,9.25 0.25,9.7 0.25,10.25c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..1317f66
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable">
+    <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/>
+    <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/>
+    <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/>
+    <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..8153195
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="24dp"
+        android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_0_G" android:translateX="2"
+               android:translateY="1.552999999999999">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M10 12.45 C9.17,12.45 8.5,11.77 8.5,10.95 C8.5,10.12 9.17,9.45 10,9.45 C10.83,9.45 11.5,10.12 11.5,10.95 C11.5,11.77 10.83,12.45 10,12.45c "/>
+            <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M6.47 15.23 C6.27,15.23 6.08,15.16 5.94,15.01 C4.85,13.93 4.25,12.48 4.25,10.95 C4.25,9.41 4.85,7.97 5.94,6.88 C8.18,4.64 11.82,4.64 14.07,6.88 C15.15,7.97 15.75,9.41 15.75,10.95 C15.75,12.48 15.15,13.93 14.07,15.01 C13.77,15.3 13.3,15.3 13.01,15.01 C12.71,14.72 12.71,14.24 13.01,13.95 C13.81,13.15 14.25,12.08 14.25,10.95 C14.25,9.81 13.81,8.74 13.01,7.94 C11.35,6.28 8.65,6.28 6.99,7.94 C6.19,8.75 5.75,9.81 5.75,10.95 C5.75,12.08 6.19,13.15 6.99,13.95 C7.29,14.25 7.29,14.72 6.99,15.01 C6.85,15.16 6.66,15.23 6.47,15.23c "/>
+            <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M16.36 18.06 C16.17,18.06 15.98,17.99 15.83,17.84 C15.54,17.55 15.54,17.07 15.83,16.78 C17.39,15.22 18.25,13.15 18.25,10.95 C18.25,8.74 17.39,6.67 15.83,5.11 C12.62,1.9 7.38,1.9 4.17,5.11 C2.61,6.67 1.75,8.74 1.75,10.95 C1.75,13.15 2.61,15.22 4.17,16.78 C4.46,17.07 4.46,17.55 4.17,17.84 C3.87,18.13 3.4,18.13 3.11,17.84 C1.26,16 0.25,13.55 0.25,10.95 C0.25,8.34 1.26,5.89 3.11,4.05 C6.91,0.25 13.09,0.25 16.89,4.05 C18.74,5.89 19.75,8.34 19.75,10.95 C19.75,13.55 18.74,16 16.89,17.84 C16.75,17.99 16.56,18.06 16.36,18.06c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..06755f6
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable">
+    <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/>
+    <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+    <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+    <target android:name="_R_G_L_0_G_D_3_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+    <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..6e45513
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_0_G" android:translateX="0.6440000000000001"
+               android:translateY="2.755000000000001">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M12.86 15.75 C12.86,16.58 12.19,17.25 11.36,17.25 C10.53,17.25 9.86,16.58 9.86,15.75 C9.86,14.92 10.53,14.25 11.36,14.25 C12.19,14.25 12.86,14.92 12.86,15.75c "/>
+            <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M15.61 12.25 C15.42,12.25 15.23,12.17 15.08,12.03 C14.09,11.04 12.76,10.5 11.36,10.5 C9.95,10.5 8.63,11.04 7.63,12.03 C7.34,12.32 6.86,12.32 6.57,12.02 C6.28,11.73 6.28,11.26 6.58,10.96 C7.86,9.7 9.55,9 11.36,9 C13.16,9 14.86,9.7 16.14,10.96 C16.43,11.25 16.43,11.73 16.14,12.02 C16,12.17 15.8,12.25 15.61,12.25c "/>
+            <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M18.77 9.08 C18.58,9.08 18.39,9.01 18.24,8.86 C16.4,7.02 13.96,6 11.36,6 C8.75,6 6.31,7.02 4.47,8.86 C4.18,9.16 3.7,9.16 3.41,8.86 C3.12,8.57 3.11,8.1 3.41,7.8 C5.53,5.67 8.35,4.5 11.36,4.5 C14.36,4.5 17.18,5.67 19.31,7.8 C19.6,8.1 19.6,8.57 19.3,8.86 C19.16,9.01 18.97,9.08 18.77,9.08c "/>
+            <path android:name="_R_G_L_0_G_D_3_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M21.96 5.89 C21.77,5.89 21.58,5.82 21.43,5.67 C18.74,2.98 15.16,1.5 11.36,1.5 C7.55,1.5 3.97,2.98 1.28,5.67 C0.99,5.97 0.51,5.97 0.22,5.67 C-0.07,5.38 -0.07,4.91 0.22,4.61 C3.19,1.64 7.15,0 11.36,0 C15.56,0 19.52,1.64 22.49,4.61 C22.78,4.91 22.78,5.38 22.49,5.67 C22.35,5.82 22.15,5.89 21.96,5.89c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml
new file mode 100644
index 0000000..ae5cc2b
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="config_batterymeterPerimeterPath" translatable="false">
+        M 8,1 C 8,0.45 7.55,0 7,0 H 5 C 4.45,0 4,0.45 4,1 H 3 C 1.34,1 0,2.34 0,4 V 17 C 0,18.66 1.34,20 3,20 H 9 C 10.66,20 12,18.66 12,17 V 4 C 12,2.34 10.66,1 9,1 Z M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z
+    </string>
+    <string name="config_batterymeterFillMask" translatable="false">
+        M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z
+    </string>
+    <string name="config_batterymeterBoltPath" translatable="false">
+        M 8.08,9.5 H 7 V 5.99 C 7,5.73 6.65,5.64 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 H 5 V 15.01 C 5,15.27 5.35,15.36 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 Z
+    </string>
+    <string name="config_batterymeterPowersavePath" translatable="false">
+        M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.6601,9.75 8.25,9.75 H 6.75 V 8.25 C 6.75,7.84 6.41,7.5 6,7.5 5.59,7.5 5.25,7.84 5.25,8.25 V 9.75 H 3.75 C 3.34,9.75 3,10.09 3,10.5 3,10.91 3.34,11.25 3.75,11.25 Z
+    </string>
+</resources>
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk
new file mode 100644
index 0000000..412c26f
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+#  Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackCircularThemePicker
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackCircularThemePickerOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..eae7de8
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.theme.icon_pack.circular.themepicker"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/>
+    <application android:label="Circular" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml
new file mode 100644
index 0000000..900aaa0
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,11.25h-7.5v-7.5C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75v7.5h-7.5C3.34,11.25,3,11.59,3,12 s0.34,0.75,0.75,0.75h7.5v7.5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-7.5h7.5c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml
new file mode 100644
index 0000000..ddfb980
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.03,3.97c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L5.03,3.97c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-6.97,6.97c-0.29,0.29-0.29,0.77,0,1.06c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22L12,13.06l6.97,6.97 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l6.97-6.97 C20.32,4.74,20.32,4.26,20.03,3.97z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml
new file mode 100644
index 0000000..f572af6
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M17.59,3.59l-2.97,2.97l-2.09-2.09c-0.29-0.29-0.77-0.29-1.06,0c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68 C3.11,16.89,3,17.15,3,17.41V21h3.59c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22 c0.19,0,0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83 C19.63,2.8,18.37,2.8,17.59,3.59z M6.38,19.5H4.5v-1.88l9.5-9.5L15.88,10L6.38,19.5z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml
new file mode 100644
index 0000000..a87186b
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9,20h6c1.66,0,3-1.34,3-3V6h0.5c0.41,0,0.75-0.34,0.75-0.75S18.91,4.5,18.5,4.5H18h-3l-1-1h-4l-1,1H6H5.5 c-0.41,0-0.75,0.34-0.75,0.75S5.09,6,5.5,6H6v11C6,18.66,7.34,20,9,20z M16.5,6v11c0,0.83-0.67,1.5-1.5,1.5H9 c-0.83,0-1.5-0.67-1.5-1.5V6H16.5z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M13.97,16c0.41,0,0.75-0.34,0.75-0.75v-6.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.5 C13.22,15.66,13.55,16,13.97,16z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10,16c0.41,0,0.75-0.34,0.75-0.75v-6.5C10.75,8.34,10.41,8,10,8S9.25,8.34,9.25,8.75v6.5C9.25,15.66,9.59,16,10,16z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml
new file mode 100644
index 0000000..edaf3c7
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,2H6C3.8,2,2,3.8,2,6v12c0,2.2,1.8,4,4,4h12c2.2,0,4-1.8,4-4V6C22,3.8,20.2,2,18,2z M20.5,18c0,1.38-1.12,2.5-2.5,2.5H6 c-1.38,0-2.5-1.12-2.5-2.5V6c0-1.38,1.12-2.5,2.5-2.5h12c1.38,0,2.5,1.12,2.5,2.5V18z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml
new file mode 100644
index 0000000..2884d71
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,12c0-5.52-4.48-10-10-10C6.48,2,2,6.48,2,12s4.48,10,10,10C17.52,22,22,17.52,22,12z M12,20.5 c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5c4.69,0,8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12.75,11.69V5.78c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.53l3.78,3.78c0.15,0.15,0.34,0.22,0.53,0.22 s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml
new file mode 100644
index 0000000..d50dbd4
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M2.75,16.75C2.34,16.75,2,17.09,2,17.5s0.34,0.75,0.75,0.75h3v3C5.75,21.66,6.09,22,6.5,22s0.75-0.34,0.75-0.75v-3h4v3 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h4v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h3 c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3 C21.66,7.25,22,6.91,22,6.5s-0.34-0.75-0.75-0.75h-3v-3C18.25,2.34,17.91,2,17.5,2s-0.75,0.34-0.75,0.75v3h-4v-3 C12.75,2.34,12.41,2,12,2s-0.75,0.34-0.75,0.75v3h-4v-3C7.25,2.34,6.91,2,6.5,2S5.75,2.34,5.75,2.75v3h-3C2.34,5.75,2,6.09,2,6.5 s0.34,0.75,0.75,0.75h3v4h-3C2.34,11.25,2,11.59,2,12s0.34,0.75,0.75,0.75h3v4H2.75z M16.75,16.75h-4v-4h4V16.75z M16.75,7.25v4h-4 v-4H16.75z M7.25,7.25h4v4h-4V7.25z M7.25,12.75h4v4h-4V12.75z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml
new file mode 100644
index 0000000..7375bc9
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M8,14h1v5c0,1.66,1.34,3,3,3s3-1.34,3-3v-5h1c1.66,0,3-1.34,3-3V2H5v9C5,12.66,6.34,14,8,14z M16,12.5h-2.5V14v5 c0,0.83-0.67,1.5-1.5,1.5s-1.5-0.67-1.5-1.5v-5v-1.5H8c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11C17.5,11.83,16.83,12.5,16,12.5z M9,3.5 v3c0,0.41,0.34,0.75,0.75,0.75S10.5,6.91,10.5,6.5v-3h2.75v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h2.75V9h-11V3.5H9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
new file mode 100644
index 0000000..bdb7442
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11,3.76c0-0.41-0.34-0.75-0.75-0.75H6.84C4.72,3.01,3,4.74,3,6.85v3.4C3,10.66,3.34,11,3.75,11s0.75-0.34,0.75-0.75v-3.4 c0-1.29,1.05-2.33,2.34-2.33h3.41C10.66,4.51,11,4.18,11,3.76z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10.25,19.5H6.84c-1.29,0-2.34-1.05-2.34-2.34v-3.41C4.5,13.34,4.16,13,3.75,13S3,13.34,3,13.75v3.41 C3,19.28,4.72,21,6.84,21h3.41c0.41,0,0.75-0.34,0.75-0.75S10.66,19.5,10.25,19.5z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,13c-0.41,0-0.75,0.34-0.75,0.75v3.41c0,1.29-1.05,2.34-2.34,2.34h-3.41c-0.41,0-0.75,0.34-0.75,0.75 S13.34,21,13.75,21h3.41c2.12,0,3.84-1.72,3.84-3.84v-3.41C21,13.34,20.66,13,20.25,13z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M17.16,3h-3.41C13.34,3,13,3.34,13,3.75s0.34,0.75,0.75,0.75h3.41c1.29,0,2.34,1.05,2.34,2.34v3.41 c0,0.41,0.34,0.75,0.75,0.75S21,10.66,21,10.25V6.84C21,4.72,19.28,3,17.16,3z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12.89,15.29l-1.41-1.82c-0.8-1.04-2.37-1.03-3.17,0.01l-2.7,3.51l12.83-0.01l-1.88-2.35c-0.79-0.99-2.29-1-3.1-0.03 L12.89,15.29z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml
new file mode 100644
index 0000000..b7e6bf9
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,9h-1.5v1.5H19c0.83,0,1.5,0.67,1.5,1.5v7c0,0.83-0.67,1.5-1.5,1.5h-8c-0.83,0-1.5-0.67-1.5-1.5v-1.5H8V19 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3v-7C22,10.34,20.66,9,19,9z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9,16c3.87,0,7-3.13,7-7c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9C2,12.87,5.13,16,9,16z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml
new file mode 100644
index 0000000..9c88211
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="20dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="20dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,4.75h-2.4C17.55,4.02,16.84,3.5,16,3.5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h2.4 C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11.98,4.75H3.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75h8.23c0.01,0,0.01,0,0.02,0V4.75 C11.99,4.75,11.99,4.75,11.98,4.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,17.75h-5.4c-0.3-0.73-1.01-1.25-1.85-1.25c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h5.4 c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M8.98,17.75H3.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75h5.23c0.01,0,0.01,0,0.02,0v-1.49 C8.99,17.75,8.99,17.75,8.98,17.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M7,10c-0.84,0-1.55,0.52-1.85,1.25h-1.4C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75h1.4C5.45,13.48,6.16,14,7,14 c1.1,0,2-0.9,2-2C9,10.9,8.1,10,7,10z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,11.25h-9.23c-0.01,0-0.01,0-0.02,0v1.49c0.01,0,0.01,0,0.02,0h9.23c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml
new file mode 100644
index 0000000..fde9965
--- /dev/null
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.42,11.84c-0.19,0-0.38-0.07-0.53-0.22C17.05,9.77,14.6,8.75,12,8.75s-5.05,1.02-6.89,2.86 c-0.29,0.29-0.77,0.29-1.06,0c-0.29-0.29-0.29-0.77,0-1.06C6.17,8.43,9,7.25,12,7.25s5.83,1.17,7.95,3.3 c0.29,0.29,0.29,0.77,0,1.06C19.8,11.76,19.61,11.84,19.42,11.84z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22.61,8.65c-0.19,0-0.38-0.07-0.53-0.22C19.38,5.74,15.81,4.25,12,4.25S4.62,5.74,1.92,8.43c-0.29,0.29-0.77,0.29-1.06,0 s-0.29-0.77,0-1.06C3.84,4.39,7.79,2.75,12,2.75s8.16,1.64,11.14,4.61c0.29,0.29,0.29,0.77,0,1.06 C22.99,8.57,22.8,8.65,22.61,8.65z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M16.25,15c-0.19,0-0.38-0.07-0.53-0.22c-1-0.99-2.32-1.53-3.73-1.53s-2.73,0.54-3.73,1.53c-0.29,0.29-0.77,0.29-1.06-0.01 s-0.29-0.77,0.01-1.06c1.28-1.27,2.98-1.96,4.78-1.96s3.5,0.7,4.78,1.96c0.29,0.29,0.3,0.77,0.01,1.06 C16.64,14.93,16.45,15,16.25,15z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..e884bd3
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable">
+    <target android:name="_R_G_L_1_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_0" />
+    <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_1" />
+    <target android:name="time_group" android:animation="@*android:anim/ic_bluetooth_transient_animation_2" />
+</animated-vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..79107d8f
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+        android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_2_G" android:translateX="3.099"
+               android:translateY="1.6400000000000006">
+            <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M10.41 10.36 C10.41,10.36 14.16,6.62 14.16,6.62 C14.57,6.21 14.57,5.55 14.16,5.14 C14.16,5.14 9.69,0.67 9.69,0.67 C9.69,0.67 9.66,0.64 9.66,0.64 C9.24,0.25 8.58,0.27 8.18,0.69 C8,0.88 7.9,1.14 7.9,1.4 C7.9,1.4 7.9,7.84 7.9,7.84 C7.9,7.84 3.85,3.79 3.85,3.79 C3.44,3.38 2.79,3.38 2.38,3.79 C1.97,4.2 1.97,4.85 2.38,5.26 C2.38,5.26 7.47,10.36 7.47,10.36 C7.47,10.36 2.38,15.45 2.38,15.45 C1.97,15.86 1.97,16.51 2.38,16.92 C2.79,17.33 3.44,17.33 3.85,16.92 C3.85,16.92 7.9,12.87 7.9,12.87 C7.9,12.87 7.9,19.32 7.9,19.32 C7.9,19.89 8.37,20.36 8.94,20.36 C9.2,20.36 9.46,20.26 9.65,20.08 C9.65,20.08 9.7,20.03 9.7,20.03 C9.7,20.03 14.16,15.57 14.16,15.57 C14.57,15.16 14.57,14.5 14.16,14.09 C14.16,14.09 10.41,10.36 10.41,10.36c  M9.89 3.73 C9.89,3.73 12.04,5.88 12.04,5.88 C12.04,5.88 9.89,8.03 9.89,8.03 C9.89,8.03 9.89,3.73 9.89,3.73c  M9.89 16.98 C9.89,16.98 9.89,12.68 9.89,12.68 C9.89,12.68 12.04,14.83 12.04,14.83 C12.04,14.83 9.89,16.98 9.89,16.98c "/>
+        </group>
+        <group android:name="_R_G_L_1_G" android:translateX="3.099"
+               android:translateY="1.6400000000000006">
+            <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M16.96 9.3 C16.95,9.3 16.95,9.29 16.95,9.28 C16.36,8.71 15.42,8.72 14.84,9.3 C14.84,9.3 14.83,9.31 14.83,9.31 C14.25,9.9 14.25,10.84 14.84,11.42 C15.42,12.01 16.37,12.01 16.96,11.42 C17.55,10.84 17.55,9.89 16.96,9.3c "/>
+        </group>
+        <group android:name="_R_G_L_0_G" android:translateX="3.099"
+               android:translateY="1.6400000000000006">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M2.96 9.3 C2.96,9.3 2.95,9.29 2.95,9.29 C2.36,8.71 1.42,8.71 0.84,9.3 C0.84,9.3 0.83,9.31 0.83,9.31 C0.25,9.9 0.25,10.84 0.84,11.42 C1.42,12.01 2.37,12.01 2.96,11.42 C3.55,10.83 3.55,9.89 2.96,9.3c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..54738c0
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable">
+    <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/>
+    <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/>
+    <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/>
+    <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..737b522
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+        android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_0_G" android:translateX="1.545"
+               android:translateY="2.1449999999999996">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M9.31 0.92 C4.67,1.43 0.92,5.26 0.5,9.92 C0.25,12.64 1.1,15.17 2.65,17.1 C3.02,17.56 3.71,17.6 4.13,17.18 C4.49,16.82 4.52,16.25 4.21,15.86 C2.81,14.11 2.12,11.76 2.61,9.25 C3.22,6.12 5.75,3.6 8.89,3 C14,2.04 18.45,5.92 18.45,10.86 C18.45,12.75 17.79,14.48 16.69,15.86 C16.37,16.25 16.41,16.81 16.77,17.17 C16.77,17.17 16.77,17.17 16.77,17.17 C17.19,17.59 17.89,17.56 18.26,17.09 C19.64,15.39 20.45,13.22 20.45,10.86 C20.45,4.96 15.34,0.25 9.31,0.92c "/>
+            <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M13.91 14.31 C13.91,14.31 13.92,14.32 13.92,14.32 C14.35,14.75 15.08,14.71 15.42,14.21 C16.07,13.25 16.45,12.1 16.45,10.85 C16.45,7.11 13.01,4.16 9.12,5 C6.88,5.49 5.08,7.3 4.6,9.54 C4.22,11.28 4.61,12.92 5.48,14.2 C5.83,14.71 6.56,14.75 6.99,14.32 C6.99,14.32 7,14.31 7,14.31 C7.34,13.97 7.37,13.43 7.1,13.03 C6.6,12.26 6.36,11.32 6.49,10.29 C6.73,8.55 8.16,7.13 9.9,6.89 C12.36,6.56 14.46,8.46 14.46,10.85 C14.46,11.66 14.22,12.4 13.81,13.02 C13.54,13.43 13.57,13.97 13.91,14.31c "/>
+            <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M12.46 10.86 C12.46,11.96 11.56,12.86 10.46,12.86 C9.35,12.86 8.46,11.96 8.46,10.86 C8.46,9.75 9.35,8.86 10.46,8.86 C11.56,8.86 12.46,9.75 12.46,10.86c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..49d235b
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable">
+    <target android:name="_R_G_L_7_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/>
+    <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+    <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+    <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+    <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+    <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+    <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+    <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+    <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+    <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/>
+    <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/>
+    <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/>
+    <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/>
+    <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/>
+    <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..9fcb479
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="24dp" android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_7_G" android:translateX="0.10000000000000142"
+               android:translateY="3">
+            <path android:name="_R_G_L_7_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+        </group>
+        <group android:name="_R_G_L_6_G" android:translateX="0.10000000000000142"
+               android:translateY="3">
+            <path android:name="_R_G_L_6_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+        </group>
+        <group android:name="_R_G_L_5_G" android:translateX="5.81"
+               android:translateY="12.747">
+            <path android:name="_R_G_L_5_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M7.01 8.85 C7.01,8.85 12.12,2.5 12.12,2.5 C10.48,1.05 8.37,0.25 6.19,0.25 C3.91,0.25 1.84,1.1 0.25,2.5 C0.25,2.5 5.35,8.85 5.35,8.85 C5.78,9.38 6.58,9.38 7.01,8.85c "/>
+        </group>
+        <group android:name="_R_G_L_4_G" android:translateX="0.10000000000000142"
+               android:translateY="3">
+            <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+        </group>
+        <group android:name="_R_G_L_3_G" android:translateX="3.9290000000000003"
+               android:translateY="9.75">
+            <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M8.89 11.85 C8.89,11.85 15.88,3.15 15.88,3.15 C13.78,1.35 11.06,0.25 8.07,0.25 C5.08,0.25 2.35,1.35 0.25,3.16 C0.25,3.16 7.23,11.86 7.23,11.86 C7.66,12.38 8.46,12.38 8.89,11.85c "/>
+        </group>
+        <group android:name="_R_G_L_2_G" android:translateX="0.10000000000000142"
+               android:translateY="3">
+            <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="0.3" android:fillType="nonZero"
+                  android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+        </group>
+        <group android:name="_R_G_L_1_G" android:translateX="2.6799999999999997"
+               android:translateY="7.748">
+            <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M10.14 13.85 C10.14,13.85 18.39,3.59 18.39,3.59 C15.86,1.43 12.65,0.25 9.32,0.25 C5.86,0.25 2.69,1.51 0.25,3.6 C0.25,3.6 8.48,13.86 8.48,13.86 C8.91,14.38 9.71,14.38 10.14,13.85c "/>
+        </group>
+        <group android:name="_R_G_L_0_G" android:translateX="-0.3000000000000007"
+               android:translateY="2.75">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M12.3 0.25 C6.74,0.25 2.63,2.61 0.86,3.82 C0.35,4.17 0.25,4.88 0.63,5.36 C0.63,5.36 11.46,18.85 11.46,18.85 C11.88,19.38 12.69,19.38 13.12,18.85 C13.12,18.85 23.96,5.36 23.96,5.36 C24.35,4.88 24.25,4.17 23.74,3.82 C21.97,2.61 17.85,0.25 12.3,0.25c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml
new file mode 100644
index 0000000..6b59b62
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="config_batterymeterPerimeterPath" translatable="false">
+        M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z
+    </string>
+    <string name="config_batterymeterErrorPerimeterPath" translatable="false">
+        M 3.5,0 V 0.5 1.5 H 2.3301 C 1.3261,1.5 0.5,2.3261 0.5,3.3301 V 18.16 C 0.5,19.17 1.3261,20 2.3301,20 H 9.6602 C 10.67,20 11.5,19.174 11.5,18.17 V 3.3301 C 11.5,2.3261 10.674,1.5 9.6699,1.5 H 8.5 V 0 Z M 9.1698,2.9999 C 9.6259,2.9999 9.9999,3.374 9.9999,3.83 V 17.67 C 9.9999,18.126 9.6299,18.5 9.1601,18.5 H 2.83 C 2.3741,18.5 2,18.13 2,17.66 V 3.83 C 2,3.374 2.3741,2.9999 2.83,2.9999 Z
+    </string>
+    <string name="config_batterymeterFillMask" translatable="false">
+        M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z
+    </string>
+    <string name="config_batterymeterBoltPath" translatable="false">
+        M 8.58,10 C 8.77,10 8.89,10.2 8.8,10.37 L 5.94,15.74 C 5.7,16.19 5,16.02 5,15.5 V 12 H 3.42 C 3.23,12 3.11,11.8 3.2,11.63 L 6.06,6.26 C 6.3,5.81 7,5.98 7,6.5 V 10 Z
+    </string>
+    <string name="config_batterymeterPowersavePath" translatable="false">
+        M 9,11 C 9,11.55 8.55,12 8,12 H 7 V 13 C 7,13.55 6.55,14 6,14 5.45,14 5,13.55 5,13 V 12 H 4 C 3.45,12 3,11.55 3,11 3,10.45 3.45,10.005 4,10 H 5 V 9 C 5,8.45 5.45,8 6,8 6.55,8 7,8.45 7,9 V 10 H 8 C 8.55,10 9,10.45 9,11 Z
+    </string>
+    <bool name="config_batterymeterDualTone">true</bool>
+</resources>
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk
new file mode 100644
index 0000000..6d15603
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+#  Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackFilledThemePicker
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackFilledThemePickerOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..35023ab
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.theme.icon_pack.filled.themepicker"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/>
+    <application android:label="Filled" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml
new file mode 100644
index 0000000..1768723
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M5,13h6v6c0,0.55,0.45,1,1,1s1-0.45,1-1v-6h6c0.55,0,1-0.45,1-1s-0.45-1-1-1h-6V5c0-0.55-0.45-1-1-1s-1,0.45-1,1v6H5 c-0.55,0-1,0.45-1,1S4.45,13,5,13z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml
new file mode 100644
index 0000000..4bfff2c
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M5.7,18.3c0.39,0.39,1.02,0.39,1.41,0L12,13.41l4.89,4.89c0.39,0.39,1.02,0.39,1.41,0s0.39-1.02,0-1.41L13.41,12l4.89-4.89 c0.38-0.38,0.38-1.02,0-1.4c-0.39-0.39-1.02-0.39-1.41,0c0,0,0,0,0,0L12,10.59L7.11,5.7c-0.39-0.39-1.02-0.39-1.41,0 s-0.39,1.02,0,1.41L10.59,12L5.7,16.89C5.31,17.28,5.31,17.91,5.7,18.3z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml
new file mode 100644
index 0000000..aa3a925
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11.21,5.2L11.21,5.2c-0.39,0.39-0.39,1.02,0,1.41l0.71,0.71l-8.63,8.63C3.11,16.14,3,16.4,3,16.66V20c0,0.55,0.45,1,1,1 h3.34c0.27,0,0.52-0.11,0.71-0.29l8.63-8.63l0.72,0.72c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41l-1.21-1.22L20,7.75 c0.78-0.78,0.78-2.05,0-2.83L19.08,4c-0.78-0.78-2.05-0.78-2.83,0l-2.41,2.41L12.61,5.2C12.23,4.81,11.6,4.81,11.21,5.2z M14.98,10.94L6.92,19H5v-1.92l8.06-8.06L14.98,10.94z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml
new file mode 100644
index 0000000..94c6311
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,4h-2.5l-0.71-0.71C14.61,3.11,14.35,3,14.09,3H9.9C9.64,3,9.38,3.11,9.2,3.29L8.49,4h-2.5c-0.55,0-1,0.45-1,1 s0.45,1,1,1h12c0.55,0,1-0.45,1-1C19,4.45,18.55,4,18,4z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml
new file mode 100644
index 0000000..7603823
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 12.07 8.6 L 11.94 8.6 L 10.43 13.06 L 13.56 13.06 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,2H5C3.35,2,2,3.35,2,5v14c0,1.65,1.35,3,3,3h14c1.65,0,3-1.35,3-3V5C22,3.35,20.65,2,19,2z M15.3,18l-1.01-2.87H9.7 L8.7,18H6.2l4.49-12h2.6l4.51,12H15.3z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml
new file mode 100644
index 0000000..11260159
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11.99,22C17.52,22,22,17.52,22,12c0-5.52-4.48-10-10.01-10C6.47,2,2,6.48,2,12C2,17.52,6.47,22,11.99,22z M11,6.82 c0-0.4,0.25-0.72,0.75-0.72s0.75,0.32,0.75,0.72v5.43l3.87,2.3c0.01,0,0.01,0.01,0.02,0.01c0.33,0.21,0.44,0.64,0.23,0.98 c-0.2,0.34-0.64,0.44-0.98,0.24L11,13V6.82z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml
new file mode 100644
index 0000000..0397b6c
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,7c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3h-4V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3H7V2 c0-0.55-0.45-1-1-1S5,1.45,5,2v3H2C1.45,5,1,5.45,1,6s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1 s0.45,1,1,1h3v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h3 c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3v-4h3c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V7H22z M11,17H7v-4h4V17z M11,11H7V7h4V11z M17,17h-4v-4 h4V17z M17,11h-4V7h4V11z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml
new file mode 100644
index 0000000..6f0462c
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,2H6C5.45,2,5,2.45,5,3v9c0,1.65,1.35,3,3,3h2v6c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-6h2c1.65,0,3-1.35,3-3V3 C19,2.45,18.55,2,18,2z M17,9H7V4h2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V4h2v2c0,0.55,0.45,1,1,1c0.55,0,1-0.45,1-1V4h2V9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
new file mode 100644
index 0000000..ea195ca
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 15.5 7 C 16.3284271247 7 17 7.67157287525 17 8.5 C 17 9.32842712475 16.3284271247 10 15.5 10 C 14.6715728753 10 14 9.32842712475 14 8.5 C 14 7.67157287525 14.6715728753 7 15.5 7 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M3.5,11h1C4.78,11,5,10.78,5,10.5V5h5.5C10.78,5,11,4.78,11,4.5v-1C11,3.22,10.78,3,10.5,3H5C3.9,3,3,3.9,3,5v5.5 C3,10.78,3.22,11,3.5,11z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,3h-5.5C13.22,3,13,3.22,13,3.5v1C13,4.78,13.22,5,13.5,5H19v5.5c0,0.28,0.22,0.5,0.5,0.5h1c0.28,0,0.5-0.22,0.5-0.5V5 C21,3.9,20.1,3,19,3z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.5,13h-1c-0.28,0-0.5,0.22-0.5,0.5V19h-5.5c-0.28,0-0.5,0.22-0.5,0.5v1c0,0.28,0.22,0.5,0.5,0.5H19c1.1,0,2-0.9,2-2 v-5.5C21,13.22,20.78,13,20.5,13z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10.5,19H5v-5.5C5,13.22,4.78,13,4.5,13h-1C3.22,13,3,13.22,3,13.5V19c0,1.1,0.9,2,2,2h5.5c0.28,0,0.5-0.22,0.5-0.5v-1 C11,19.22,10.78,19,10.5,19z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10.78,12.98c-0.4-0.5-1.16-0.5-1.56,0l-2.57,3.21C6.39,16.51,6.62,17,7.04,17H17c0.41,0,0.65-0.47,0.4-0.8l-1.6-2.13 c-0.4-0.53-1.2-0.53-1.6,0l-1.23,1.64L10.78,12.98z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml
new file mode 100644
index 0000000..cea09b56
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,9h-3v2h3v9H10v-2H8v2c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M15,9c0-1.07-0.25-2.09-0.68-3C13.2,3.64,10.79,2,8,2C4.13,2,1,5.13,1,9c0,2.79,1.64,5.2,4,6.32C5.91,15.75,6.93,16,8,16 C11.87,16,15,12.87,15,9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml
new file mode 100644
index 0000000..ae03b51
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="20dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="20dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M8,9c-0.55,0-1,0.45-1,1v1H4c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h3v1c0,0.55,0.45,1,1,1s1-0.45,1-1v-4 C9,9.45,8.55,9,8,9z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,5h-3V4c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1V7h3c0.55,0,1-0.45,1-1C21,5.45,20.55,5,20,5z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,11h-9v2h9c0.55,0,1-0.45,1-1C21,11.45,20.55,11,20,11z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,17h-7v-1c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1v-1h7c0.55,0,1-0.45,1-1S20.55,17,20,17z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M4,7h9V5H4C3.45,5,3,5.45,3,6C3,6.55,3.45,7,4,7z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M3,18c0,0.55,0.45,1,1,1h5v-2H4C3.45,17,3,17.45,3,18z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml
new file mode 100644
index 0000000..03e142e
--- /dev/null
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11.29,19.29c0.39,0.39,1.03,0.4,1.42,0L14,18c0.47-0.47,0.38-1.28-0.22-1.58C13.25,16.15,12.64,16,12,16 c-0.64,0-1.24,0.15-1.77,0.41c-0.59,0.29-0.69,1.11-0.22,1.58L11.29,19.29z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M17.6,14.39l0.71-0.71c0.42-0.42,0.39-1.12-0.08-1.5C16.52,10.82,14.35,10,12,10c-2.34,0-4.5,0.81-6.21,2.17 c-0.47,0.37-0.51,1.07-0.09,1.49l0.71,0.71c0.35,0.36,0.92,0.39,1.32,0.08C8.91,13.54,10.39,13,12,13c1.61,0,3.1,0.55,4.29,1.47 C16.69,14.78,17.25,14.75,17.6,14.39z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M21.83,10.16l0.71-0.71c0.42-0.42,0.38-1.09-0.06-1.48C19.68,5.5,16.01,4,12,4C8.01,4,4.36,5.49,1.56,7.94 C1.12,8.33,1.08,9,1.49,9.41l0.71,0.71c0.37,0.37,0.96,0.4,1.35,0.06C5.81,8.2,8.77,7,12,7c3.25,0,6.22,1.22,8.49,3.22 C20.88,10.56,21.47,10.53,21.83,10.16z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..09e7360
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable">
+    <target android:name="_R_G_L_1_G_D_0_P_0"
+            android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/>
+    <target android:name="_R_G_L_0_G_D_0_P_0"
+            android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/>
+    <target android:name="time_group"
+            android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..e0f155a
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+        android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_2_G" android:translateX="3.75"
+               android:translateY="1.4410000000000007">
+            <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M13.78 5.28 C13.78,5.28 9.03,0.53 9.03,0.53 C8.82,0.31 8.49,0.25 8.21,0.37 C7.93,0.48 7.75,0.75 7.75,1.06 C7.75,1.06 7.75,8.75 7.75,8.75 C7.75,8.75 3.78,4.78 3.78,4.78 C3.49,4.49 3.01,4.49 2.72,4.78 C2.43,5.07 2.43,5.55 2.72,5.84 C2.72,5.84 7.44,10.56 7.44,10.56 C7.44,10.56 2.72,15.28 2.72,15.28 C2.43,15.57 2.43,16.05 2.72,16.34 C3.01,16.63 3.49,16.63 3.78,16.34 C3.78,16.34 7.75,12.37 7.75,12.37 C7.75,12.37 7.75,20.06 7.75,20.06 C7.75,20.36 7.93,20.64 8.21,20.75 C8.31,20.79 8.4,20.81 8.5,20.81 C8.7,20.81 8.89,20.73 9.03,20.59 C9.03,20.59 13.78,15.84 13.78,15.84 C14.07,15.55 14.07,15.07 13.78,14.78 C13.78,14.78 9.56,10.56 9.56,10.56 C9.56,10.56 13.78,6.34 13.78,6.34 C14.07,6.05 14.07,5.57 13.78,5.28c  M12.19 15.31 C12.19,15.31 9.25,18.25 9.25,18.25 C9.25,18.25 9.25,12.37 9.25,12.37 C9.25,12.37 12.19,15.31 12.19,15.31c  M9.25 8.75 C9.25,8.75 9.25,2.87 9.25,2.87 C9.25,2.87 12.19,5.81 12.19,5.81 C12.19,5.81 9.25,8.75 9.25,8.75c "/>
+        </group>
+        <group android:name="_R_G_L_1_G" android:translateX="3.75"
+               android:translateY="1.4410000000000007">
+            <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M15.25 9.56 C14.7,9.56 14.25,10.01 14.25,10.56 C14.25,11.11 14.7,11.56 15.25,11.56 C15.8,11.56 16.25,11.11 16.25,10.56 C16.25,10.01 15.8,9.56 15.25,9.56c "/>
+        </group>
+        <group android:name="_R_G_L_0_G" android:translateX="3.75"
+               android:translateY="1.4410000000000007">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M1.25 9.56 C0.7,9.56 0.25,10.01 0.25,10.56 C0.25,11.11 0.7,11.56 1.25,11.56 C1.8,11.56 2.25,11.11 2.25,10.56 C2.25,10.01 1.8,9.56 1.25,9.56c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
+    
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..1317f66
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable">
+    <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/>
+    <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/>
+    <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/>
+    <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..6b87f63
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+        android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_0_G" android:translateX="2"
+               android:translateY="1.552999999999999">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M8.5 10.95 C8.5,11.77 9.17,12.45 10,12.45 C10.83,12.45 11.5,11.77 11.5,10.95 C11.5,10.12 10.83,9.45 10,9.45 C9.17,9.45 8.5,10.12 8.5,10.95c "/>
+            <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M13.01 15.01 C13.3,15.31 13.77,15.31 14.07,15.01 C15.15,13.93 15.75,12.48 15.75,10.95 C15.75,9.41 15.15,7.97 14.07,6.88 C11.82,4.64 8.18,4.64 5.94,6.88 C4.85,7.97 4.25,9.41 4.25,10.95 C4.25,12.48 4.85,13.93 5.94,15.01 C6.08,15.16 6.27,15.23 6.47,15.23 C6.66,15.23 6.85,15.16 6.99,15.01 C7.29,14.72 7.29,14.25 6.99,13.95 C6.19,13.15 5.75,12.08 5.75,10.95 C5.75,9.81 6.19,8.74 6.99,7.94 C8.65,6.28 11.35,6.28 13.01,7.94 C13.81,8.74 14.25,9.81 14.25,10.95 C14.25,12.08 13.81,13.15 13.01,13.95 C12.71,14.25 12.71,14.72 13.01,15.01c "/>
+            <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M16.36 18.06 C16.56,18.06 16.75,17.99 16.89,17.84 C18.74,16 19.75,13.55 19.75,10.95 C19.75,8.34 18.74,5.89 16.89,4.05 C13.09,0.25 6.91,0.25 3.11,4.05 C1.26,5.89 0.25,8.34 0.25,10.95 C0.25,13.55 1.26,16 3.11,17.84 C3.4,18.13 3.87,18.13 4.17,17.84 C4.46,17.55 4.46,17.07 4.17,16.78 C2.61,15.22 1.75,13.15 1.75,10.95 C1.75,8.74 2.61,6.67 4.17,5.11 C7.38,1.9 12.62,1.9 15.83,5.11 C17.39,6.67 18.25,8.74 18.25,10.95 C18.25,13.15 17.39,15.22 15.83,16.78 C15.54,17.07 15.54,17.55 15.83,17.84 C15.98,17.99 16.17,18.06 16.36,18.06c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..ae64e56
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable">
+    <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/>
+    <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+    <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+    <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+    <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+    <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/>
+    <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/>
+    <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/>
+    <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..4a2505a
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="24dp" android:width="24dp" android:viewportHeight="24"
+        android:viewportWidth="24">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_4_G" android:translateX="0.10500000000000043"
+               android:translateY="1.7490000000000006">
+            <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c  M11.9 17.89 C11.9,17.89 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 11.9,17.89 11.9,17.89c "/>
+        </group>
+        <group android:name="_R_G_L_3_G" android:translateX="0.10500000000000043"
+               android:translateY="1.75">
+            <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 7.48,14.87 7.48,14.87 C7.48,14.87 7.48,14.87 7.48,14.87 C7.48,14.87 8,15.51 8,15.51 C8,15.51 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 15.79,15.51 15.79,15.51 C15.79,15.51 16.31,14.87 16.31,14.87 C16.31,14.87 16.31,14.87 16.31,14.87 C16.31,14.87 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c  M15.67 13.29 C15.57,13.15 15.47,13.01 15.36,12.88 C14.54,11.88 13.3,11.24 11.9,11.24 C10.5,11.24 9.25,11.88 8.43,12.88 C8.32,13.01 8.22,13.15 8.12,13.29 C8.12,13.29 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 15.67,13.29 15.67,13.29c "/>
+        </group>
+        <group android:name="_R_G_L_2_G" android:translateX="0.10500000000000043"
+               android:translateY="1.7490000000000006">
+            <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M11.89 0.25 C7.65,0.25 3.77,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.89,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.48,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.89,0.25c  M17.61 10.93 C17.5,10.8 17.4,10.66 17.28,10.54 C15.92,9.12 14.01,8.24 11.89,8.24 C9.77,8.24 7.86,9.12 6.51,10.54 C6.39,10.66 6.29,10.8 6.18,10.92 C6.18,10.92 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.89,1.75 C15.6,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 17.61,10.93 17.61,10.93c "/>
+        </group>
+        <group android:name="_R_G_L_1_G" android:translateX="0.10500000000000043"
+               android:translateY="1.7490000000000006">
+            <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 3.2,9.66 3.2,9.66 C3.2,9.66 3.2,9.66 3.2,9.66 C3.2,9.66 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 20.6,9.66 20.6,9.66 C20.6,9.66 20.59,9.66 20.59,9.66 C20.59,9.66 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c  M19.6 8.5 C19.51,8.41 19.43,8.32 19.34,8.23 C17.41,6.37 14.8,5.24 11.9,5.24 C8.99,5.24 6.38,6.38 4.45,8.23 C4.36,8.32 4.28,8.41 4.19,8.51 C4.19,8.51 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 19.6,8.5 19.6,8.5c "/>
+        </group>
+        <group android:name="_R_G_L_0_G" android:translateX="0.10500000000000043"
+               android:translateY="1.75">
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+                  android:fillAlpha="1" android:fillType="nonZero"
+                  android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c "/>
+        </group>
+    </group>
+    <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml
new file mode 100644
index 0000000..ebcac82
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="config_batterymeterPerimeterPath" translatable="false">
+        M 11,1.505 H 8 V 0.995 C 8,0.445 7.55,-0.005 7,-0.005 H 5 C 4.45,-0.005 4,0.445 4,0.995 V 1.505 H 1 C 0.45,1.505 0,1.955 0,2.505 V 19.005 C 0,19.555 0.45,20.005 1,20.005 H 11 C 11.55,20.005 12,19.555 12,19.005 V 2.505 C 12,1.955 11.543,1.505 11,1.505 Z M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z
+    </string>
+
+    <string name="config_batterymeterFillMask" translatable="false">
+        M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z
+    </string>
+    <string name="config_batterymeterBoltPath" translatable="false">
+        M 3.92,11.5 H 5 V 15.01 C 5,15.17 5.13,15.26 5.25,15.26 5.33,15.26 5.42,15.22 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 H 7 V 5.99 C 7,5.83 6.87,5.74 6.75,5.74 6.67,5.74 6.58,5.78 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 Z
+    </string>
+    <string name="config_batterymeterPowersavePath" translatable="false">
+        M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.66,9.7499 8.25,9.7499 H 6.75 V 8.2499 C 6.75,7.8399 6.41,7.4999 6,7.4999 5.59,7.4999 5.2794,7.841 5.25,8.2499 V 9.7499 H 3.75 C 3.34,9.7499 3,10.09 3,10.5 3,10.91 3.3401,11.25 3.75,11.25 Z
+    </string>
+</resources>
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk
new file mode 100644
index 0000000..ae48186
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+#  Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackRoundedThemePicker
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackRoundedThemePickerOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..9a90a05
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.theme.icon_pack.rounded.themepicker"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/>
+    <application android:label="Rounded" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml
new file mode 100644
index 0000000..707369a
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M4.67,12.75h6.58v6.58C11.25,19.7,11.59,20,12,20s0.75-0.3,0.75-0.67v-6.58h6.58C19.7,12.75,20,12.41,20,12 s-0.3-0.75-0.67-0.75h-6.58V4.67C12.75,4.3,12.41,4,12,4s-0.75,0.3-0.75,0.67v6.58H4.67C4.3,11.25,4,11.59,4,12 S4.3,12.75,4.67,12.75z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml
new file mode 100644
index 0000000..1dca14d
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18.78,5.22c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L6.28,5.22c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-5.72,5.72c-0.29,0.29-0.29,0.77,0,1.06C5.37,18.93,5.56,19,5.75,19s0.38-0.07,0.53-0.22L12,13.06l5.72,5.72 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l5.72-5.72 C19.07,5.99,19.07,5.51,18.78,5.22z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml
new file mode 100644
index 0000000..5c21b23
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11.47,4.47c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68C3.11,16.89,3,17.15,3,17.42l0,2.58c0,0.55,0.45,1,1,1L6.58,21 c0,0,0,0,0,0c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22c0.19,0,0.38-0.07,0.53-0.22 c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83c-0.78-0.78-2.05-0.78-2.83,0l-2.97,2.97l-2.09-2.09 C12.24,4.17,11.77,4.17,11.47,4.47z M6.38,19.5L4.5,19.49l0-1.87l9-9l1.88,1.88L6.38,19.5z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml
new file mode 100644
index 0000000..48a430f
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,4h-1h-4c0-0.55-0.45-1-1-1h-4C9.45,3,9,3.45,9,4H5H4C3.59,4,3.25,4.34,3.25,4.75S3.59,5.5,4,5.5h1V18 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V5.5h1c0.41,0,0.75-0.34,0.75-0.75S20.41,4,20,4z M17.5,18c0,0.83-0.67,1.5-1.5,1.5H8 c-0.83,0-1.5-0.67-1.5-1.5V5.5h11V18z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M14.25,8c-0.41,0-0.75,0.34-0.75,0.75v7.5c0,0.41,0.34,0.75,0.75,0.75S15,16.66,15,16.25v-7.5C15,8.34,14.66,8,14.25,8z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9.75,8C9.34,8,9,8.34,9,8.75v7.5C9,16.66,9.34,17,9.75,17s0.75-0.34,0.75-0.75v-7.5C10.5,8.34,10.16,8,9.75,8z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml
new file mode 100644
index 0000000..bbae929
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,2H4C2.9,2,2,2.9,2,4v16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M20.5,20c0,0.27-0.23,0.5-0.5,0.5H4 c-0.27,0-0.5-0.23-0.5-0.5V4c0-0.27,0.23-0.5,0.5-0.5h16c0.27,0,0.5,0.23,0.5,0.5V20z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml
new file mode 100644
index 0000000..9c9d663
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.07,4.93c-3.91-3.9-10.24-3.9-14.14,0.01s-3.9,10.24,0.01,14.14s10.24,3.9,14.14-0.01C20.95,17.2,22,14.65,22,12 C22,9.35,20.94,6.81,19.07,4.93z M18,18c-1.6,1.59-3.76,2.48-6.02,2.48c-4.69-0.01-8.49-3.83-8.48-8.52 c0.01-4.69,3.83-8.49,8.52-8.48c4.69,0.01,8.49,3.83,8.48,8.52C20.49,14.25,19.6,16.41,18,18z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12.75,11.69V6.75C12.75,6.34,12.41,6,12,6s-0.75,0.34-0.75,0.75V12c0,0.2,0.08,0.39,0.22,0.53l3.25,3.25 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml
new file mode 100644
index 0000000..c81ca1e
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,7C20.66,7,21,6.66,21,6.25S20.66,5.5,20.25,5.5H18.5V3.75C18.5,3.34,18.16,3,17.75,3S17,3.34,17,3.75V5.5h-4.25 V3.75C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75V5.5H7V3.75C7,3.34,6.66,3,6.25,3S5.5,3.34,5.5,3.75V5.5H3.75 C3.34,5.5,3,5.84,3,6.25S3.34,7,3.75,7H5.5v4.25H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H5.5V17H3.75 C3.34,17,3,17.34,3,17.75s0.34,0.75,0.75,0.75H5.5v1.75C5.5,20.66,5.84,21,6.25,21S7,20.66,7,20.25V18.5h4.25v1.75 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5H17v1.75c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5h1.75 c0.41,0,0.75-0.34,0.75-0.75S20.66,17,20.25,17H18.5v-4.25h1.75c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75H18.5V7H20.25z M7,7h4.25v4.25H7V7z M7,17v-4.25h4.25V17H7z M17,17h-4.25v-4.25H17V17z M17,11.25h-4.25V7H17V11.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml
new file mode 100644
index 0000000..32d154b
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11.5,22h1c1.1,0,2-0.9,2-2v-6H16c1.66,0,3-1.34,3-3V3c0-0.55-0.45-1-1-1H6C5.45,2,5,2.45,5,3v8c0,1.66,1.34,3,3,3h1.5v6 C9.5,21.1,10.4,22,11.5,22z M9,3.5v2.75C9,6.66,9.34,7,9.75,7s0.75-0.34,0.75-0.75V3.5h3v2.75C13.5,6.66,13.84,7,14.25,7 S15,6.66,15,6.25V3.5h2.5V9h-11V3.5H9z M8,12.5c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11c0,0.83-0.67,1.5-1.5,1.5h-3V14v6 c0,0.28-0.22,0.5-0.5,0.5h-1c-0.28,0-0.5-0.22-0.5-0.5v-6v-1.5H8z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
new file mode 100644
index 0000000..21daf9d
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M3.75,10.75c0.41,0,0.75-0.34,0.75-0.75V4.75c0-0.14,0.11-0.25,0.25-0.25H10c0.41,0,0.75-0.34,0.75-0.75S10.41,3,10,3 H4.75C3.79,3,3,3.79,3,4.75V10C3,10.41,3.34,10.75,3.75,10.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10,19.5H4.75c-0.14,0-0.25-0.11-0.25-0.25V14c0-0.41-0.34-0.75-0.75-0.75S3,13.59,3,14v5.25C3,20.21,3.79,21,4.75,21H10 c0.41,0,0.75-0.34,0.75-0.75S10.41,19.5,10,19.5z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,13.25c-0.41,0-0.75,0.34-0.75,0.75v5.25c0,0.14-0.11,0.25-0.25,0.25H14c-0.41,0-0.75,0.34-0.75,0.75 S13.59,21,14,21h5.25c0.96,0,1.75-0.79,1.75-1.75V14C21,13.59,20.66,13.25,20.25,13.25z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.25,3H14c-0.41,0-0.75,0.34-0.75,0.75S13.59,4.5,14,4.5h5.25c0.14,0,0.25,0.11,0.25,0.25V10 c0,0.41,0.34,0.75,0.75,0.75S21,10.41,21,10V4.75C21,3.79,20.21,3,19.25,3z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M13.73,12.37l-2.6,3.35l-1.74-2.1c-0.2-0.25-0.58-0.24-0.78,0.01l-1.99,2.56C6.36,16.52,6.59,17,7.01,17h9.98 c0.41,0,0.65-0.47,0.4-0.8l-2.87-3.83C14.32,12.11,13.93,12.11,13.73,12.37z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml
new file mode 100644
index 0000000..19ce4e3
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,9h-2.51c0,0.1,0.01,0.2,0.01,0.3v1.2H20c0.28,0,0.5,0.22,0.5,0.5v9c0,0.28-0.22,0.5-0.5,0.5H10 c-0.28,0-0.5-0.22-0.5-0.5v-2.5H8V20c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M16,9c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9c0,3.87,3.13,7,7,7C12.87,16,16,12.87,16,9z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml
new file mode 100644
index 0000000..2a56cc5
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="20dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="20dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M3.75,4.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75H14v-1.5H3.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,4.75H17v-1.5c0-0.41-0.34-0.75-0.75-0.75S15.5,2.84,15.5,3.25v4.5c0,0.41,0.34,0.75,0.75,0.75S17,8.16,17,7.75v-1.5 h3.25C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,11.25H10v1.5h10.25c0.41,0,0.75-0.34,0.75-0.75S20.66,11.25,20.25,11.25z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M3.75,17.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75H10v-1.5H3.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.25,17.75H13v-1.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v4.5c0,0.41,0.34,0.75,0.75,0.75S13,21.16,13,20.75 v-1.5h7.25c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M8.5,9.75C8.5,9.34,8.16,9,7.75,9S7,9.34,7,9.75v1.5H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H7v1.5 C7,14.66,7.34,15,7.75,15s0.75-0.34,0.75-0.75V9.75z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml
new file mode 100644
index 0000000..0a1c305
--- /dev/null
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2.75C7.95,2.69,4.05,4.3,1.22,7.2C0.96,7.5,0.97,7.95,1.24,8.23C1.53,8.53,2,8.54,2.3,8.25c2.55-2.61,6.05-4.06,9.7-4 c3.65-0.06,7.17,1.4,9.72,4.02c0.28,0.27,0.73,0.28,1.03,0.01c0.31-0.28,0.33-0.75,0.05-1.06C19.96,4.32,16.06,2.69,12,2.75z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M15.78,14.82c0.05,0.06,0.1,0.11,0.17,0.15c0.34,0.23,0.81,0.14,1.04-0.21s0.14-0.81-0.21-1.04 c-2.64-2.64-6.91-2.64-9.55,0c-0.27,0.29-0.27,0.73,0,1.02c0.28,0.3,0.76,0.32,1.06,0.04h0.03c0,0,0,0,0.01-0.01 c2.05-2.05,5.37-2.04,7.42,0.01C15.75,14.8,15.76,14.81,15.78,14.82z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.03,11.79c0.3-0.29,0.3-0.77,0.01-1.06h-0.01c-2.12-2.18-5.01-3.44-8.04-3.5c-3.04,0.06-5.93,1.32-8.05,3.5 c-0.29,0.3-0.28,0.77,0.01,1.06c0.3,0.29,0.77,0.28,1.06-0.01c1.85-1.88,4.36-2.96,7-3c2.62,0.05,5.11,1.13,6.95,3 C19.25,12.07,19.73,12.08,20.03,11.79z" />
+</vector>
\ No newline at end of file
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 21c6035..10387f1 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7206,6 +7206,16 @@
     // ACTION: Share Wi-Fi hotspot by generating a QR code
     ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712;
 
+    // OPEN: Settings > Network & internet > Mobile network > Delete sim
+    DIALOG_DELETE_SIM_CONFIRMATION = 1713;
+
+    // OPEN: Settings >  Network & internet > Mobile network > Delete sim > (answer yes to
+    //       confirmation)
+    DIALOG_DELETE_SIM_PROGRESS = 1714;
+
+    // Settings > Apps and notifications > Notifications > Gentle notifications
+    GENTLE_NOTIFICATIONS_SCREEN = 1715;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 3d392c7..80b0375 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -211,25 +211,26 @@
      * Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}.
      */
     @NonNull
-    static ArraySet<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) {
-        final ArraySet<AutofillId> ids = new ArraySet<>();
+    static ArrayList<AutofillId> getAutofillIds(@NonNull AssistStructure structure,
+            boolean autofillableOnly) {
+        final ArrayList<AutofillId> ids = new ArrayList<>();
         final int size = structure.getWindowNodeCount();
         for (int i = 0; i < size; i++) {
             final WindowNode node = structure.getWindowNodeAt(i);
-            addAutofillableIds(node.getRootViewNode(), ids);
+            addAutofillableIds(node.getRootViewNode(), ids, autofillableOnly);
         }
         return ids;
     }
 
     private static void addAutofillableIds(@NonNull ViewNode node,
-            @NonNull ArraySet<AutofillId> ids) {
-        if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
+            @NonNull ArrayList<AutofillId> ids, boolean autofillableOnly) {
+        if (!autofillableOnly || node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
             ids.add(node.getAutofillId());
         }
         final int size = node.getChildCount();
         for (int i = 0; i < size; i++) {
             final ViewNode child = node.getChildAt(i);
-            addAutofillableIds(child, ids);
+            addAutofillableIds(child, ids, autofillableOnly);
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7b97353..f35e464 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,7 +26,6 @@
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
-import static android.view.autofill.Helper.toList;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.getNumericValue;
@@ -283,7 +282,7 @@
      * on autofilling the app.
      */
     @GuardedBy("mLock")
-    private ArraySet<AutofillId> mAugmentedAutofillableIds;
+    private ArrayList<AutofillId> mAugmentedAutofillableIds;
 
     /**
      * When {@code true}, the session was created only to handle Augmented Autofill requests (i.e.,
@@ -336,6 +335,12 @@
                     return;
                 }
 
+                final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure,
+                        /* autofillableOnly= */false);
+                for (int i = 0; i < ids.size(); i++) {
+                    ids.get(i).setSessionId(Session.this.id);
+                }
+
                 // Flags used to start the session.
                 int flags = structure.getFlags();
 
@@ -2259,6 +2264,7 @@
                     + id + " destroyed");
             return;
         }
+        id.setSessionId(this.id);
         if (sVerbose) {
             Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action="
                     + actionAsString(action) + ", flags=" + flags);
@@ -2528,14 +2534,14 @@
     }
 
     private void notifyUnavailableToClient(int sessionFinishedState,
-            @Nullable ArraySet<AutofillId> autofillableIds) {
+            @Nullable ArrayList<AutofillId> autofillableIds) {
         synchronized (mLock) {
             if (mCurrentViewId == null) return;
             try {
                 if (mHasCallback) {
                     mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState);
                 } else if (sessionFinishedState != 0) {
-                    mClient.setSessionFinished(sessionFinishedState, toList(autofillableIds));
+                    mClient.setSessionFinished(sessionFinishedState, autofillableIds);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
@@ -2661,10 +2667,10 @@
 
         final FillContext context = getFillContextByRequestIdLocked(requestId);
 
-        final ArraySet<AutofillId> autofillableIds;
+        final ArrayList<AutofillId> autofillableIds;
         if (context != null) {
             final AssistStructure structure = context.getStructure();
-            autofillableIds = Helper.getAutofillableIds(structure);
+            autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true);
         } else {
             Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId);
             autofillableIds = null;
@@ -2770,7 +2776,9 @@
                 remoteService.getComponentName().getPackageName());
         mAugmentedRequestsLogs.add(log);
 
-        remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, mCurrentViewId,
+        final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
+
+        remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
                 currentValue);
 
         if (mAugmentedAutofillDestroyer == null) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 843aa74..73f5cb8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -297,6 +297,9 @@
         // First apply the unconditional transformations (if any) to the templates.
         final ArrayList<Pair<Integer, InternalTransformation>> transformations =
                 customDescription.getTransformations();
+        if (sVerbose) {
+            Slog.v(TAG, "applyCustomDescription(): transformations = " + transformations);
+        }
         if (transformations != null) {
             if (!InternalTransformation.batchApply(valueFinder, template, transformations)) {
                 Slog.w(TAG, "could not apply main transformations on custom description");
@@ -345,6 +348,10 @@
             // Apply batch updates (if any).
             final ArrayList<Pair<InternalValidator, BatchUpdates>> updates =
                     customDescription.getUpdates();
+            if (sVerbose) {
+                Slog.v(TAG, "applyCustomDescription(): view = " + customSubtitleView
+                        + " updates=" + updates);
+            }
             if (updates != null) {
                 final int size = updates.size();
                 if (sDebug) Slog.d(TAG, "custom description has " + size + " batch updates");
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index a7921b5..4399e42 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -161,8 +161,14 @@
     @Override // from PerUserSystemService
     @GuardedBy("mLock")
     protected boolean updateLocked(boolean disabled) {
-        destroyLocked();
         final boolean disabledStateChanged = super.updateLocked(disabled);
+        if (disabledStateChanged) {
+            // update session content capture enabled state.
+            for (int i = 0; i < mSessions.size(); i++) {
+                mSessions.valueAt(i).setContentCaptureEnabledLocked(!disabled);
+            }
+        }
+        destroyLocked();
         updateRemoteServiceLocked(disabled);
         return disabledStateChanged;
     }
@@ -542,7 +548,8 @@
                 Slog.v(TAG, "setContentCaptureWhitelist(" + (packages == null
                         ? "null_packages" : packages.size() + " packages")
                         + ", " + (activities == null
-                        ? "null_activities" : activities.size() + " activities") + ")");
+                        ? "null_activities" : activities.size() + " activities") + ")"
+                        + " for user " + mUserId);
             }
             mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index d38dfd4..2643db1 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -16,6 +16,8 @@
 package com.android.server.contentcapture;
 
 import static android.service.contentcapture.ContentCaptureService.setClientState;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
 import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
 import static android.view.contentcapture.ContentCaptureSession.STATE_ACTIVE;
 import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
@@ -24,13 +26,16 @@
 
 import android.annotation.NonNull;
 import android.content.ComponentName;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.service.contentcapture.ContentCaptureService;
 import android.service.contentcapture.SnapshotData;
 import android.util.LocalLog;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.MainContentCaptureSession;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
@@ -108,6 +113,20 @@
     }
 
     /**
+     * Changes the {@link ContentCaptureService} enabled state.
+     */
+    @GuardedBy("mLock")
+    public void setContentCaptureEnabledLocked(boolean enabled) {
+        try {
+            final Bundle extras = new Bundle();
+            extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true);
+            mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error async reporting result to client: " + e);
+        }
+    }
+
+    /**
      * Notifies the {@link ContentCaptureService} of a snapshot of an activity.
      */
     @GuardedBy("mLock")
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 9b70272..7709311 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -28,6 +28,7 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -99,11 +100,17 @@
             ActivityManager.TaskSnapshot snapshot =
                     mActivityTaskManagerInternal.getTaskSnapshot(taskId, false);
             GraphicBuffer snapshotBuffer = null;
+            int colorSpaceId = 0;
             if (snapshot != null) {
                 snapshotBuffer = snapshot.getSnapshot();
+                ColorSpace colorSpace = snapshot.getColorSpace();
+                if (colorSpace != null) {
+                    colorSpaceId = colorSpace.getId();
+                }
             }
 
-            service.provideContextImage(taskId, snapshotBuffer, imageContextRequestExtras);
+            service.provideContextImage(taskId, snapshotBuffer, colorSpaceId,
+                    imageContextRequestExtras);
         }
     }
 
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
index 4b36352..a8b7b81 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
@@ -68,9 +68,9 @@
     }
 
     void provideContextImage(int taskId, @Nullable GraphicBuffer contextImage,
-            @NonNull Bundle imageContextRequestExtras) {
+            int colorSpaceId, @NonNull Bundle imageContextRequestExtras) {
         scheduleAsyncRequest((s) -> s.provideContextImage(taskId, contextImage,
-                imageContextRequestExtras));
+                colorSpaceId, imageContextRequestExtras));
     }
 
     void suggestContentSelections(
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 6573c3b..188d654 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1136,11 +1136,21 @@
     public void unbindBluetoothProfileService(int bluetoothProfile,
             IBluetoothProfileServiceConnection proxy) {
         synchronized (mProfileServices) {
-            ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
+            Integer profile = new Integer(bluetoothProfile);
+            ProfileServiceConnections psc = mProfileServices.get(profile);
             if (psc == null) {
                 return;
             }
             psc.removeProxy(proxy);
+            if (psc.isEmpty()) {
+                // All prxoies are disconnected, unbind with the service.
+                try {
+                    mContext.unbindService(psc);
+                } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
+                }
+                mProfileServices.remove(profile);
+            }
         }
     }
 
@@ -1298,6 +1308,10 @@
             mProxies.kill();
         }
 
+        private boolean isEmpty() {
+            return mProxies.getRegisteredCallbackCount() == 0;
+        }
+
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             // remove timeout message
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 44fc45e..d52fe81 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -120,7 +120,9 @@
 import com.android.server.location.PassiveProvider;
 import com.android.server.location.RemoteListenerHelper;
 
+import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
+import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -2299,6 +2301,7 @@
         private boolean mIsForegroundUid;
         private Location mLastFixBroadcast;
         private long mLastStatusBroadcast;
+        private Throwable mStackTrace;  // for debugging only
 
         /**
          * Note: must be constructed with lock held.
@@ -2311,6 +2314,10 @@
             mIsForegroundUid = isImportanceForeground(
                     mActivityManager.getPackageImportance(mReceiver.mCallerIdentity.mPackageName));
 
+            if (D && receiver.mCallerIdentity.mPid == Process.myPid()) {
+                mStackTrace = new Throwable();
+            }
+
             ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
             if (records == null) {
                 records = new ArrayList<>();
@@ -2361,11 +2368,26 @@
 
         @Override
         public String toString() {
-            return "UpdateRecord[" + mProvider + " " + mReceiver.mCallerIdentity.mPackageName
-                    + "(" + mReceiver.mCallerIdentity.mUid + (mIsForegroundUid ? " foreground"
-                    : " background")
-                    + ")" + " " + mRealRequest + " "
-                    + mReceiver.mWorkSource + "]";
+            StringBuilder b = new StringBuilder("UpdateRecord[");
+            b.append(mProvider).append(" ");
+            b.append(mReceiver.mCallerIdentity.mPackageName);
+            b.append("(").append(mReceiver.mCallerIdentity.mUid);
+            if (mIsForegroundUid) {
+                b.append(" foreground");
+            } else {
+                b.append(" background");
+            }
+            b.append(") ");
+            b.append(mRealRequest).append(" ").append(mReceiver.mWorkSource);
+
+            if (mStackTrace != null) {
+                ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+                mStackTrace.printStackTrace(new PrintStream(tmp));
+                b.append("\n\n").append(tmp.toString()).append("\n");
+            }
+
+            b.append("]");
+            return b.toString();
         }
     }
 
@@ -3642,6 +3664,13 @@
                 }
             }
 
+            if (!mIgnoreSettingsPackageWhitelist.isEmpty()) {
+                pw.println("  Bypass Whitelisted Packages:");
+                for (String packageName : mIgnoreSettingsPackageWhitelist) {
+                    pw.println("    " + packageName);
+                }
+            }
+
             if (mLocationFudger != null) {
                 pw.append("  fudger: ");
                 mLocationFudger.dump(fd, pw, args);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 3a50aa8..0eb3a84 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -33,6 +33,8 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Binder;
 import android.os.Handler;
@@ -45,7 +47,7 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.service.dreams.Sandman;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -87,6 +89,7 @@
     private boolean mVrHeadset;
     private boolean mComputedNightMode;
     private int mCarModeEnableFlags;
+    private boolean mSetupWizardComplete;
 
     // flag set by resource, whether to enable Car dock launch when starting car mode.
     private boolean mEnableCarDockLaunch = true;
@@ -196,6 +199,29 @@
         }
     };
 
+    private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            // setup wizard is done now so we can unblock
+            if (setupWizardCompleteForCurrentUser()) {
+                mSetupWizardComplete = true;
+                getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
+                // update night mode
+                Context context = getContext();
+                updateNightModeFromSettings(context, context.getResources(),
+                        UserHandle.getCallingUserId());
+                updateLocked(0, 0);
+            }
+        }
+    };
+
+    @Override
+    public void onSwitchUser(int userHandle) {
+        super.onSwitchUser(userHandle);
+        getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
+        verifySetupWizardCompleted();
+    }
+
     @Override
     public void onStart() {
         final Context context = getContext();
@@ -204,6 +230,10 @@
                 (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
 
+        // If setup isn't complete for this user listen for completion so we can unblock
+        // being able to send a night mode configuration change event
+        verifySetupWizardCompleted();
+
         context.registerReceiver(mDockModeReceiver,
                 new IntentFilter(Intent.ACTION_DOCK_EVENT));
         IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
@@ -262,6 +292,25 @@
         context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
     }
 
+    // Records whether setup wizard has happened or not and adds an observer for this user if not.
+    private void verifySetupWizardCompleted() {
+        final Context context = getContext();
+        final int userId = UserHandle.getCallingUserId();
+        if (!setupWizardCompleteForCurrentUser()) {
+            mSetupWizardComplete = false;
+            context.getContentResolver().registerContentObserver(
+                    Secure.getUriFor(
+                            Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId);
+        } else {
+            mSetupWizardComplete = true;
+        }
+    }
+
+    private boolean setupWizardCompleteForCurrentUser() {
+        return Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
+    }
+
     /**
      * Updates the night mode setting in Settings.Global and returns if the value was successfully
      * changed.
@@ -274,8 +323,12 @@
         final int defaultNightMode = res.getInteger(
                 com.android.internal.R.integer.config_defaultNightMode);
         int oldNightMode = mNightMode;
-        mNightMode = Settings.Secure.getIntForUser(context.getContentResolver(),
-                Settings.Secure.UI_NIGHT_MODE, defaultNightMode, userId);
+        if (mSetupWizardComplete) {
+            mNightMode = Secure.getIntForUser(context.getContentResolver(),
+                    Secure.UI_NIGHT_MODE, defaultNightMode, userId);
+        } else {
+            mNightMode = defaultNightMode;
+        }
 
         // false if night mode stayed the same, true otherwise.
         return !(oldNightMode == mNightMode);
@@ -340,6 +393,10 @@
                 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
                 return;
             }
+            if (!mSetupWizardComplete) {
+                Slog.d(TAG, "Night mode cannot be changed before setup wizard completes.");
+                return;
+            }
             switch (mode) {
                 case UiModeManager.MODE_NIGHT_NO:
                 case UiModeManager.MODE_NIGHT_YES:
@@ -356,8 +413,8 @@
                     if (mNightMode != mode) {
                         // Only persist setting if not in car mode
                         if (!mCarModeEnabled) {
-                            Settings.Secure.putIntForUser(getContext().getContentResolver(),
-                                    Settings.Secure.UI_NIGHT_MODE, mode, user);
+                            Secure.putIntForUser(getContext().getContentResolver(),
+                                    Secure.UI_NIGHT_MODE, mode, user);
                         }
 
                         mNightMode = mode;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11ddceb..4d0d3d2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2185,24 +2185,38 @@
                 "hidden_api_access_statslog_sampling_rate";
 
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
-            int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0x0);
-            if (logSampleRate < 0 || logSampleRate > 0x10000) {
-                logSampleRate = -1;
-            }
-            if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
+            int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE,
+                    mLogSampleRate);
+            int statslogSampleRate = properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE,
+                    mStatslogSampleRate);
+            setSampleRates(logSampleRate, statslogSampleRate);
+        }
+
+        private void setSampleRates(int logSampleRate, int statslogSampleRate) {
+            if (logSampleRate >= 0 && logSampleRate <= 0x10000
+                    && logSampleRate != mLogSampleRate) {
                 mLogSampleRate = logSampleRate;
                 ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate);
             }
 
-            int statslogSampleRate =
-                    properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0);
-            if (statslogSampleRate < 0 || statslogSampleRate > 0x10000) {
-                statslogSampleRate = -1;
-            }
-            if (statslogSampleRate != -1 && statslogSampleRate != mStatslogSampleRate) {
+            if (statslogSampleRate >= 0 && statslogSampleRate <= 0x10000
+                    && statslogSampleRate != mStatslogSampleRate) {
                 mStatslogSampleRate = statslogSampleRate;
                 ZYGOTE_PROCESS.setHiddenApiAccessStatslogSampleRate(mStatslogSampleRate);
             }
+
+        }
+
+        /**
+         * Set initial sampling rates from DeviceConfig. This is required after each restart,
+         * if they never get updated.
+         */
+        private void initializeSampleRates() {
+            int logSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
+                    HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0);
+            int statslogSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
+                    HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0);
+            setSampleRates(logSampleRate, statslogSampleRate);
         }
 
         public HiddenApiSettings(Handler handler, Context context) {
@@ -2219,6 +2233,7 @@
                     Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY),
                     false,
                     this);
+            initializeSampleRates();
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT,
                     mContext.getMainExecutor(), this);
             update();
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index ee9b561..1c2f51b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -437,6 +437,19 @@
         return next;
     }
 
+    private void postActivityStartTokenRemoval(ProcessRecord app, BroadcastRecord r) {
+        // the receiver had run for less than allowed bg activity start timeout,
+        // so allow the process to still start activities from bg for some more time
+        String msgToken = (app.toShortString() + r.toString()).intern();
+        // first, if there exists a past scheduled request to remove this token, drop
+        // that request - we don't want the token to be swept from under our feet...
+        mHandler.removeCallbacksAndMessages(msgToken);
+        // ...then schedule the removal of the token after the extended timeout
+        mHandler.postAtTime(() -> {
+            app.removeAllowBackgroundActivityStartsToken(r);
+        }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
+    }
+
     public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
             String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
         final int state = r.state;
@@ -453,17 +466,8 @@
                 // just remove the token for this process now and we're done
                 r.curApp.removeAllowBackgroundActivityStartsToken(r);
             } else {
-                // the receiver had run for less than allowed bg activity start timeout,
-                // so allow the process to still start activities from bg for some more time
-                String msgToken = (r.curApp.toShortString() + r.toString()).intern();
-                // first, if there exists a past scheduled request to remove this token, drop
-                // that request - we don't want the token to be swept from under our feet...
-                mHandler.removeCallbacksAndMessages(msgToken);
-                // ...then schedule the removal of the token after the extended timeout
-                final ProcessRecord app = r.curApp;
-                mHandler.postAtTime(() -> {
-                    app.removeAllowBackgroundActivityStartsToken(r);
-                }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
+                // It gets more time; post the removal to happen at the appropriate moment
+                postActivityStartTokenRemoval(r.curApp, r);
             }
         }
         // If we're abandoning this broadcast before any receivers were actually spun up,
@@ -810,19 +814,29 @@
                 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                         new Intent(r.intent), r.resultCode, r.resultData,
                         r.resultExtras, r.ordered, r.initialSticky, r.userId);
+                // parallel broadcasts are fire-and-forget, not bookended by a call to
+                // finishReceiverLocked(), so we manage their activity-start token here
+                if (r.allowBackgroundActivityStarts && !r.ordered) {
+                    postActivityStartTokenRemoval(filter.receiverList.app, r);
+                }
             }
             if (ordered) {
                 r.state = BroadcastRecord.CALL_DONE_RECEIVE;
             }
         } catch (RemoteException e) {
             Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
+            // Clean up ProcessRecord state related to this broadcast attempt
+            if (filter.receiverList.app != null) {
+                filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
+                if (ordered) {
+                    filter.receiverList.app.curReceivers.remove(r);
+                }
+            }
+            // And BroadcastRecord state related to ordered delivery, if appropriate
             if (ordered) {
                 r.receiver = null;
                 r.curFilter = null;
                 filter.receiverList.curBroadcast = null;
-                if (filter.receiverList.app != null) {
-                    filter.receiverList.app.curReceivers.remove(r);
-                }
             }
         }
     }
@@ -1283,6 +1297,8 @@
             } else {
                 if (filter.receiverList != null) {
                     maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+                    // r is guaranteed ordered at this point, so we know finishReceiverLocked()
+                    // will get a callback and handle the activity start token lifecycle.
                 }
                 if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                     scheduleTempWhitelistLocked(filter.owningUid,
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 7c2ea3f..30a297e 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -144,3 +144,6 @@
 30064 am_on_top_resumed_gained_called (User|1|5),(Component Name|3),(Reason|3)
 # The activity's onTopResumedActivityChanged(false) has been called.
 30065 am_on_top_resumed_lost_called (User|1|5),(Component Name|3),(Reason|3)
+
+# An activity been add into stopping list
+30066 am_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 043daef..7abfcea 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -38,6 +38,7 @@
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
 
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
@@ -60,8 +61,8 @@
 import android.app.ActivityManager;
 import android.app.usage.UsageEvents;
 import android.content.Context;
-import android.os.Binder;
 import android.os.Debug;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.PowerManagerInternal;
 import android.os.Process;
@@ -78,6 +79,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowProcessController;
 
@@ -148,6 +150,12 @@
     /** Track all uids that have actively running processes. */
     ActiveUids mActiveUids;
 
+    /**
+     * The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many
+     * threads) for reducing the time spent in {@link #applyOomAdjLocked}.
+     */
+    private final Handler mProcessGroupHandler;
+
     private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
 
     private final ActivityManagerService mService;
@@ -161,6 +169,28 @@
         mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
         mConstants = mService.mConstants;
         mAppCompact = new AppCompactor(mService);
+
+        // The process group is usually critical to the response time of foreground app, so the
+        // setter should apply it as soon as possible.
+        final ServiceThread adjusterThread = new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST,
+                false /* allowIo */);
+        adjusterThread.start();
+        Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP);
+        mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup");
+            final int pid = msg.arg1;
+            final int group = msg.arg2;
+            try {
+                setProcessGroup(pid, group);
+            } catch (Exception e) {
+                if (DEBUG_ALL) {
+                    Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
+                }
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            }
+            return true;
+        });
     }
 
     void initSettings() {
@@ -1726,9 +1756,9 @@
                         processGroup = THREAD_GROUP_DEFAULT;
                         break;
                 }
-                long oldId = Binder.clearCallingIdentity();
+                mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
+                        0 /* unused */, app.pid, processGroup));
                 try {
-                    setProcessGroup(app.pid, processGroup);
                     if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                         // do nothing if we already switched to RT
                         if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
@@ -1791,13 +1821,9 @@
                         }
                     }
                 } catch (Exception e) {
-                    if (false) {
-                        Slog.w(TAG, "Failed setting process group of " + app.pid
-                                + " to " + app.getCurrentSchedulingGroup());
-                        Slog.w(TAG, "at location", e);
+                    if (DEBUG_ALL) {
+                        Slog.w(TAG, "Failed setting thread priority of " + app.pid, e);
                     }
-                } finally {
-                    Binder.restoreCallingIdentity(oldId);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c56d8ea..d04aa89 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3292,7 +3292,7 @@
         pw.println("    Starts a given operation for a particular application.");
         pw.println("  stop [--user <USER_ID>] <PACKAGE | UID> <OP> ");
         pw.println("    Stops a given operation for a particular application.");
-        pw.println("  set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>");
+        pw.println("  set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>");
         pw.println("    Set the mode for a particular application and operation.");
         pw.println("  get [--user <USER_ID>] <PACKAGE | UID> [<OP>]");
         pw.println("    Return the mode for a particular application and optional operation.");
@@ -3305,12 +3305,11 @@
         pw.println("  read-settings");
         pw.println("    Read the last written settings, replacing current state in RAM.");
         pw.println("  options:");
-        pw.println("    <PACKAGE> an Android package name.");
+        pw.println("    <PACKAGE> an Android package name or its UID if prefixed by --uid");
         pw.println("    <OP>      an AppOps operation.");
         pw.println("    <MODE>    one of allow, ignore, deny, or default");
         pw.println("    <USER_ID> the user id under which the package is installed. If --user is not");
         pw.println("              specified, the current user is assumed.");
-        pw.println("    --uid PACKAGE refer to the UID of the package");
     }
 
     static int onShellCommand(Shell shell, String cmd) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 507f398..d510912 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1825,14 +1825,28 @@
                             && streamTypeAlias == AudioSystem.STREAM_MUSIC
                             // vol change on a full volume device
                             && ((device & mFullVolumeDevices) != 0)) {
-                        int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
-                                KeyEvent.KEYCODE_VOLUME_UP;
-                        final long ident = Binder.clearCallingIdentity();
-                        try {
-                            mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
-                            mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
-                        } finally {
-                            Binder.restoreCallingIdentity(ident);
+                        int keyCode = KeyEvent.KEYCODE_UNKNOWN;
+                        switch (direction) {
+                            case AudioManager.ADJUST_RAISE:
+                                keyCode = KeyEvent.KEYCODE_VOLUME_UP;
+                                break;
+                            case AudioManager.ADJUST_LOWER:
+                                keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
+                                break;
+                            case AudioManager.ADJUST_TOGGLE_MUTE:
+                                keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
+                                break;
+                            default:
+                                break;
+                        }
+                        if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
+                            final long ident = Binder.clearCallingIdentity();
+                            try {
+                                mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
+                                mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+                            } finally {
+                                Binder.restoreCallingIdentity(ident);
+                            }
                         }
                     }
 
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 74b7221..1e0f205 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -63,11 +63,11 @@
      */
     public abstract boolean shouldFrameworkHandleLockout();
 
-    public AuthenticationClient(Context context, Metrics metrics,
+    public AuthenticationClient(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
             boolean restricted, String owner, int cookie, boolean requireConfirmation) {
-        super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
+        super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId,
                 restricted, owner, cookie);
         mOpId = opId;
         mRequireConfirmation = requireConfirmation;
@@ -126,7 +126,7 @@
 
         final BiometricServiceBase.ServiceListener listener = getListener();
 
-        mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
+        mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated);
         boolean result = false;
 
         try {
@@ -225,7 +225,7 @@
             final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
             if (result != 0) {
                 Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
-                mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result);
+                mMetricsLogger.histogram(mConstants.tagAuthStartError(), result);
                 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                         0 /* vendorCode */);
                 return result;
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 3f856d3..d3c62be 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -138,7 +138,7 @@
     /**
      * @return the metrics constants for a biometric implementation.
      */
-    protected abstract Metrics getMetrics();
+    protected abstract Constants getConstants();
 
     /**
      * @param userId
@@ -220,7 +220,7 @@
         public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
                 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
-            super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId,
+            super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId,
                     groupId, opId, restricted, owner, cookie, requireConfirmation);
         }
 
@@ -283,7 +283,7 @@
                 IBinder token, ServiceListener listener, int userId, int groupId,
                 byte[] cryptoToken, boolean restricted, String owner,
                 final int[] disabledFeatures) {
-            super(context, getMetrics(), daemon, halDeviceId, token, listener,
+            super(context, getConstants(), daemon, halDeviceId, token, listener,
                     userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
                     disabledFeatures);
         }
@@ -302,7 +302,7 @@
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int templateId, int groupId, int userId,
                 boolean restricted, String owner) {
-            super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId,
+            super(context, getConstants(), daemon, halDeviceId, token, listener, templateId, groupId,
                     userId, restricted, owner, getBiometricUtils());
         }
 
@@ -329,7 +329,7 @@
                 ServiceListener listener, int groupId, int userId, boolean restricted,
                 String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
                 BiometricUtils utils) {
-            super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
+            super(context, getConstants(), daemon, halDeviceId, token, listener, groupId, userId,
                     restricted, owner);
             mEnrolledList = enrolledList;
             mUtils = utils;
@@ -655,7 +655,7 @@
     @Override
     public void serviceDied(long cookie) {
         Slog.e(getTag(), "HAL died");
-        mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+        mMetricsLogger.count(getConstants().tagHalDied(), 1);
         mHALDeathCount++;
         mCurrentUserId = UserHandle.USER_NULL;
         handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
@@ -845,7 +845,7 @@
         }
 
         mHandler.post(() -> {
-            mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
+            mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0);
 
             // Get performance stats object for this user.
             HashMap<Integer, PerformanceStats> pmap
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index 0065580..942e050 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -63,7 +63,7 @@
     private final int mCookie;
 
     protected final MetricsLogger mMetricsLogger;
-    protected final Metrics mMetrics;
+    protected final Constants mConstants;
 
     protected boolean mAlreadyCancelled;
     protected boolean mAlreadyDone;
@@ -80,12 +80,12 @@
      * permission
      * @param owner name of the client that owns this
      */
-    public ClientMonitor(Context context, Metrics metrics,
+    public ClientMonitor(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
             boolean restricted, String owner, int cookie) {
         mContext = context;
-        mMetrics = metrics;
+        mConstants = constants;
         mDaemon = daemon;
         mHalDeviceId = halDeviceId;
         mToken = token;
@@ -108,7 +108,7 @@
     }
 
     protected String getLogTag() {
-        return mMetrics.logTag();
+        return mConstants.logTag();
     }
 
     public int getCookie() {
@@ -145,6 +145,31 @@
     public abstract boolean onEnumerationResult(
             BiometricAuthenticator.Identifier identifier, int remaining);
 
+    public int[] getAcquireIgnorelist() {
+        return new int[0];
+    }
+    public int[] getAcquireVendorIgnorelist() {
+        return new int[0];
+    }
+
+    private boolean blacklistContains(int acquiredInfo, int vendorCode) {
+        if (acquiredInfo == mConstants.acquireVendorCode()) {
+            for (int i = 0; i < getAcquireVendorIgnorelist().length; i++) {
+                if (getAcquireVendorIgnorelist()[i] == vendorCode) {
+                    if (DEBUG) Slog.v(getLogTag(), "Ignoring vendor message: " + vendorCode);
+                    return true;
+                }
+            }
+        } else {
+            for (int i = 0; i < getAcquireIgnorelist().length; i++) {
+                if (getAcquireIgnorelist()[i] == acquiredInfo) {
+                    if (DEBUG) Slog.v(getLogTag(), "Ignoring message: " + acquiredInfo);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 
     public boolean isAlreadyDone() {
         return mAlreadyDone;
@@ -160,7 +185,7 @@
         super.logOnAcquired(mContext, acquiredInfo, vendorCode, getTargetUserId());
         if (DEBUG) Slog.v(getLogTag(), "Acquired: " + acquiredInfo + " " + vendorCode);
         try {
-            if (mListener != null) {
+            if (mListener != null && !blacklistContains(acquiredInfo, vendorCode)) {
                 mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
             }
             return false; // acquisition continues...
diff --git a/services/core/java/com/android/server/biometrics/Metrics.java b/services/core/java/com/android/server/biometrics/Constants.java
similarity index 94%
rename from services/core/java/com/android/server/biometrics/Metrics.java
rename to services/core/java/com/android/server/biometrics/Constants.java
index 02e44e9..874fd42 100644
--- a/services/core/java/com/android/server/biometrics/Metrics.java
+++ b/services/core/java/com/android/server/biometrics/Constants.java
@@ -16,7 +16,7 @@
 
 package com.android.server.biometrics;
 
-public interface Metrics {
+public interface Constants {
     /** The log tag */
     String logTag();
 
@@ -31,4 +31,6 @@
     /** Integers for MetricsLogger.action() */
     int actionBiometricAuth();
     int actionBiometricEnroll();
+
+    int acquireVendorCode();
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index e656d98..854528f 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -40,12 +40,12 @@
 
     public abstract boolean shouldVibrate();
 
-    public EnrollClient(Context context, Metrics metrics,
+    public EnrollClient(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
             byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils,
             final int[] disabledFeatures) {
-        super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+        super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
                 owner, 0 /* cookie */);
         mBiometricUtils = utils;
         mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
@@ -78,7 +78,7 @@
         if (shouldVibrate()) {
             vibrateSuccess();
         }
-        mMetricsLogger.action(mMetrics.actionBiometricEnroll());
+        mMetricsLogger.action(mConstants.actionBiometricEnroll());
         try {
             final BiometricServiceBase.ServiceListener listener = getListener();
             if (listener != null) {
@@ -105,7 +105,7 @@
                     disabledFeatures);
             if (result != 0) {
                 Slog.w(getLogTag(), "startEnroll failed, result=" + result);
-                mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result);
+                mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
                 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                         0 /* vendorCode */);
                 return result;
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 44ac037..f889d2b 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -30,11 +30,11 @@
  * A class to keep track of the enumeration state for a given client.
  */
 public abstract class EnumerateClient extends ClientMonitor {
-    public EnumerateClient(Context context, Metrics metrics,
+    public EnumerateClient(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int groupId, int userId,
             boolean restricted, String owner) {
-        super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+        super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
                 owner, 0 /* cookie */);
     }
 
@@ -55,7 +55,7 @@
             if (result != 0) {
                 Slog.w(getLogTag(), "start enumerate for user " + getTargetUserId()
                     + " failed, result=" + result);
-                mMetricsLogger.histogram(mMetrics.tagEnumerateStartError(), result);
+                mMetricsLogger.histogram(mConstants.tagEnumerateStartError(), result);
                 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                         0 /* vendorCode */);
                 return result;
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index a18f336..bccab7b 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -33,11 +33,11 @@
     private final int mBiometricId;
     private final BiometricUtils mBiometricUtils;
 
-    public RemovalClient(Context context, Metrics metrics,
+    public RemovalClient(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId,
             boolean restricted, String owner, BiometricUtils utils) {
-        super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+        super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
                 owner, 0 /* cookie */);
         mBiometricId = biometricId;
         mBiometricUtils = utils;
@@ -60,7 +60,7 @@
             if (result != 0) {
                 Slog.w(getLogTag(), "startRemove with id = " + mBiometricId + " failed, result=" +
                         result);
-                mMetricsLogger.histogram(mMetrics.tagRemoveStartError(), result);
+                mMetricsLogger.histogram(mConstants.tagRemoveStartError(), result);
                 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                         0 /* vendorCode */);
                 return result;
diff --git a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java b/services/core/java/com/android/server/biometrics/face/FaceConstants.java
similarity index 86%
rename from services/core/java/com/android/server/biometrics/face/FaceMetrics.java
rename to services/core/java/com/android/server/biometrics/face/FaceConstants.java
index 1c5cd5a..143eed5 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceConstants.java
@@ -16,10 +16,12 @@
 
 package com.android.server.biometrics.face;
 
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.server.biometrics.Metrics;
+import android.hardware.face.FaceManager;
 
-public class FaceMetrics implements Metrics {
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.Constants;
+
+public class FaceConstants implements Constants {
     @Override
     public String logTag() {
         return FaceService.TAG;
@@ -64,4 +66,9 @@
     public int actionBiometricEnroll() {
         return MetricsProto.MetricsEvent.ACTION_FACE_ENROLL;
     }
+
+    @Override
+    public int acquireVendorCode() {
+        return FaceManager.FACE_ACQUIRED_VENDOR;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index f5a96c7..feb58a3a 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -65,7 +65,7 @@
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
 import com.android.server.biometrics.EnumerateClient;
-import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.Constants;
 import com.android.server.biometrics.RemovalClient;
 
 import org.json.JSONArray;
@@ -131,6 +131,26 @@
         }
 
         @Override
+        public int[] getAcquireIgnorelist() {
+            if (isBiometricPrompt()) {
+                return mBiometricPromptIgnoreList;
+            } else {
+                // Keyguard
+                return mKeyguardIgnoreList;
+            }
+        }
+
+        @Override
+        public int[] getAcquireVendorIgnorelist() {
+            if (isBiometricPrompt()) {
+                return mBiometricPromptIgnoreListVendor;
+            } else {
+                // Keyguard
+                return mKeyguardIgnoreListVendor;
+            }
+        }
+
+        @Override
         public boolean onAcquired(int acquireInfo, int vendorCode) {
 
             if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
@@ -205,6 +225,17 @@
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
                     0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) {
+
+                @Override
+                public int[] getAcquireIgnorelist() {
+                    return mEnrollIgnoreList;
+                }
+
+                @Override
+                public int[] getAcquireVendorIgnorelist() {
+                    return mEnrollIgnoreListVendor;
+                }
+
                 @Override
                 public boolean shouldVibrate() {
                     return false;
@@ -293,7 +324,7 @@
             }
 
             final boolean restricted = isRestricted();
-            final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+            final RemovalClient client = new RemovalClient(getContext(), getConstants(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
                     0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
                 @Override
@@ -310,7 +341,7 @@
             checkPermission(MANAGE_BIOMETRIC);
 
             final boolean restricted = isRestricted();
-            final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+            final EnumerateClient client = new EnumerateClient(getContext(), getConstants(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
                     userId, restricted, getContext().getOpPackageName()) {
                 @Override
@@ -638,13 +669,20 @@
         }
     }
 
-    private final FaceMetrics mFaceMetrics = new FaceMetrics();
+    private final FaceConstants mFaceConstants = new FaceConstants();
 
     @GuardedBy("this")
     private IBiometricsFace mDaemon;
     // One of the AuthenticationClient constants
     private int mCurrentUserLockoutMode;
 
+    private int[] mBiometricPromptIgnoreList;
+    private int[] mBiometricPromptIgnoreListVendor;
+    private int[] mKeyguardIgnoreList;
+    private int[] mKeyguardIgnoreListVendor;
+    private int[] mEnrollIgnoreList;
+    private int[] mEnrollIgnoreListVendor;
+
     /**
      * Receives callbacks from the HAL.
      */
@@ -830,6 +868,19 @@
 
     public FaceService(Context context) {
         super(context);
+
+        mBiometricPromptIgnoreList = getContext().getResources()
+                .getIntArray(R.array.config_face_acquire_biometricprompt_ignorelist);
+        mBiometricPromptIgnoreListVendor = getContext().getResources()
+                .getIntArray(R.array.config_face_acquire_vendor_biometricprompt_ignorelist);
+        mKeyguardIgnoreList = getContext().getResources()
+                .getIntArray(R.array.config_face_acquire_keyguard_ignorelist);
+        mKeyguardIgnoreListVendor = getContext().getResources()
+                .getIntArray(R.array.config_face_acquire_vendor_keyguard_ignorelist);
+        mEnrollIgnoreList = getContext().getResources()
+                .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
+        mEnrollIgnoreListVendor = getContext().getResources()
+                .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist);
     }
 
     @Override
@@ -855,8 +906,8 @@
     }
 
     @Override
-    protected Metrics getMetrics() {
-        return mFaceMetrics;
+    protected Constants getConstants() {
+        return mFaceConstants;
     }
 
     @Override
@@ -887,7 +938,7 @@
             try {
                 userId = getUserOrWorkProfileId(clientPackage, userId);
                 if (userId != mCurrentUserId) {
-                    final File baseDir = Environment.getDataVendorCeDirectory(userId);
+                    final File baseDir = Environment.getDataVendorDeDirectory(userId);
                     final File faceDir = new File(baseDir, FACE_DATA_DIR);
                     if (!faceDir.exists()) {
                         if (!faceDir.mkdir()) {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java
similarity index 85%
rename from services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
rename to services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java
index a1115c8..bdaff71 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java
@@ -16,10 +16,12 @@
 
 package com.android.server.biometrics.fingerprint;
 
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.server.biometrics.Metrics;
+import android.hardware.fingerprint.FingerprintManager;
 
-public class FingerprintMetrics implements Metrics {
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.Constants;
+
+public class FingerprintConstants implements Constants {
 
     @Override
     public String logTag() {
@@ -65,4 +67,9 @@
     public int actionBiometricEnroll() {
         return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_ENROLL;
     }
+
+    @Override
+    public int acquireVendorCode() {
+        return FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 6ebeaf9..d91670d 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -68,7 +68,7 @@
 import com.android.server.biometrics.BiometricUtils;
 import com.android.server.biometrics.ClientMonitor;
 import com.android.server.biometrics.EnumerateClient;
-import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.Constants;
 import com.android.server.biometrics.RemovalClient;
 
 import org.json.JSONArray;
@@ -284,7 +284,7 @@
             }
 
             final boolean restricted = isRestricted();
-            final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+            final RemovalClient client = new RemovalClient(getContext(), getConstants(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) {
                 @Override
@@ -301,7 +301,7 @@
             checkPermission(MANAGE_FINGERPRINT);
 
             final boolean restricted = isRestricted();
-            final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+            final EnumerateClient client = new EnumerateClient(getContext(), getConstants(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
                     userId, restricted, getContext().getOpPackageName()) {
                 @Override
@@ -557,7 +557,7 @@
         }
     }
 
-    private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
+    private final FingerprintConstants mFingerprintConstants = new FingerprintConstants();
     private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
             new CopyOnWriteArrayList<>();
 
@@ -736,8 +736,8 @@
     }
 
     @Override
-    protected Metrics getMetrics() {
-        return mFingerprintMetrics;
+    protected Constants getConstants() {
+        return mFingerprintConstants;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index cb8a772..2817315 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -23,7 +23,7 @@
 import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
-import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.Constants;
 
 import java.util.List;
 
@@ -75,7 +75,7 @@
     }
 
     @Override
-    protected Metrics getMetrics() {
+    protected Constants getConstants() {
         return null;
     }
 
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index ba4dcdb..998ee1e 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1104,11 +1104,13 @@
 
     @Override
     public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+        final int callingUid = Binder.getCallingUid();
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null && callback != null) {
-                syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
+                syncManager.getSyncStorageEngine().addStatusChangeListener(
+                        mask, UserHandle.getUserId(callingUid), callback);
             }
         } finally {
             restoreCallingIdentity(identityToken);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 9f80a83..7e79a12 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3375,7 +3375,8 @@
             }
 
             scheduleSyncOperationH(op);
-            mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+            mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+                    target.userId);
         }
 
         /**
@@ -3877,7 +3878,8 @@
             EventLog.writeEvent(2720,
                     syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
-                    resultMessage, downstreamActivity, upstreamActivity);
+                    resultMessage, downstreamActivity, upstreamActivity,
+                    syncOperation.target.userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 6b441a0..c7a3f4b 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -43,6 +43,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
@@ -54,6 +55,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IntPair;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -588,9 +590,10 @@
         return mSyncRandomOffset;
     }
 
-    public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+    public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) {
         synchronized (mAuthorities) {
-            mChangeListeners.register(callback, mask);
+            final long cookie = IntPair.of(userId, mask);
+            mChangeListeners.register(callback, cookie);
         }
     }
 
@@ -622,14 +625,16 @@
         }
     }
 
-    void reportChange(int which) {
+    void reportChange(int which, int callingUserId) {
         ArrayList<ISyncStatusObserver> reports = null;
         synchronized (mAuthorities) {
             int i = mChangeListeners.beginBroadcast();
             while (i > 0) {
                 i--;
-                Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
-                if ((which & mask.intValue()) == 0) {
+                final long cookie = (long) mChangeListeners.getBroadcastCookie(i);
+                final int userId = IntPair.first(cookie);
+                final int mask = IntPair.second(cookie);
+                if ((which & mask) == 0 || callingUserId != userId) {
                     continue;
                 }
                 if (reports == null) {
@@ -719,7 +724,7 @@
                     new Bundle(),
                     syncExemptionFlag, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
         queueBackup();
     }
 
@@ -787,7 +792,7 @@
             requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
                     ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId);
     }
 
     public Pair<Long, Long> getBackoff(EndPoint info) {
@@ -833,7 +838,7 @@
             }
         }
         if (changed) {
-            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
         }
     }
 
@@ -871,7 +876,7 @@
     }
 
     public void clearAllBackoffsLocked() {
-        boolean changed = false;
+        final ArraySet<Integer> changedUserIds = new ArraySet<>();
         synchronized (mAuthorities) {
             // Clear backoff for all sync adapters.
             for (AccountInfo accountInfo : mAccounts.values()) {
@@ -888,14 +893,14 @@
                         }
                         authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
                         authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
-                        changed = true;
+                        changedUserIds.add(accountInfo.accountAndUser.userId);
                     }
                 }
             }
         }
 
-        if (changed) {
-            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        for (int i = changedUserIds.size() - 1; i > 0; i--) {
+            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i));
         }
     }
 
@@ -921,7 +926,7 @@
             }
             authority.delayUntil = delayUntil;
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
     }
 
     /**
@@ -964,7 +969,7 @@
                     new Bundle(),
                     syncExemptionFlag, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
         mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
         queueBackup();
     }
@@ -1015,7 +1020,7 @@
             SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
             status.pending = pendingValue;
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId);
     }
 
     /**
@@ -1103,7 +1108,7 @@
                     activeSyncContext.mStartTime);
             getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
         }
-        reportActiveChange();
+        reportActiveChange(activeSyncContext.mSyncOperation.target.userId);
         return syncInfo;
     }
 
@@ -1120,14 +1125,14 @@
             getCurrentSyncs(userId).remove(syncInfo);
         }
 
-        reportActiveChange();
+        reportActiveChange(userId);
     }
 
     /**
      * To allow others to send active change reports, to poke clients.
      */
-    public void reportActiveChange() {
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
+    public void reportActiveChange(int userId) {
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId);
     }
 
     /**
@@ -1162,12 +1167,12 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id);
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId);
         return id;
     }
 
     public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
-                              long downstreamActivity, long upstreamActivity) {
+                              long downstreamActivity, long upstreamActivity, int userId) {
         synchronized (mAuthorities) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Slog.v(TAG, "stopSyncEvent: historyId=" + historyId);
@@ -1307,7 +1312,7 @@
             }
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3abd0ba..6d01375 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -16,12 +16,15 @@
 
 package com.android.server.display;
 
+import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
+import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
-import static android.hardware.display.DisplayManager
-        .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
 import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
 import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
 import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
@@ -1979,6 +1982,18 @@
                 }
             }
 
+            // Sometimes users can have sensitive information in system decoration windows. An app
+            // could create a virtual display with system decorations support and read the user info
+            // from the surface.
+            // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+            // to virtual displays that are owned by the system.
+            if (callingUid != Process.SYSTEM_UID
+                    && (flags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+                if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
+                    throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+                }
+            }
+
             final long token = Binder.clearCallingIdentity();
             try {
                 return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
@@ -2279,9 +2294,7 @@
                     Slog.e(TAG, "Unable to query projection service for permissions", e);
                 }
             }
-            if (mContext.checkCallingPermission(
-                    android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
-                    == PackageManager.PERMISSION_GRANTED) {
+            if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
                 return true;
             }
             return canProjectSecureVideo(projection);
@@ -2297,9 +2310,17 @@
                     Slog.e(TAG, "Unable to query projection service for permissions", e);
                 }
             }
-            return mContext.checkCallingPermission(
-                    android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT)
-                    == PackageManager.PERMISSION_GRANTED;
+            return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
+        }
+
+        private boolean checkCallingPermission(String permission, String func) {
+            if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+            final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid() + " requires " + permission;
+            Slog.w(TAG, msg);
+            return false;
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 30d244f..6330270 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -63,7 +63,9 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
+import android.graphics.Matrix;
 import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManagerInternal;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Binder;
@@ -97,7 +99,9 @@
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.ContextThemeWrapper;
+import android.view.DisplayInfo;
 import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.LayoutInflater;
@@ -300,6 +304,7 @@
     final SettingsObserver mSettingsObserver;
     final IWindowManager mIWindowManager;
     final WindowManagerInternal mWindowManagerInternal;
+    private final DisplayManagerInternal mDisplayManagerInternal;
     final HandlerCaller mCaller;
     final boolean mHasFeature;
     private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
@@ -432,6 +437,32 @@
 
     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
 
+    private static final class ActivityViewInfo {
+        /**
+         * {@link ClientState} where {@link android.app.ActivityView} is running.
+         */
+        private final ClientState mParentClient;
+        /**
+         * {@link Matrix} to convert screen coordinates in the embedded virtual display to
+         * screen coordinates where {@link #mParentClient} exists.
+         */
+        private final Matrix mMatrix;
+
+        ActivityViewInfo(ClientState parentClient, Matrix matrix) {
+            mParentClient = parentClient;
+            mMatrix = matrix;
+        }
+    }
+
+    /**
+     * A mapping table from virtual display IDs created for {@link android.app.ActivityView}
+     * to its parent IME client where {@link android.app.ActivityView} is running.
+     *
+     * <p>Note: this can be used only for virtual display IDs created by
+     * {@link android.app.ActivityView}.</p>
+     */
+    private SparseArray<ActivityViewInfo> mActivityViewDisplayIdToParentMap = new SparseArray<>();
+
     /**
      * Set once the system is ready to run third party code.
      */
@@ -510,6 +541,16 @@
     EditorInfo mCurAttribute;
 
     /**
+     * A special {@link Matrix} to convert virtual screen coordinates to the IME target display
+     * coordinates.
+     *
+     * <p>Used only while the IME client is running in a virtual display inside
+     * {@link android.app.ActivityView}. {@code null} otherwise.</p>
+     */
+    @Nullable
+    private Matrix mCurActivityViewToScreenMatrix = null;
+
+    /**
      * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
      * connected to or in the process of connecting to.
      *
@@ -1409,6 +1450,7 @@
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mImeDisplayValidator = displayId -> mWindowManagerInternal.shouldShowIme(displayId);
         mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
             @Override
@@ -1883,6 +1925,15 @@
             if (cs != null) {
                 client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
                 clearClientSessionLocked(cs);
+
+                final int numItems = mActivityViewDisplayIdToParentMap.size();
+                for (int i = numItems - 1; i >= 0; --i) {
+                    final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.valueAt(i);
+                    if (info.mParentClient == cs) {
+                        mActivityViewDisplayIdToParentMap.removeAt(i);
+                    }
+                }
+
                 if (mCurClient == cs) {
                     if (mBoundToMethod) {
                         mBoundToMethod = false;
@@ -1892,6 +1943,7 @@
                         }
                     }
                     mCurClient = null;
+                    mCurActivityViewToScreenMatrix = null;
                 }
                 if (mCurFocusedWindowClient == cs) {
                     mCurFocusedWindowClient = null;
@@ -1927,6 +1979,7 @@
                     MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client));
             mCurClient.sessionRequested = false;
             mCurClient = null;
+            mCurActivityViewToScreenMatrix = null;
 
             hideInputMethodMenuLocked();
         }
@@ -1980,7 +2033,31 @@
         }
         return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
                 session.session, (session.channel != null ? session.channel.dup() : null),
-                mCurId, mCurSeq);
+                mCurId, mCurSeq, mCurActivityViewToScreenMatrix);
+    }
+
+    @Nullable
+    private Matrix getActivityViewToScreenMatrixLocked(int clientDisplayId, int imeDisplayId) {
+        if (clientDisplayId == imeDisplayId) {
+            return null;
+        }
+        int displayId = clientDisplayId;
+        Matrix matrix = null;
+        while (true) {
+            final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(displayId);
+            if (info == null) {
+                return null;
+            }
+            if (matrix == null) {
+                matrix = new Matrix(info.mMatrix);
+            } else {
+                matrix.postConcat(info.mMatrix);
+            }
+            if (info.mParentClient.selfReportedDisplayId == imeDisplayId) {
+                return matrix;
+            }
+            displayId = info.mParentClient.selfReportedDisplayId;
+        }
     }
 
     @GuardedBy("mMethodMap")
@@ -1998,7 +2075,7 @@
             // party code.
             return new InputBindResult(
                     InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
-                    null, null, mCurMethodId, mCurSeq);
+                    null, null, mCurMethodId, mCurSeq, null);
         }
 
         if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2037,7 +2114,10 @@
         if (mCurSeq <= 0) mCurSeq = 1;
         mCurClient = cs;
         mCurInputContext = inputContext;
-        if (cs.selfReportedDisplayId != displayIdToShowIme) {
+        mCurActivityViewToScreenMatrix =
+                getActivityViewToScreenMatrixLocked(cs.selfReportedDisplayId, displayIdToShowIme);
+        if (cs.selfReportedDisplayId != displayIdToShowIme
+                && mCurActivityViewToScreenMatrix == null) {
             // CursorAnchorInfo API does not work as-is for cross-display scenario.  Pretend that
             // InputConnection#requestCursorUpdates() is not implemented in the application so that
             // IMEs will always receive false from this API.
@@ -2064,7 +2144,7 @@
                     requestClientSessionLocked(cs);
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
-                            null, null, mCurId, mCurSeq);
+                            null, null, mCurId, mCurSeq, null);
                 } else if (SystemClock.uptimeMillis()
                         < (mLastBindTime+TIME_TO_RECONNECT)) {
                     // In this case we have connected to the service, but
@@ -2076,7 +2156,7 @@
                     // to see if we can get back in touch with the service.
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                            null, null, mCurId, mCurSeq);
+                            null, null, mCurId, mCurSeq, null);
                 } else {
                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
                             mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -2115,7 +2195,7 @@
             }
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                    null, null, mCurId, mCurSeq);
+                    null, null, mCurId, mCurSeq, null);
         }
         mCurIntent = null;
         Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
@@ -2960,7 +3040,7 @@
             }
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
-                    null, null, null, -1);
+                    null, null, null, -1, null);
         }
         mCurFocusedWindow = windowToken;
         mCurFocusedWindowSoftInputMode = softInputMode;
@@ -3387,6 +3467,88 @@
         return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
     }
 
+    @Override
+    public void reportActivityView(IInputMethodClient parentClient, int childDisplayId,
+            float[] matrixValues) {
+        final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(childDisplayId);
+        if (displayInfo == null) {
+            throw new IllegalArgumentException(
+                    "Cannot find display for non-existent displayId: " + childDisplayId);
+        }
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != displayInfo.ownerUid) {
+            throw new SecurityException("The caller doesn't own the display.");
+        }
+
+        synchronized (mMethodMap) {
+            final ClientState cs = mClients.get(parentClient.asBinder());
+            if (cs == null) {
+                return;
+            }
+
+            // null matrixValues means that the entry needs to be removed.
+            if (matrixValues == null) {
+                final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
+                if (info == null) {
+                    return;
+                }
+                if (info.mParentClient != cs) {
+                    throw new SecurityException("Only the owner client can clear"
+                            + " ActivityViewGeometry for display #" + childDisplayId);
+                }
+                mActivityViewDisplayIdToParentMap.remove(childDisplayId);
+                return;
+            }
+
+            ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
+            if (info != null && info.mParentClient != cs) {
+                throw new InvalidParameterException("Display #" + childDisplayId
+                        + " is already registered by " + info.mParentClient);
+            }
+            if (info == null) {
+                if (!mWindowManagerInternal.isUidAllowedOnDisplay(childDisplayId, cs.uid)) {
+                    throw new SecurityException(cs + " cannot access to display #"
+                            + childDisplayId);
+                }
+                info = new ActivityViewInfo(cs, new Matrix());
+                mActivityViewDisplayIdToParentMap.put(childDisplayId, info);
+            }
+            info.mMatrix.setValues(matrixValues);
+
+            if (mCurClient == null || mCurClient.curSession == null) {
+                return;
+            }
+
+            Matrix matrix = null;
+            int displayId = mCurClient.selfReportedDisplayId;
+            boolean needToNotify = false;
+            while (true) {
+                needToNotify |= (displayId == childDisplayId);
+                final ActivityViewInfo next = mActivityViewDisplayIdToParentMap.get(displayId);
+                if (next == null) {
+                    break;
+                }
+                if (matrix == null) {
+                    matrix = new Matrix(next.mMatrix);
+                } else {
+                    matrix.postConcat(next.mMatrix);
+                }
+                if (next.mParentClient.selfReportedDisplayId == mCurTokenDisplayId) {
+                    if (needToNotify) {
+                        final float[] values = new float[9];
+                        matrix.getValues(values);
+                        try {
+                            mCurClient.client.updateActivityViewToScreenMatrix(mCurSeq, values);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    break;
+                }
+                displayId = info.mParentClient.selfReportedDisplayId;
+            }
+        }
+    }
+
     @BinderThread
     private void notifyUserAction(@NonNull IBinder token) {
         if (DEBUG) {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index e0b8e71..3dd7304 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1619,7 +1619,7 @@
                         return new InputBindResult(
                                 InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
                                 null, null, data.mCurrentInputMethodInfo.getId(),
-                                clientInfo.mBindingSequence);
+                                clientInfo.mBindingSequence, null);
                     case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
                     case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
                         clientInfo.mBindingSequence++;
@@ -1642,7 +1642,7 @@
                                 clientInfo.mInputMethodSession,
                                 clientInfo.mWriteChannel.dup(),
                                 data.mCurrentInputMethodInfo.getId(),
-                                clientInfo.mBindingSequence);
+                                clientInfo.mBindingSequence, null);
                     case InputMethodClientState.UNREGISTERED:
                         Slog.e(TAG, "The client is already unregistered.");
                         return InputBindResult.INVALID_CLIENT;
@@ -1701,6 +1701,13 @@
 
         @BinderThread
         @Override
+        public void reportActivityView(IInputMethodClient parentClient, int childDisplayId,
+                float[] matrixValues) {
+            reportNotSupported();
+        }
+
+        @BinderThread
+        @Override
         public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
                 @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback,
                 ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 55191db..cdcb641 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -26,6 +26,7 @@
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
+import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
@@ -111,6 +112,7 @@
 import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
 import android.app.Person;
+import android.app.RemoteInput;
 import android.app.StatusBarManager;
 import android.app.UriGrantsManager;
 import android.app.admin.DeviceAdminInfo;
@@ -3754,7 +3756,7 @@
                             pkg, userId, true, granted);
 
                     getContext().sendBroadcastAsUser(new Intent(
-                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                            ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
                                     .setPackage(pkg)
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
                             UserHandle.of(userId), null);
@@ -3914,7 +3916,7 @@
                             userId, true, granted);
 
                     getContext().sendBroadcastAsUser(new Intent(
-                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                            ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
                                     .setPackage(listener.getPackageName())
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
@@ -3930,7 +3932,9 @@
         public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
                 int userId, boolean granted) {
             checkCallerIsSystemOrSystemUiOrShell();
-            mAssistants.setUserSet(userId, true);
+            for (UserInfo ui : mUm.getEnabledProfiles(userId)) {
+                mAssistants.setUserSet(ui.id, true);
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted);
@@ -4144,30 +4148,36 @@
 
     @VisibleForTesting
     protected void setNotificationAssistantAccessGrantedForUserInternal(
-            ComponentName assistant, int userId, boolean granted) {
-        if (assistant == null) {
-            ComponentName allowedAssistant = CollectionUtils.firstOrNull(
-                    mAssistants.getAllowedComponents(userId));
-            if (allowedAssistant != null) {
-                setNotificationAssistantAccessGrantedForUserInternal(
-                        allowedAssistant, userId, false);
+            ComponentName assistant, int baseUserId, boolean granted) {
+        List<UserInfo> users = mUm.getEnabledProfiles(baseUserId);
+        if (users != null) {
+            for (UserInfo user : users) {
+                int userId = user.id;
+                if (assistant == null) {
+                    ComponentName allowedAssistant = CollectionUtils.firstOrNull(
+                            mAssistants.getAllowedComponents(userId));
+                    if (allowedAssistant != null) {
+                        setNotificationAssistantAccessGrantedForUserInternal(
+                                allowedAssistant, userId, false);
+                    }
+                    continue;
+                }
+                if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(),
+                        userId, mAssistants.getRequiredPermission())) {
+                    mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
+                            userId, false, granted);
+                    mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
+                            userId, true, granted);
+
+                    getContext().sendBroadcastAsUser(
+                            new Intent(ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                                    .setPackage(assistant.getPackageName())
+                                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                            UserHandle.of(userId), null);
+
+                    handleSavePolicyFile();
+                }
             }
-            return;
-        }
-        if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), userId,
-                mAssistants.getRequiredPermission())) {
-            mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
-                    userId, false, granted);
-            mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
-                    userId, true, granted);
-
-            getContext().sendBroadcastAsUser(new Intent(
-                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
-                            .setPackage(assistant.getPackageName())
-                            .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
-                    UserHandle.of(userId), null);
-
-            handleSavePolicyFile();
         }
     }
 
@@ -4834,17 +4844,35 @@
                 : null;
         boolean isForegroundCall = CATEGORY_CALL.equals(notification.category)
                 && (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
-        // OR message style (which always has a person)
+        // OR message style (which always has a person) with any remote input
         Class<? extends Notification.Style> style = notification.getNotificationStyle();
         boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
-        boolean notificationAppropriateToBubble = isMessageStyle
+        boolean notificationAppropriateToBubble =
+                (isMessageStyle && hasValidRemoteInput(notification))
                 || (peopleList != null && !peopleList.isEmpty() && isForegroundCall);
+
         // OR something that was previously a bubble & still exists
         boolean bubbleUpdate = oldRecord != null
                 && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0;
         return canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate);
     }
 
+    private boolean hasValidRemoteInput(Notification n) {
+        // Also check for inline reply
+        Notification.Action[] actions = n.actions;
+        if (actions != null) {
+            // Get the remote inputs
+            for (int i = 0; i < actions.length; i++) {
+                Notification.Action action = actions[i];
+                RemoteInput[] inputs = action.getRemoteInputs();
+                if (inputs != null && inputs.length > 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private void doChannelWarningToast(CharSequence toastText) {
         Binder.withCleanCallingIdentity(() -> {
             final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 3a7919a..d6108b7c 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -24,6 +24,7 @@
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.IApplicationThread;
+import android.app.admin.DevicePolicyEventLogger;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -37,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.stats.devicepolicy.DevicePolicyEnums;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -69,6 +71,11 @@
 
         verifyCallingPackage(callingPackage);
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES)
+                .setStrings(new String[] {callingPackage})
+                .write();
+
         return getTargetUserProfilesUnchecked(
                 callingPackage, mInjector.getCallingUserId());
     }
@@ -85,6 +92,11 @@
 
         verifyCallingPackage(callingPackage);
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER)
+                .setStrings(new String[] {callingPackage})
+                .write();
+
         final int callerUserId = mInjector.getCallingUserId();
         final int callingUid = mInjector.getCallingUid();
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 20d47ed..a64ae9c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2923,28 +2923,50 @@
                 // Remove disable package settings for updated system apps that were
                 // removed via an OTA. If the update is no longer present, remove the
                 // app completely. Otherwise, revoke their system privileges.
-                for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
-                    PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
-                    mSettings.removeDisabledSystemPackageLPw(deletedAppName);
+                for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
+                    final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
+                    final PackageParser.Package pkg = mPackages.get(packageName);
                     final String msg;
-                    if (deletedPkg == null) {
+
+                    // remove from the disabled system list; do this first so any future
+                    // scans of this package are performed without this state
+                    mSettings.removeDisabledSystemPackageLPw(packageName);
+
+                    if (pkg == null) {
                         // should have found an update, but, we didn't; remove everything
-                        msg = "Updated system package " + deletedAppName
+                        msg = "Updated system package " + packageName
                                 + " no longer exists; removing its data";
                         // Actual deletion of code and data will be handled by later
                         // reconciliation step
                     } else {
                         // found an update; revoke system privileges
-                        msg = "Updated system package + " + deletedAppName
-                                + " no longer exists; revoking system privileges";
+                        msg = "Updated system package " + packageName
+                                + " no longer exists; rescanning package on data";
 
-                        // Don't do anything if a stub is removed from the system image. If
-                        // we were to remove the uncompressed version from the /data partition,
-                        // this is where it'd be done.
+                        // NOTE: We don't do anything special if a stub is removed from the
+                        // system image. But, if we were [like removing the uncompressed
+                        // version from the /data partition], this is where it'd be done.
 
-                        final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
-                        deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
-                        deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
+                        // remove the package from the system and re-scan it without any
+                        // special privileges
+                        removePackageLI(pkg, true);
+                        try {
+                            final File codePath = new File(pkg.applicationInfo.getCodePath());
+                            scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
+                        } catch (PackageManagerException e) {
+                            Slog.e(TAG, "Failed to parse updated, ex-system package: "
+                                    + e.getMessage());
+                        }
+                    }
+
+                    // one final check. if we still have a package setting [ie. it was
+                    // previously scanned and known to the system], but, we don't have
+                    // a package [ie. there was an error scanning it from the /data
+                    // partition], completely remove the package data.
+                    final PackageSetting ps = mSettings.mPackages.get(packageName);
+                    if (ps != null && mPackages.get(packageName) == null) {
+                        removePackageDataLIF(ps, null, null, 0, false);
+
                     }
                     logCriticalInfo(Log.WARN, msg);
                 }
@@ -14974,12 +14996,14 @@
         final int installReason;
         @Nullable
         MultiPackageInstallParams mParentInstallParams;
+        final long requiredInstalledVersionCode;
 
         InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, String installerPackageName, String volumeUuid,
                 VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
                 String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
-                PackageParser.SigningDetails signingDetails, int installReason) {
+                PackageParser.SigningDetails signingDetails, int installReason,
+                long requiredInstalledVersionCode) {
             super(user);
             this.origin = origin;
             this.move = move;
@@ -14993,6 +15017,7 @@
             this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
             this.signingDetails = signingDetails;
             this.installReason = installReason;
+            this.requiredInstalledVersionCode = requiredInstalledVersionCode;
         }
 
         InstallParams(ActiveInstallSession activeInstallSession) {
@@ -15023,6 +15048,8 @@
             whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
                     .whitelistedRestrictedPermissions;
             signingDetails = activeInstallSession.getSigningDetails();
+            requiredInstalledVersionCode = activeInstallSession.getSessionParams()
+                    .requiredInstalledVersionCode;
         }
 
         @Override
@@ -15051,6 +15078,23 @@
                     }
                 }
 
+                if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
+                    if (dataOwnerPkg == null) {
+                        Slog.w(TAG, "Required installed version code was "
+                                + requiredInstalledVersionCode
+                                + " but package is not installed");
+                        return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
+                    }
+
+                    if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
+                        Slog.w(TAG, "Required installed version code was "
+                                + requiredInstalledVersionCode
+                                + " but actual installed version is "
+                                + dataOwnerPkg.getLongVersionCode());
+                        return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
+                    }
+                }
+
                 if (dataOwnerPkg != null) {
                     if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
                             dataOwnerPkg.applicationInfo.flags)) {
@@ -15177,6 +15221,8 @@
                     loc = installLocationPolicy(pkgLite);
                     if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                         ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+                    } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
+                        ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
                     } else if (!onInt) {
                         // Override install location with flags
                         if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
@@ -18762,6 +18808,7 @@
         boolean installedStateChanged = false;
         if (deletedPs != null) {
             if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+                final SparseBooleanArray changedUsers = new SparseBooleanArray();
                 synchronized (mPackages) {
                     clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
                     clearDefaultBrowserIfNeeded(packageName);
@@ -18793,10 +18840,9 @@
                             }
                         }
                     }
+                    clearPackagePreferredActivitiesLPw(
+                            deletedPs.name, changedUsers, UserHandle.USER_ALL);
                 }
-                final SparseBooleanArray changedUsers = new SparseBooleanArray();
-                clearPackagePreferredActivitiesLPw(
-                        deletedPs.name, changedUsers, UserHandle.USER_ALL);
                 if (changedUsers.size() > 0) {
                     updateDefaultHomeNotLocked(changedUsers);
                     postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
@@ -23289,7 +23335,7 @@
                 installerPackageName, volumeUuid, null /*verificationInfo*/, user,
                 packageAbiOverride, null /*grantedPermissions*/,
                 null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
-                PackageManager.INSTALL_REASON_UNKNOWN);
+                PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST);
         params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
@@ -23915,6 +23961,18 @@
         }
 
         @Override
+        public int getTargetSdkVersionForPackage(String packageName)
+                throws RemoteException {
+            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+            ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser);
+            if (info == null) {
+                throw new RemoteException(
+                        "Couldn't get ApplicationInfo for package " + packageName);
+            }
+            return info.targetSdkVersion;
+        }
+
+        @Override
         public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
                 throws RemoteException {
             int callingUser = UserHandle.getUserId(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 803ab2d..170d085 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -44,13 +44,11 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.storage.IStorageManager;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageHelper;
 import com.android.internal.os.BackgroundThread;
 
 import java.io.File;
@@ -165,6 +163,22 @@
                 continue;
             }
             long activeVersion = activePackage.applicationInfo.longVersionCode;
+            if (session.params.requiredInstalledVersionCode
+                    != PackageManager.VERSION_CODE_HIGHEST) {
+                if (activeVersion != session.params.requiredInstalledVersionCode) {
+                    session.setStagedSessionFailed(
+                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                            "Installed version of APEX package " + newPackage.packageName
+                            + " does not match required. Active version: " + activeVersion
+                            + " required: " + session.params.requiredInstalledVersionCode);
+
+                    if (!mApexManager.abortActiveSession()) {
+                        Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
+                    }
+                    return false;
+                }
+            }
+
             boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
                     session.params.installFlags, activePackage.applicationInfo.flags);
             if (activeVersion > newPackage.versionCode && !allowsDowngrade) {
@@ -255,21 +269,6 @@
             }
         }
 
-        // Make sure we start a filesystem checkpoint on the next boot.
-        try {
-            IStorageManager storageManager = PackageHelper.getStorageManager();
-            if (storageManager.supportsCheckpoint()) {
-                storageManager.startCheckpoint(1 /* numRetries */);
-            }
-        } catch (Exception e) { // TODO(b/130190815) make a RemoteException again
-            // While StorageManager lives in the same process, the native implementation
-            // it calls through lives in 'vold'; so, this call can fail if 'vold' isn't
-            // reachable.
-            // Since we can live without filesystem checkpointing, just warn in this case
-            // and continue.
-            Slog.w(TAG, "Could not start filesystem checkpoint:", e);
-        }
-
         session.setStagedSessionReady();
         if (sessionContainsApex(session)
                 && !mApexManager.markStagedSessionReady(session.sessionId)) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 108eaf6..e025646 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -744,12 +744,6 @@
                         TelephonyManager.ACTION_EMERGENCY_ASSISTANCE, userId),
                 userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
 
-        // STOPSHIP(b/128289173): remove once EmergencyInfo app was replaced.
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
-                        "com.android.emergency.action.EMERGENCY_ASSISTANCE", userId),
-                userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
-
         // NFC Tag viewer
         Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW)
                 .setType("vnd.android.cursor.item/ndef_msg");
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index db2c742..da88ec5 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -50,6 +50,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.SparseBooleanArray;
@@ -69,11 +70,9 @@
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -107,15 +106,9 @@
     @GuardedBy("mLock")
     private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
 
-    // Package rollback data for rollback-enabled installs that have not yet
-    // been committed. Maps from sessionId to rollback data.
+    // Package rollback data for rollbacks we are in the process of enabling.
     @GuardedBy("mLock")
-    private final Map<Integer, RollbackData> mPendingRollbacks = new HashMap<>();
-
-    // Map from child session id's for enabled rollbacks to their
-    // corresponding parent session ids.
-    @GuardedBy("mLock")
-    private final Map<Integer, Integer> mChildSessions = new HashMap<>();
+    private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
 
     // The list of all rollbacks, including available and committed rollbacks.
     // This list is null until the rollback data has been loaded.
@@ -136,7 +129,6 @@
     // No need for guarding with lock because value is only accessed in handler thread.
     private long  mRelativeBootTime = calculateRelativeBootTime();
 
-
     RollbackManagerServiceImpl(Context context) {
         mContext = context;
         // Note that we're calling onStart here because this object is only constructed on
@@ -247,9 +239,20 @@
         }, filter, null, getHandler());
     }
 
+    /**
+     * This method posts a blocking call to the handler thread, so it should not be called from
+     * that same thread.
+     * @throws {@link IllegalStateException} if called from {@link #mHandlerThread}
+     */
     @Override
     public ParceledListSlice getAvailableRollbacks() {
         enforceManageRollbacks("getAvailableRollbacks");
+        if (Thread.currentThread().equals(mHandlerThread)) {
+            Log.wtf(TAG, "Calling getAvailableRollbacks from mHandlerThread "
+                    + "causes a deadlock");
+            throw new IllegalStateException("Cannot call RollbackManager#getAvailableRollbacks "
+                    + "from the handler thread!");
+        }
 
         // Wait for the handler thread to get the list of available rollbacks
         // to get the most up-to-date results. This is intended to reduce test
@@ -357,31 +360,6 @@
             return;
         }
 
-        // Verify the RollbackData is up to date with what's installed on
-        // device.
-        // TODO: We assume that between now and the time we commit the
-        // downgrade install, the currently installed package version does not
-        // change. This is not safe to assume, particularly in the case of a
-        // rollback racing with a roll-forward fix of a buggy package.
-        // Figure out how to ensure we don't commit the rollback if
-        // roll forward happens at the same time.
-        for (PackageRollbackInfo info : data.info.getPackages()) {
-            VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName());
-            if (installedVersion == null) {
-                // TODO: Test this case
-                sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
-                        "Package to roll back is not installed");
-                return;
-            }
-
-            if (!packageVersionsEqual(info.getVersionRolledBackFrom(), installedVersion)) {
-                // TODO: Test this case
-                sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
-                        "Package version to roll back not installed.");
-                return;
-            }
-        }
-
         // Get a context for the caller to use to install the downgraded
         // version of the package.
         Context context = null;
@@ -420,6 +398,8 @@
                     }
                 }
                 params.setRequestDowngrade(true);
+                params.setRequiredInstalledVersionCode(
+                        info.getVersionRolledBackFrom().getLongVersionCode());
                 if (data.isStaged()) {
                     params.setStaged();
                 }
@@ -649,8 +629,7 @@
                 // hasn't actually been updated.
                 onPackageReplaced(apexPackageName);
             }
-
-            mPackageHealthObserver.onBootCompleted();
+            mPackageHealthObserver.onBootCompletedAsync();
         });
     }
 
@@ -848,7 +827,6 @@
         // TODO: It would be nice if package manager or package installer told
         // us the session directly, rather than have to search for it
         // ourselves.
-        PackageInstaller.SessionInfo session = null;
 
         // getAllSessions only returns sessions for the associated user.
         // Create a context with the right user so we can find the matching
@@ -859,7 +837,8 @@
             return false;
         }
 
-        int parentSessionId = -1;
+        PackageInstaller.SessionInfo parentSession = null;
+        PackageInstaller.SessionInfo packageSession = null;
         PackageInstaller installer = context.getPackageManager().getPackageInstaller();
         for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
             if (info.isMultiPackage()) {
@@ -867,21 +846,21 @@
                     PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
                     if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
                         // TODO: Check we only have one matching session?
-                        parentSessionId = info.getSessionId();
-                        session = child;
+                        parentSession = info;
+                        packageSession = child;
                         break;
                     }
                 }
             } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
                 // TODO: Check we only have one matching session?
-                parentSessionId = info.getSessionId();
-                session = info;
+                parentSession = info;
+                packageSession = info;
                 break;
             }
         }
 
-        if (session == null) {
-            Log.e(TAG, "Unable to find session id for enabled rollback.");
+        if (parentSession == null || packageSession == null) {
+            Log.e(TAG, "Unable to find session for enabled rollback.");
             return false;
         }
 
@@ -893,7 +872,7 @@
             ensureRollbackDataLoadedLocked();
             for (int i = 0; i < mRollbacks.size(); ++i) {
                 RollbackData data = mRollbacks.get(i);
-                if (data.apkSessionId == parentSessionId) {
+                if (data.apkSessionId == parentSession.getSessionId()) {
                     rd = data;
                     break;
                 }
@@ -906,7 +885,7 @@
             PackageParser.PackageLite newPackage = null;
             try {
                 newPackage = PackageParser.parsePackageLite(
-                        new File(session.resolvedBaseCodePath), 0);
+                        new File(packageSession.resolvedBaseCodePath), 0);
             } catch (PackageParser.PackageParserException e) {
                 Log.e(TAG, "Unable to parse new package", e);
                 return false;
@@ -924,16 +903,32 @@
             return false;
         }
 
-        return enableRollbackForSession(session, installedUsers, true);
+        NewRollback newRollback;
+        synchronized (mLock) {
+            // See if we already have a NewRollback that contains this package
+            // session. If not, create a NewRollback for the parent session
+            // that we will use for all the packages in the session.
+            newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
+            if (newRollback == null) {
+                newRollback = createNewRollbackLocked(parentSession);
+                mNewRollbacks.add(newRollback);
+            }
+        }
+
+        return enableRollbackForPackageSession(newRollback.data, packageSession,
+                installedUsers, /* snapshotUserData*/ true);
     }
 
     /**
      * Do code and userdata backups to enable rollback of the given session.
      * In case of multiPackage sessions, <code>session</code> should be one of
      * the child sessions, not the parent session.
+     *
+     * @return true on success, false on failure.
      */
-    private boolean enableRollbackForSession(PackageInstaller.SessionInfo session,
-            @NonNull int[] installedUsers, boolean snapshotUserData) {
+    private boolean enableRollbackForPackageSession(RollbackData data,
+            PackageInstaller.SessionInfo session, @NonNull int[] installedUsers,
+            boolean snapshotUserData) {
         // TODO: Don't attempt to enable rollback for split installs.
         final int installFlags = session.installFlags;
         if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
@@ -988,41 +983,14 @@
         VersionedPackage installedVersion = new VersionedPackage(packageName,
                 pkgInfo.getLongVersionCode());
 
-        PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion,
+        PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
+                newVersion, installedVersion,
                 new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
                 isApex, IntArray.wrap(installedUsers),
                 new SparseLongArray() /* ceSnapshotInodes */);
-        RollbackData data;
-        try {
-            int childSessionId = session.getSessionId();
-            int parentSessionId = session.getParentSessionId();
-            if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) {
-                parentSessionId = childSessionId;
-            }
-
-            synchronized (mLock) {
-                // TODO: no need to add to mChildSessions if childSessionId is
-                // the same as parentSessionId.
-                mChildSessions.put(childSessionId, parentSessionId);
-                data = mPendingRollbacks.get(parentSessionId);
-                if (data == null) {
-                    int rollbackId = allocateRollbackIdLocked();
-                    if (session.isStaged()) {
-                        data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
-                    } else {
-                        data = mRollbackStore.createNonStagedRollback(rollbackId);
-                    }
-                    mPendingRollbacks.put(parentSessionId, data);
-                }
-                data.info.getPackages().add(info);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to create rollback for " + packageName, e);
-            return false;
-        }
 
         if (snapshotUserData && !isApex) {
-            mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info);
+            mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), packageRollbackInfo);
         }
 
         try {
@@ -1037,6 +1005,10 @@
             Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
             return false;
         }
+
+        synchronized (mLock) {
+            data.info.getPackages().add(packageRollbackInfo);
+        }
         return true;
     }
 
@@ -1107,8 +1079,14 @@
                 return;
             }
 
+            NewRollback newRollback;
+            synchronized (mLock) {
+                newRollback = createNewRollbackLocked(session);
+            }
+
             if (!session.isMultiPackage()) {
-                if (!enableRollbackForSession(session, new int[0], false)) {
+                if (!enableRollbackForPackageSession(newRollback.data, session,
+                            new int[0], /* snapshotUserData */ false)) {
                     Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
                     result.offer(false);
                     return;
@@ -1122,7 +1100,8 @@
                         result.offer(false);
                         return;
                     }
-                    if (!enableRollbackForSession(childSession, new int[0], false)) {
+                    if (!enableRollbackForPackageSession(newRollback.data, childSession,
+                                new int[0], /* snapshotUserData */ false)) {
                         Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
                         result.offer(false);
                         return;
@@ -1130,8 +1109,7 @@
                 }
             }
 
-            completeEnableRollback(sessionId, true);
-            result.offer(true);
+            result.offer(completeEnableRollback(newRollback, true) != null);
         });
 
         try {
@@ -1262,9 +1240,19 @@
 
         @Override
         public void onFinished(int sessionId, boolean success) {
-            RollbackData rollback = completeEnableRollback(sessionId, success);
-            if (rollback != null && !rollback.isStaged()) {
-                makeRollbackAvailable(rollback);
+            NewRollback newRollback;
+            synchronized (mLock) {
+                newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+                if (newRollback != null) {
+                    mNewRollbacks.remove(newRollback);
+                }
+            }
+
+            if (newRollback != null) {
+                RollbackData rollback = completeEnableRollback(newRollback, success);
+                if (rollback != null && !rollback.isStaged()) {
+                    makeRollbackAvailable(rollback);
+                }
             }
         }
     }
@@ -1274,25 +1262,22 @@
      * This should be called after rollback has been enabled for all packages
      * in the rollback. It does not make the rollback available yet.
      *
-     * @return the rollback data for a successfully enable-completed rollback.
+     * @return the rollback data for a successfully enable-completed rollback,
+     * or null on error.
      */
-    private RollbackData completeEnableRollback(int sessionId, boolean success) {
-        RollbackData data = null;
-        synchronized (mLock) {
-            Integer parentSessionId = mChildSessions.remove(sessionId);
-            if (parentSessionId != null) {
-                sessionId = parentSessionId;
-            }
-
-            data = mPendingRollbacks.remove(sessionId);
-        }
-
-        if (data == null) {
+    private RollbackData completeEnableRollback(NewRollback newRollback, boolean success) {
+        RollbackData data = newRollback.data;
+        if (!success) {
+            // The install session was aborted, clean up the pending install.
+            deleteRollback(data);
             return null;
         }
 
-        if (!success) {
-            // The install session was aborted, clean up the pending install.
+        // It's safe to access data.info outside a synchronized block because
+        // this is running on the handler thread and all changes to the
+        // data.info occur on the handler thread.
+        if (data.info.getPackages().size() != newRollback.packageSessionIds.length) {
+            Log.e(TAG, "Failed to enable rollback for all packages in session.");
             deleteRollback(data);
             return null;
         }
@@ -1376,7 +1361,7 @@
     }
 
     @GuardedBy("mLock")
-    private int allocateRollbackIdLocked() throws IOException {
+    private int allocateRollbackIdLocked() {
         int n = 0;
         int rollbackId;
         do {
@@ -1387,7 +1372,7 @@
             }
         } while (n++ < 32);
 
-        throw new IOException("Failed to allocate rollback ID");
+        throw new IllegalStateException("Failed to allocate rollback ID");
     }
 
     private void deleteRollback(RollbackData rollbackData) {
@@ -1462,4 +1447,60 @@
                     + Manifest.permission.TEST_MANAGE_ROLLBACKS);
         }
     }
+
+    private static class NewRollback {
+        public final RollbackData data;
+
+        /**
+         * Session ids for all packages in the install.
+         * For multi-package sessions, this is the list of child session ids.
+         * For normal sessions, this list is a single element with the normal
+         * session id.
+         */
+        public final int[] packageSessionIds;
+
+        NewRollback(RollbackData data, int[] packageSessionIds) {
+            this.data = data;
+            this.packageSessionIds = packageSessionIds;
+        }
+    }
+
+    NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
+        int rollbackId = allocateRollbackIdLocked();
+        final RollbackData data;
+        int parentSessionId = parentSession.getSessionId();
+
+        if (parentSession.isStaged()) {
+            data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
+        } else {
+            data = mRollbackStore.createNonStagedRollback(rollbackId);
+        }
+
+        int[] packageSessionIds;
+        if (parentSession.isMultiPackage()) {
+            packageSessionIds = parentSession.getChildSessionIds();
+        } else {
+            packageSessionIds = new int[]{parentSessionId};
+        }
+
+        return new NewRollback(data, packageSessionIds);
+    }
+
+    /**
+     * Returns the NewRollback associated with the given package session.
+     * Returns null if no NewRollback is found for the given package
+     * session.
+     */
+    NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
+        // We expect mNewRollbacks to be a very small list; linear search
+        // should be plenty fast.
+        for (NewRollback newRollbackData : mNewRollbacks) {
+            for (int id : newRollbackData.packageSessionIds) {
+                if (id == packageSessionId) {
+                    return newRollbackData;
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 748a661..bcef66c 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -32,6 +32,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.PowerManager;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.StatsLog;
@@ -49,9 +50,12 @@
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
- * {@code PackageHealthObserver} for {@code RollbackManagerService}.
+ * {@link PackageHealthObserver} for {@link RollbackManagerService}.
+ * This class monitors crashes and triggers RollbackManager rollback accordingly.
+ * It also monitors native crashes for some short while after boot.
  *
  * @hide
  */
@@ -59,12 +63,21 @@
     private static final String TAG = "RollbackPackageHealthObserver";
     private static final String NAME = "rollback-observer";
     private static final int INVALID_ROLLBACK_ID = -1;
+    // TODO: make the following values configurable via DeviceConfig
+    private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS =
+            TimeUnit.SECONDS.toMillis(30);
+    private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10;
+
     private final Context mContext;
     private final Handler mHandler;
     private final File mLastStagedRollbackIdFile;
+    // this field is initialized in the c'tor and then only accessed from mHandler thread, so
+    // no need to guard with a lock
+    private long mNumberOfNativeCrashPollsRemaining;
 
     RollbackPackageHealthObserver(Context context) {
         mContext = context;
+        mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
         HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
         handlerThread.start();
         mHandler = handlerThread.getThreadHandler();
@@ -76,8 +89,6 @@
 
     @Override
     public int onHealthCheckFailed(VersionedPackage failedPackage) {
-        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
-
         if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
                 == null) {
             // Don't handle the notification, no rollbacks available for the package
@@ -145,16 +156,29 @@
         PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
     }
 
-    /** Verifies the rollback state after a reboot. */
-    public void onBootCompleted() {
+    /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot
+     * to check for native crashes and mitigate them if needed.
+     */
+    public void onBootCompletedAsync() {
+        mHandler.post(()->onBootCompleted());
+    }
+
+    private void onBootCompleted() {
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+        String moduleMetadataPackageName = getModuleMetadataPackageName();
+        VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage();
+
+        if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) {
+            scheduleCheckAndMitigateNativeCrashes();
+        }
+
         int rollbackId = popLastStagedRollbackId();
         if (rollbackId == INVALID_ROLLBACK_ID) {
             // No staged rollback before reboot
             return;
         }
 
-        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
-        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
         RollbackInfo rollback = null;
         for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) {
             if (rollbackId == info.getRollbackId()) {
@@ -168,14 +192,12 @@
             return;
         }
 
-        String moduleMetadataPackageName = getModuleMetadataPackageName();
-
         // Use the version of the metadata package that was installed before
         // we rolled back for logging purposes.
-        VersionedPackage moduleMetadataPackage = null;
+        VersionedPackage oldModuleMetadataPackage = null;
         for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
             if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) {
-                moduleMetadataPackage = packageRollback.getVersionRolledBackFrom();
+                oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom();
                 break;
             }
         }
@@ -187,12 +209,12 @@
             return;
         }
         if (sessionInfo.isStagedSessionApplied()) {
-            logEvent(moduleMetadataPackage,
+            logEvent(oldModuleMetadataPackage,
                     StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS);
         } else if (sessionInfo.isStagedSessionReady()) {
             // TODO: What do for staged session ready but not applied
         } else {
-            logEvent(moduleMetadataPackage,
+            logEvent(oldModuleMetadataPackage,
                     StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE);
         }
     }
@@ -320,4 +342,34 @@
                     moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode());
         }
     }
+
+    /**
+     * This method should be only called on mHandler thread, since it modifies
+     * {@link #mNumberOfNativeCrashPollsRemaining} and we want to keep this class lock free.
+     */
+    private void checkAndMitigateNativeCrashes() {
+        mNumberOfNativeCrashPollsRemaining--;
+        // Check if native watchdog reported a crash
+        if ("1".equals(SystemProperties.get("ro.init.updatable_crashing"))) {
+            execute(getModuleMetadataPackage());
+            // we stop polling after an attempt to execute rollback, regardless of whether the
+            // attempt succeeds or not
+        } else {
+            if (mNumberOfNativeCrashPollsRemaining > 0) {
+                mHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
+                        NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
+            }
+        }
+    }
+
+    /**
+     * Since this method can eventually trigger a RollbackManager rollback, it should be called
+     * only once boot has completed {@code onBootCompleted} and not earlier, because the install
+     * session must be entirely completed before we try to rollback.
+     */
+    private void scheduleCheckAndMitigateNativeCrashes() {
+        Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check "
+                + "and mitigate native crashes");
+        mHandler.post(()->checkAndMitigateNativeCrashes());
+    }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 2cfa465..8a26368c 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -194,7 +194,7 @@
      * Creates a new RollbackData instance for a non-staged rollback with
      * backupDir assigned.
      */
-    RollbackData createNonStagedRollback(int rollbackId) throws IOException {
+    RollbackData createNonStagedRollback(int rollbackId) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
         return new RollbackData(rollbackId, backupDir, -1);
     }
@@ -203,8 +203,7 @@
      * Creates a new RollbackData instance for a staged rollback with
      * backupDir assigned.
      */
-    RollbackData createStagedRollback(int rollbackId, int stagedSessionId)
-            throws IOException {
+    RollbackData createStagedRollback(int rollbackId, int stagedSessionId) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
         return new RollbackData(rollbackId, backupDir, stagedSessionId);
     }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b9b5aae..7734d6b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -77,7 +77,8 @@
     void onCameraLaunchGestureDetected(int source);
     void topAppWindowChanged(int displayId, boolean menuVisible);
     void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenBounds, Rect dockedBounds, String cause);
+            int mask, Rect fullscreenBounds, Rect dockedBounds, boolean isNavbarColorManagedByIme,
+            String cause);
     void toggleSplitScreen();
     void appTransitionFinished(int displayId);
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index a5656c3..b2d7084 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -262,9 +262,10 @@
         @Override
         public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
                 int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
-                String cause) {
+                boolean isNavbarColorManagedByIme, String cause) {
             StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
-                    dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
+                    dockedStackVis, mask, fullscreenBounds, dockedBounds, isNavbarColorManagedByIme,
+                    cause);
         }
 
         @Override
@@ -872,11 +873,13 @@
     public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) {
         final UiState state = getUiState(displayId);
         setSystemUiVisibility(displayId, vis, 0, 0, mask,
-                state.mFullscreenStackBounds, state.mDockedStackBounds, cause);
+                state.mFullscreenStackBounds, state.mDockedStackBounds,
+                state.mNavbarColorManagedByIme, cause);
     }
 
     private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) {
+            int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
+            boolean isNavbarColorManagedByIme, String cause) {
         // also allows calls from window manager which is in this process.
         enforceStatusBarService();
 
@@ -884,7 +887,7 @@
 
         synchronized (mLock) {
             updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask,
-                    fullscreenBounds, dockedBounds);
+                    fullscreenBounds, dockedBounds, isNavbarColorManagedByIme);
             disableLocked(
                     displayId,
                     mCurrentUserId,
@@ -896,17 +899,19 @@
 
     private void updateUiVisibilityLocked(final int displayId, final int vis,
             final int fullscreenStackVis, final int dockedStackVis, final int mask,
-            final Rect fullscreenBounds, final Rect dockedBounds) {
+            final Rect fullscreenBounds, final Rect dockedBounds,
+            final boolean isNavbarColorManagedByIme) {
         final UiState state = getUiState(displayId);
         if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis,
-                fullscreenBounds, dockedBounds)) {
+                fullscreenBounds, dockedBounds, isNavbarColorManagedByIme)) {
             state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds,
-                    dockedBounds);
+                    dockedBounds, isNavbarColorManagedByIme);
             mHandler.post(() -> {
                 if (mBar != null) {
                     try {
                         mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
-                                dockedStackVis, mask, fullscreenBounds, dockedBounds);
+                                dockedStackVis, mask, fullscreenBounds, dockedBounds,
+                                isNavbarColorManagedByIme);
                     } catch (RemoteException ex) {
                         Log.w(TAG, "Can not get StatusBar!");
                     }
@@ -945,6 +950,7 @@
         private int mImeBackDisposition = 0;
         private boolean mShowImeSwitcher = false;
         private IBinder mImeToken = null;
+        private boolean mNavbarColorManagedByIme = false;
 
         private int getDisabled1() {
             return mDisabled1;
@@ -972,21 +978,25 @@
         }
 
         private void setSystemUiState(final int vis, final int fullscreenStackVis,
-                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds,
+                final boolean navbarColorManagedByIme) {
             mSystemUiVisibility = vis;
             mFullscreenStackSysUiVisibility = fullscreenStackVis;
             mDockedStackSysUiVisibility = dockedStackVis;
             mFullscreenStackBounds.set(fullscreenBounds);
             mDockedStackBounds.set(dockedBounds);
+            mNavbarColorManagedByIme = navbarColorManagedByIme;
         }
 
         private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis,
-                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds,
+                final boolean navbarColorManagedByIme) {
             return mSystemUiVisibility == vis
                 && mFullscreenStackSysUiVisibility == fullscreenStackVis
                 && mDockedStackSysUiVisibility == dockedStackVis
                 && mFullscreenStackBounds.equals(fullscreenBounds)
-                && mDockedStackBounds.equals(dockedBounds);
+                && mDockedStackBounds.equals(dockedBounds)
+                && mNavbarColorManagedByIme == navbarColorManagedByIme;
         }
 
         private void setImeWindowState(final int vis, final int backDisposition,
@@ -1051,7 +1061,8 @@
                     state.mImeBackDisposition, state.mShowImeSwitcher,
                     gatherDisableActionsLocked(mCurrentUserId, 2),
                     state.mFullscreenStackSysUiVisibility, state.mDockedStackSysUiVisibility,
-                    state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds);
+                    state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds,
+                    state.mNavbarColorManagedByIme);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6ce42ec..b6a5be8 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1108,6 +1108,7 @@
                 // the window manager is still looking for where to put it.
                 // We will do the work when we get a focus change callback.
                 // TODO(b/112273690): Support multiple displays
+                // TODO(b/129098348): Support embedded displays
                 if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) {
                     return;
                 }
@@ -1400,7 +1401,28 @@
                 if (w.isVisibleLw()) {
                     outWindows.put(mTempLayer++, w);
                 }
-            }, false /* traverseTopToBottom */ );
+            }, false /* traverseTopToBottom */);
+            mService.mRoot.forAllWindows(w -> {
+                final WindowState win = findRootDisplayParentWindow(w);
+                if (win != null && win.getDisplayContent().isDefaultDisplay && w.isVisibleLw()) {
+                    // TODO(b/129098348): insert windows on child displays into outWindows based on
+                    // root-display-parent window.
+                    outWindows.put(mTempLayer++, w);
+                }
+            }, false /* traverseTopToBottom */);
+        }
+
+        private WindowState findRootDisplayParentWindow(WindowState win) {
+            WindowState displayParentWindow = win.getDisplayContent().getParentWindow();
+            if (displayParentWindow == null) {
+                return null;
+            }
+            WindowState candidate = displayParentWindow;
+            while (candidate != null) {
+                displayParentWindow = candidate;
+                candidate = displayParentWindow.getDisplayContent().getParentWindow();
+            }
+            return displayParentWindow;
         }
 
         private class MyHandler extends Handler {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0891ba4..9decb58 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -965,16 +965,13 @@
      * @param info
      * */
     private void startTraces(WindowingModeTransitionInfo info) {
-        if (info == null) {
+        if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER) || info == null
+                || info.launchTraceActive) {
             return;
         }
-        int transitionType = getTransitionType(info);
-        if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH
-                || transitionType == TYPE_TRANSITION_COLD_LAUNCH) {
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
-                    + info.launchedActivity.packageName, 0);
-            info.launchTraceActive = true;
-        }
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
+                + info.launchedActivity.packageName, 0);
+        info.launchTraceActive = true;
     }
 
     private void stopLaunchTrace(WindowingModeTransitionInfo info) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4278860..802683a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -916,8 +916,12 @@
         }
     }
 
+    static boolean isResolverActivity(String className) {
+        return ResolverActivity.class.getName().equals(className);
+    }
+
     boolean isResolverActivity() {
-        return ResolverActivity.class.getName().equals(mActivityComponent.getClassName());
+        return isResolverActivity(mActivityComponent.getClassName());
     }
 
     boolean isResolverOrChildActivity() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8a834c8..fe99fd2 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1822,7 +1822,8 @@
                     prev.setDeferHidingClient(false);
                     // If we were visible then resumeTopActivities will release resources before
                     // stopping.
-                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */);
+                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */,
+                            "completePauseLocked");
                 }
             } else {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
@@ -1883,8 +1884,11 @@
         mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
     }
 
-    private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
+    private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed,
+            String reason) {
         if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+            EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId,
+                    System.identityHashCode(r), r.shortComponentName, reason);
             mStackSupervisor.mStoppingActivities.add(r);
         }
 
@@ -2433,7 +2437,7 @@
                 case PAUSING:
                 case PAUSED:
                     addToStopping(r, true /* scheduleIdle */,
-                            canEnterPictureInPicture /* idleDelayed */);
+                            canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
                     break;
 
                 default:
@@ -3051,21 +3055,7 @@
         ActivityOptions.abort(options);
         if (DEBUG_STATES) Slog.d(TAG_STATES,
                 "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
-        if (isActivityTypeHome()) {
-            // resumeTopActivityUncheckedLocked has been prevented to run recursively. Post a
-            // runnable to resume home since we are currently in the process of resuming top
-            // activity in home stack.
-            // See {@link #mInResumeTopActivity}.
-            mService.mH.post(
-                    () -> {
-                        synchronized (mService.mGlobalLock) {
-                            mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
-                        }
-                    });
-            return true;
-        } else {
-            return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
-        }
+        return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
     }
 
     /** Returns the position the input task should be placed in this stack. */
@@ -4098,7 +4088,8 @@
         if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
                 && next != null && !next.nowVisible && !isFloating) {
             if (!mStackSupervisor.mStoppingActivities.contains(r)) {
-                addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
+                addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */,
+                        "finishCurrentActivityLocked");
             }
             if (DEBUG_STATES) Slog.v(TAG_STATES,
                     "Moving to STOPPING: "+ r + " (finish requested)");
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 7eac07c..919141c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -171,7 +171,12 @@
     void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+        if (!ActivityRecord.isResolverActivity(aInfo.name)) {
+            // The resolver activity shouldn't be put in home stack because when the foreground is
+            // standard type activity, the resolver activity should be put on the top of current
+            // foreground instead of bring home stack to front.
+            options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+        }
         options.setLaunchDisplayId(displayId);
         mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                 .setOutActivity(tmpOutRecord)
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4ef8753..e7e34bb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -160,6 +160,10 @@
     private int mCallingUid;
     private ActivityOptions mOptions;
 
+    // If it is true, background activity can only be started in an existing task that contains
+    // an activity with same uid.
+    private boolean mRestrictedBgActivity;
+
     private int mLaunchMode;
     private boolean mLaunchTaskBehind;
     private int mLaunchFlags;
@@ -455,6 +459,7 @@
         mIntent = starter.mIntent;
         mCallingUid = starter.mCallingUid;
         mOptions = starter.mOptions;
+        mRestrictedBgActivity = starter.mRestrictedBgActivity;
 
         mLaunchTaskBehind = starter.mLaunchTaskBehind;
         mLaunchFlags = starter.mLaunchFlags;
@@ -551,7 +556,8 @@
             mLastStartActivityTimeMs = System.currentTimeMillis();
             mLastStartActivityRecord[0] = r;
             mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor,
-                    startFlags, doResume, options, inTask, mLastStartActivityRecord);
+                    startFlags, doResume, options, inTask, mLastStartActivityRecord,
+                    false /* restrictedBgActivity */);
             mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
                     mLastStartActivityRecord[0]);
             return mLastStartActivityResult;
@@ -760,22 +766,17 @@
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
 
-        boolean abortBackgroundStart = false;
+        boolean restrictedBgActivity = false;
         if (!abort) {
             try {
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                         "shouldAbortBackgroundActivityStart");
-                abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid,
+                restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                         callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                         originatingPendingIntent, allowBackgroundActivityStart, intent);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             }
-            abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled());
-            // TODO: remove this toast after feature development is done
-            if (abortBackgroundStart) {
-                showBackgroundActivityBlockedToast(abort, callingPackage);
-            }
         }
 
         // Merge the two options bundles, while realCallerOptions takes precedence.
@@ -918,8 +919,10 @@
                 || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                     realCallingPid, realCallingUid, "Activity start")) {
-                mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
-                        sourceRecord, startFlags, stack, callerApp));
+                if (!restrictedBgActivity) {
+                    mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
+                            sourceRecord, startFlags, stack, callerApp));
+                }
                 ActivityOptions.abort(checkedOptions);
                 return ActivityManager.START_SWITCHES_CANCELED;
             }
@@ -929,7 +932,7 @@
         mController.doPendingActivityLaunches(false);
 
         final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
-                true /* doResume */, checkedOptions, inTask, outActivity);
+                true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
         mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
         return res;
     }
@@ -1038,6 +1041,12 @@
                 }
             }
         }
+        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+            Slog.w(TAG, "Background activity start for " + callingPackage
+                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+            return false;
+        }
         // anything that has fallen through would currently be aborted
         Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                 + "; callingUid: " + callingUid
@@ -1395,13 +1404,13 @@
     private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                 IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                 int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-                ActivityRecord[] outActivity) {
+                ActivityRecord[] outActivity, boolean restrictedBgActivity) {
         int result = START_CANCELED;
         final ActivityStack startedActivityStack;
         try {
             mService.mWindowManager.deferSurfaceLayout();
             result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
-                    startFlags, doResume, options, inTask, outActivity);
+                    startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
         } finally {
             final ActivityStack currentStack = r.getActivityStack();
             startedActivityStack = currentStack != null ? currentStack : mTargetStack;
@@ -1437,14 +1446,40 @@
         return result;
     }
 
+    /**
+     * Return true if background activity is really aborted.
+     *
+     * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
+     */
+    private boolean handleBackgroundActivityAbort(ActivityRecord r) {
+        // TODO(b/131747138): Remove toast and refactor related code in Q release.
+        boolean abort = !mService.isBackgroundActivityStartsEnabled();
+        showBackgroundActivityBlockedToast(abort, r.launchedFromPackage);
+        if (!abort) {
+            return false;
+        }
+        ActivityRecord resultRecord = r.resultTo;
+        String resultWho = r.resultWho;
+        int requestCode = r.requestCode;
+        if (resultRecord != null) {
+            ActivityStack resultStack = resultRecord.getActivityStack();
+            resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
+                    RESULT_CANCELED, null);
+        }
+        // We pretend to the caller that it was really started to make it backward compatible, but
+        // they will just get a cancel result.
+        ActivityOptions.abort(r.pendingOptions);
+        return true;
+    }
+
     // Note: This method should only be called from {@link startActivity}.
     private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-            ActivityRecord[] outActivity) {
-
+            ActivityRecord[] outActivity, boolean restrictedBgActivity) {
         setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
-                voiceInteractor);
+                voiceInteractor, restrictedBgActivity);
+
         final int preferredWindowingMode = mLaunchParams.mWindowingMode;
 
         computeLaunchingTaskFlags();
@@ -1652,7 +1687,7 @@
         } else {
             // This not being started from an existing activity, and not part of a new task...
             // just put it in the top task, though these days this case should never happen.
-            setTaskToCurrentTopOrCreateNewTask();
+            result = setTaskToCurrentTopOrCreateNewTask();
         }
         if (result != START_SUCCESS) {
             return result;
@@ -1725,6 +1760,7 @@
         mIntent = null;
         mCallingUid = -1;
         mOptions = null;
+        mRestrictedBgActivity = false;
 
         mLaunchTaskBehind = false;
         mLaunchFlags = 0;
@@ -1764,7 +1800,8 @@
 
     private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
             boolean doResume, int startFlags, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            boolean restrictedBgActivity) {
         reset(false /* clearRequest */);
 
         mStartActivity = r;
@@ -1774,6 +1811,7 @@
         mSourceRecord = sourceRecord;
         mVoiceSession = voiceSession;
         mVoiceInteractor = voiceInteractor;
+        mRestrictedBgActivity = restrictedBgActivity;
 
         mLaunchParams.reset();
 
@@ -1874,6 +1912,11 @@
         }
 
         mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
+
+        if (restrictedBgActivity) {
+            mAvoidMoveToFront = true;
+            mDoResume = false;
+        }
     }
 
     private void sendNewTaskResultRequestIfNeeded() {
@@ -2075,8 +2118,8 @@
             final ActivityRecord curTop = (focusStack == null)
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
             final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null;
-            differentTopTask = topTask != null
-                    && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask());
+            differentTopTask = topTask != intentActivity.getTaskRecord()
+                    || (focusStack != null && topTask != focusStack.topTask());
         } else {
             // The existing task should always be different from those in other displays.
             differentTopTask = true;
@@ -2271,6 +2314,9 @@
         // isLockTaskModeViolation fails below.
 
         if (mReuseTask == null) {
+            if (mRestrictedBgActivity && handleBackgroundActivityAbort(mStartActivity)) {
+                return START_ABORTED;
+            }
             final TaskRecord task = mTargetStack.createTaskRecord(
                     mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
                     mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
@@ -2283,6 +2329,11 @@
             if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
                     + " in new task " + mStartActivity.getTaskRecord());
         } else {
+            if (mRestrictedBgActivity && !mReuseTask.containsAppUid(mCallingUid)) {
+                if (handleBackgroundActivityAbort(mStartActivity)) {
+                    return START_ABORTED;
+                }
+            }
             addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
         }
 
@@ -2322,6 +2373,12 @@
 
         final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
         final ActivityStack sourceStack = mSourceRecord.getActivityStack();
+        if (mRestrictedBgActivity && !sourceTask.containsAppUid(mCallingUid)) {
+            if (handleBackgroundActivityAbort(mStartActivity)) {
+                return START_ABORTED;
+            }
+            return START_ABORTED;
+        }
         // We only want to allow changing stack in two cases:
         // 1. If the target task is not the top one. Otherwise we would move the launching task to
         //    the other side, rather than show two side by side.
@@ -2483,20 +2540,33 @@
         }
     }
 
-    private void setTaskToCurrentTopOrCreateNewTask() {
+    private int setTaskToCurrentTopOrCreateNewTask() {
         mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
         if (mDoResume) {
             mTargetStack.moveToFront("addingToTopTask");
         }
         final ActivityRecord prev = mTargetStack.getTopActivity();
+        if (mRestrictedBgActivity && prev == null) {
+            if (handleBackgroundActivityAbort(mStartActivity)) {
+                return START_ABORTED;
+            }
+            return START_ABORTED;
+        }
         final TaskRecord task = (prev != null)
                 ? prev.getTaskRecord() : mTargetStack.createTaskRecord(
                 mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info,
                 mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
+        if (mRestrictedBgActivity && !task.containsAppUid(mCallingUid)) {
+            if (handleBackgroundActivityAbort(mStartActivity)) {
+                return START_ABORTED;
+            }
+            return START_ABORTED;
+        }
         addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
         mTargetStack.positionChildWindowContainerAtTop(task);
         if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
                 + " in new guessed " + mStartActivity.getTaskRecord());
+        return START_SUCCESS;
     }
 
     private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7bc9600..4a6aa33 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -108,12 +108,10 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
 import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -136,6 +134,7 @@
 import android.app.ActivityThread;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
+import android.app.AppOpsManager;
 import android.app.Dialog;
 import android.app.IActivityController;
 import android.app.IActivityTaskManager;
@@ -216,7 +215,6 @@
 import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -875,6 +873,16 @@
         return getUserManager().hasUserRestriction(restriction, userId);
     }
 
+    boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
+        final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                callingUid, callingPackage);
+        if (mode == AppOpsManager.MODE_DEFAULT) {
+            return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
+                    == PERMISSION_GRANTED;
+        }
+        return mode == AppOpsManager.MODE_ALLOWED;
+    }
+
     protected RecentTasks createRecentTasks() {
         return new RecentTasks(this, mStackSupervisor);
     }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 75e34fb..d4c4e6a 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -478,7 +478,7 @@
                     outReasons.put(windowingMode,  APP_TRANSITION_WINDOWS_DRAWN);
                 } else {
                     outReasons.put(windowingMode,
-                            wtoken.startingData instanceof SplashScreenStartingData
+                            wtoken.mStartingData instanceof SplashScreenStartingData
                                     ? APP_TRANSITION_SPLASH_SCREEN
                                     : APP_TRANSITION_SNAPSHOT);
                 }
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 6318486..4d972dc 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -129,7 +129,7 @@
         mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
                 new WindowAnimationSpec(anim, position,
                         mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(),
-                        mAppToken.getWindowCornerRadiusForAnimation()),
+                        mAppToken.getDisplayContent().getWindowCornerRadius()),
                 mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a3cef7f..5be8e14 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -78,6 +78,7 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 
@@ -203,7 +204,7 @@
     boolean removed;
 
     // Information about an application starting window if displayed.
-    StartingData startingData;
+    StartingData mStartingData;
     WindowState startingWindow;
     StartingSurface startingSurface;
     boolean startingDisplayed;
@@ -385,8 +386,8 @@
             // it from behind the starting window, so there is no need for it to also be doing its
             // own stuff.
             win.cancelAnimation();
-            removeStartingWindow();
         }
+        removeStartingWindow();
         updateReportedVisibilityLocked();
     }
 
@@ -638,8 +639,8 @@
                 // If we are being set visible, and the starting window is not yet displayed,
                 // then make sure it doesn't get displayed.
                 if (startingWindow != null && !startingWindow.isDrawnLw()) {
-                    startingWindow.mPolicyVisibility = false;
-                    startingWindow.mPolicyVisibilityAfterAnim = false;
+                    startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+                    startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
                 }
 
                 // We are becoming visible, so better freeze the screen with the windows that are
@@ -874,7 +875,7 @@
         if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                 + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
 
-        if (startingData != null) {
+        if (mStartingData != null) {
             removeStartingWindow();
         }
 
@@ -1053,7 +1054,7 @@
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData");
-            startingData = null;
+            mStartingData = null;
             if (mHiddenSetFromTransferredStartingWindow) {
                 // We set the hidden state to false for the token from a transferred starting window.
                 // We now reset it back to true since the starting window was the last window in the
@@ -1486,13 +1487,13 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 // Transfer the starting window over to the new token.
-                startingData = fromToken.startingData;
+                mStartingData = fromToken.mStartingData;
                 startingSurface = fromToken.startingSurface;
                 startingDisplayed = fromToken.startingDisplayed;
                 fromToken.startingDisplayed = false;
                 startingWindow = tStartingWindow;
                 reportedVisible = fromToken.reportedVisible;
-                fromToken.startingData = null;
+                fromToken.mStartingData = null;
                 fromToken.startingSurface = null;
                 fromToken.startingWindow = null;
                 fromToken.startingMoved = true;
@@ -1538,13 +1539,13 @@
                 Binder.restoreCallingIdentity(origId);
             }
             return true;
-        } else if (fromToken.startingData != null) {
+        } else if (fromToken.mStartingData != null) {
             // The previous app was getting ready to show a
             // starting window, but hasn't yet done so.  Steal it!
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
                     "Moving pending starting from " + fromToken + " to " + this);
-            startingData = fromToken.startingData;
-            fromToken.startingData = null;
+            mStartingData = fromToken.mStartingData;
+            fromToken.mStartingData = null;
             fromToken.startingMoved = true;
             scheduleAddStartingWindow();
             return true;
@@ -1932,7 +1933,7 @@
                         + ", isAnimationSet=" + isSelfAnimating());
                 if (!w.isDrawnLw()) {
                     Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
-                            + " pv=" + w.mPolicyVisibility
+                            + " pv=" + w.isVisibleByPolicy()
                             + " mDrawState=" + winAnimator.drawStateToString()
                             + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
                             + " a=" + isSelfAnimating());
@@ -2042,7 +2043,7 @@
             return false;
         }
 
-        if (startingData != null) {
+        if (mStartingData != null) {
             return false;
         }
 
@@ -2123,7 +2124,7 @@
         }
 
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
-        startingData = new SplashScreenStartingData(mWmService, pkg,
+        mStartingData = new SplashScreenStartingData(mWmService, pkg,
                 theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 getMergedOverrideConfiguration());
         scheduleAddStartingWindow();
@@ -2137,7 +2138,7 @@
         }
 
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
-        startingData = new SnapshotStartingData(mWmService, snapshot);
+        mStartingData = new SnapshotStartingData(mWmService, snapshot);
         scheduleAddStartingWindow();
         return true;
     }
@@ -2156,18 +2157,21 @@
 
         @Override
         public void run() {
+            // Can be accessed without holding the global lock
+            final StartingData startingData;
             synchronized (mWmService.mGlobalLock) {
                 // There can only be one adding request, silly caller!
                 mWmService.mAnimationHandler.removeCallbacks(this);
-            }
 
-            if (startingData == null) {
-                // Animation has been canceled... do nothing.
-                if (DEBUG_STARTING_WINDOW) {
-                    Slog.v(TAG, "startingData was nulled out before handling"
-                            + " mAddStartingWindow: " + AppWindowToken.this);
+                if (mStartingData == null) {
+                    // Animation has been canceled... do nothing.
+                    if (DEBUG_STARTING_WINDOW) {
+                        Slog.v(TAG, "startingData was nulled out before handling"
+                                + " mAddStartingWindow: " + AppWindowToken.this);
+                    }
+                    return;
                 }
-                return;
+                startingData = mStartingData;
             }
 
             if (DEBUG_STARTING_WINDOW) {
@@ -2185,20 +2189,21 @@
                 synchronized (mWmService.mGlobalLock) {
                     // If the window was successfully added, then
                     // we need to remove it.
-                    if (removed || startingData == null) {
+                    if (removed || mStartingData == null) {
                         if (DEBUG_STARTING_WINDOW) {
                             Slog.v(TAG, "Aborted starting " + AppWindowToken.this
-                                    + ": removed=" + removed + " startingData=" + startingData);
+                                    + ": removed=" + removed + " startingData=" + mStartingData);
                         }
                         startingWindow = null;
-                        startingData = null;
+                        mStartingData = null;
                         abort = true;
                     } else {
                         startingSurface = surface;
                     }
                     if (DEBUG_STARTING_WINDOW && !abort) {
-                        Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow="
-                                + startingWindow + " startingView=" + startingSurface);
+                        Slog.v(TAG,
+                                "Added starting " + AppWindowToken.this + ": startingWindow="
+                                        + startingWindow + " startingView=" + startingSurface);
                     }
                 }
                 if (abort) {
@@ -2245,21 +2250,21 @@
 
     void removeStartingWindow() {
         if (startingWindow == null) {
-            if (startingData != null) {
+            if (mStartingData != null) {
                 // Starting window has not been added yet, but it is scheduled to be added.
                 // Go ahead and cancel the request.
                 if (DEBUG_STARTING_WINDOW) {
                     Slog.v(TAG_WM, "Clearing startingData for token=" + this);
                 }
-                startingData = null;
+                mStartingData = null;
             }
             return;
         }
 
         final WindowManagerPolicy.StartingSurface surface;
-        if (startingData != null) {
+        if (mStartingData != null) {
             surface = startingSurface;
-            startingData = null;
+            mStartingData = null;
             startingSurface = null;
             startingWindow = null;
             startingDisplayed = false;
@@ -2552,12 +2557,17 @@
 
                 final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
                 if (a != null) {
+                    // Only apply corner radius to animation if we're not in multi window mode.
+                    // We don't want rounded corners when in pip or split screen.
+                    final float windowCornerRadius = !inMultiWindowMode()
+                            ? getDisplayContent().getWindowCornerRadius()
+                            : 0;
                     adapter = new LocalAnimationAdapter(
                             new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                                     getDisplayContent().mAppTransition.canSkipFirstFrame(),
                                     appStackClipMode,
                                     true /* isAppAnimation */,
-                                    getWindowCornerRadiusForAnimation()),
+                                    windowCornerRadius),
                             mWmService.mSurfaceAnimationRunner);
                     if (a.getZAdjustment() == Animation.ZORDER_TOP) {
                         mNeedsZBoost = true;
@@ -2994,8 +3004,8 @@
             pw.print(prefix); pw.print("inPendingTransaction=");
                     pw.println(inPendingTransaction);
         }
-        if (startingData != null || removed || firstWindowDrawn || mIsExiting) {
-            pw.print(prefix); pw.print("startingData="); pw.print(startingData);
+        if (mStartingData != null || removed || firstWindowDrawn || mIsExiting) {
+            pw.print(prefix); pw.print("startingData="); pw.print(mStartingData);
                     pw.print(" removed="); pw.print(removed);
                     pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
                     pw.print(" mIsExiting="); pw.println(mIsExiting);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 41292d2..652f7ee 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -90,6 +90,9 @@
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.STACKS;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
+import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
+import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
+import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -144,6 +147,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -541,6 +545,10 @@
 
     private final InsetsStateController mInsetsStateController;
 
+    /** @see #getParentWindow() */
+    private WindowState mParentWindow;
+
+    private Point mLocationInParentWindow = new Point();
     private SurfaceControl mParentSurfaceControl;
     private InputWindowHandle mPortalWindowHandle;
 
@@ -549,6 +557,9 @@
     // Last systemUiVisibility we dispatched to windows.
     private int mLastDispatchedSystemUiVisibility = 0;
 
+    /** Corner radius that windows should have in order to match the display. */
+    private final float mWindowCornerRadius;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final AppWindowToken atoken = w.mAppToken;
@@ -909,6 +920,7 @@
         if (mWmService.mSystemReady) {
             mDisplayPolicy.systemReady();
         }
+        mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
@@ -953,6 +965,10 @@
         return mDisplayId;
     }
 
+    float getWindowCornerRadius() {
+        return mWindowCornerRadius;
+    }
+
     WindowToken getWindowToken(IBinder binder) {
         return mTokenMap.get(binder);
     }
@@ -2723,6 +2739,15 @@
         if (mFocusedApp != null) {
             mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
         }
+        for (int i = mOpeningApps.size() - 1; i >= 0; i--) {
+            mOpeningApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, OPENING_APPS);
+        }
+        for (int i = mClosingApps.size() - 1; i >= 0; i--) {
+            mClosingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CLOSING_APPS);
+        }
+        for (int i = mChangingApps.size() - 1; i >= 0; i--) {
+            mChangingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CHANGING_APPS);
+        }
         proto.end(token);
     }
 
@@ -4562,7 +4587,7 @@
                         token2.mOwnerCanManageAppTokens) ? -1 : 1;
 
         private final Predicate<WindowState> mGetOrientingWindow = w -> {
-            if (!w.isVisibleLw() || !w.mPolicyVisibilityAfterAnim) {
+            if (!w.isVisibleLw() || !w.mLegacyPolicyVisibilityAfterAnim) {
                 return false;
             }
             final int req = w.mAttrs.screenOrientation;
@@ -4923,11 +4948,14 @@
 
     /**
      * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
-     * {@link #mOverlayLayer} to the specified surfaceControl.
+     * {@link #mOverlayLayer} to the specified SurfaceControl.
      *
+     * @param win The window which owns the SurfaceControl. This indicates the z-order of the
+     *            windows of this display against the windows on the parent display.
      * @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to.
      */
-    void reparentDisplayContent(SurfaceControl sc) {
+    void reparentDisplayContent(WindowState win, SurfaceControl sc) {
+        mParentWindow = win;
         mParentSurfaceControl = sc;
         if (mPortalWindowHandle == null) {
             mPortalWindowHandle = createPortalWindowHandle(sc.toString());
@@ -4936,6 +4964,41 @@
                 .reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
     }
 
+    /**
+     * Get the window which owns the surface that this DisplayContent is re-parented to.
+     *
+     * @return the parent window.
+     */
+    WindowState getParentWindow() {
+        return mParentWindow;
+    }
+
+    /**
+     * Update the location of this display in the parent window. This enables windows in this
+     * display to compute the global transformation matrix.
+     *
+     * @param win The parent window of this display.
+     * @param x The x coordinate in the parent window.
+     * @param y The y coordinate in the parent window.
+     */
+    void updateLocation(WindowState win, int x, int y) {
+        if (mParentWindow != win) {
+            throw new IllegalArgumentException(
+                    "The given window is not the parent window of this display.");
+        }
+        if (mLocationInParentWindow.x != x || mLocationInParentWindow.y != y) {
+            mLocationInParentWindow.x = x;
+            mLocationInParentWindow.y = y;
+            if (mWmService.mAccessibilityController != null) {
+                mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+            }
+        }
+    }
+
+    Point getLocationInParentWindow() {
+        return mLocationInParentWindow;
+    }
+
     @VisibleForTesting
     SurfaceControl getWindowingLayer() {
         return mWindowingLayer;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 3bb3653..c23cdbb 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.Display.TYPE_BUILT_IN;
 import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.InsetsState.TYPE_TOP_GESTURES;
 import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT;
@@ -112,7 +113,6 @@
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
 import android.app.LoadedApk;
 import android.app.ResourcesManager;
@@ -133,6 +133,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.DisplayCutout;
@@ -157,6 +158,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.function.TriConsumer;
@@ -2718,11 +2720,18 @@
     private void updateCurrentUserResources() {
         final int userId = mService.mAmInternal.getCurrentUserId();
         final Context uiContext = getSystemUiContext();
+
+        if (userId == UserHandle.USER_SYSTEM) {
+            // Skip the (expensive) recreation of resources for the system user below and just
+            // use the resources from the system ui context
+            mCurrentUserResources = uiContext.getResources();
+            return;
+        }
+
+        // For non-system users, ensure that the resources are loaded from the current
+        // user's package info (see ContextImpl.createDisplayContext)
         final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo(
                 uiContext.getPackageName(), null, 0, userId);
-
-        // Create the resources from the current-user package info
-        // (see ContextImpl.createDisplayContext)
         mCurrentUserResources = ResourcesManager.getInstance().getResources(null,
                 pi.getResDir(),
                 null /* splitResDirs */,
@@ -2870,6 +2879,16 @@
                 - statusBarHeight;
     }
 
+    /**
+     * Return corner radius in pixels that should be used on windows in order to cover the display.
+     * The radius is only valid for built-in displays since the one who configures window corner
+     * radius cannot know the corner radius of non-built-in display.
+     */
+    float getWindowCornerRadius() {
+        return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN
+                ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f;
+    }
+
     boolean isShowingDreamLw() {
         return mShowingDream;
     }
@@ -3113,7 +3132,9 @@
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
         mService.getStackBounds(
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
-        final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
+        final Pair<Integer, Boolean> result =
+                updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
+        final int visibility = result.first;
         final int diff = visibility ^ mLastSystemUiFlags;
         final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
         final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
@@ -3133,13 +3154,14 @@
         mLastDockedStackBounds.set(mDockedStackBounds);
         final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
         final Rect dockedStackBounds = new Rect(mDockedStackBounds);
+        final boolean isNavbarColorManagedByIme = result.second;
         mHandler.post(() -> {
             StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
             if (statusBar != null) {
                 final int displayId = getDisplayId();
                 statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility,
                         dockedVisibility, 0xffffffff, fullscreenStackBounds,
-                        dockedStackBounds, win.toString());
+                        dockedStackBounds, isNavbarColorManagedByIme, win.toString());
                 statusBar.topAppWindowChanged(displayId, needsMenu);
             }
         });
@@ -3222,7 +3244,7 @@
         return vis;
     }
 
-    private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+    private Pair<Integer, Boolean> updateSystemBarsLw(WindowState win, int oldVis, int vis) {
         final boolean dockedStackVisible =
                 mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final boolean freeformStackVisible =
@@ -3355,8 +3377,11 @@
         vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
                 mTopFullscreenOpaqueOrDimmingWindowState,
                 mDisplayContent.mInputMethodWindow, navColorWin);
+        // Navbar color is controlled by the IME.
+        final boolean isManagedByIme =
+                navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
 
-        return vis;
+        return Pair.create(vis, isManagedByIme);
     }
 
     private boolean drawsBarBackground(int vis, WindowState win, BarController controller,
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f67b11b..402ec59 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -29,12 +29,11 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import android.view.InsetsSource;
-import android.view.InsetsSourceControl;
-import android.view.ViewRootImpl;
 
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -142,7 +141,7 @@
                 mStateController.notifyControlChanged(mControllingWin);
             }
         }
-        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility
+        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()
                 && !mWin.mGivenInsetsPending);
     }
 
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index e65a241..3ec461d 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1174,8 +1174,9 @@
                     resumedOnDisplay |= result;
                     continue;
                 }
-                if (topRunningActivity.isState(RESUMED)) {
-                    // Kick off any lingering app transitions form the MoveTaskToFront operation.
+                if (display.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
+                    // Kick off any lingering app transitions form the MoveTaskToFront operation,
+                    // but only consider the top task and stack on that display.
                     stack.executeAppTransition(targetOptions);
                 } else {
                     resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b33f8c7..34273f3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -426,6 +426,16 @@
     }
 
     @Override
+    public void reparentDisplayContent(IWindow window, SurfaceControl sc, int displayId) {
+        mService.reparentDisplayContent(window, sc, displayId);
+    }
+
+    @Override
+    public void updateDisplayContentLocation(IWindow window, int x, int y, int displayId) {
+        mService.updateDisplayContentLocation(window, x, y, displayId);
+    }
+
+    @Override
     public void updateTapExcludeRegion(IWindow window, int regionId, Region region) {
         final long identity = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index d4d157fb..8505ec2 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1161,6 +1161,19 @@
         return false;
     }
 
+    /**
+     * Return true if any activities in this task belongs to input uid.
+     */
+    boolean containsAppUid(int uid) {
+        for (int i = mActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.getUid() == uid) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
         if (mStack != null) {
             for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4eddb30..7ac887e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -240,7 +240,6 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
-import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
@@ -793,9 +792,6 @@
     final DisplayManager mDisplayManager;
     final ActivityTaskManagerService mAtmService;
 
-    /** Corner radius that windows should have in order to match the display. */
-    final float mWindowCornerRadius;
-
     /** Indicates whether this device supports wide color gamut / HDR rendering */
     private boolean mHasWideColorGamutSupport;
     private boolean mHasHdrSupport;
@@ -1020,7 +1016,6 @@
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplayWindowSettings = new DisplayWindowSettings(this);
-        mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context.getResources());
 
         mTransactionFactory = transactionFactory;
         mTransaction = mTransactionFactory.make();
@@ -1880,7 +1875,8 @@
 
                     // We need to report touchable region changes to accessibility.
                     if (mAccessibilityController != null
-                            && w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
+                            && (w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY
+                                    || w.getDisplayContent().getParentWindow() != null)) {
                         mAccessibilityController.onSomeWindowResizedOrMovedLocked();
                     }
                 }
@@ -2012,7 +2008,8 @@
                 }
                 if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
                         && (mAccessibilityController != null)
-                        && (win.getDisplayId() == DEFAULT_DISPLAY)) {
+                        && (win.getDisplayId() == DEFAULT_DISPLAY
+                                || win.getDisplayContent().getParentWindow() != null)) {
                     // No move or resize, but the controller checks for title changes as well
                     mAccessibilityController.onSomeWindowResizedOrMovedLocked();
                 }
@@ -5300,7 +5297,7 @@
                     ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
                     " mHasSurface=" + win.mHasSurface +
                     " drawState=" + win.mWinAnimator.mDrawState);
-            if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {
+            if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
                 // Window has been removed or hidden; no draw will now happen, so stop waiting.
                 if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win);
                 mWaitingForDrawn.remove(win);
@@ -6703,6 +6700,61 @@
         }
     }
 
+    private void checkCallerOwnsDisplay(int displayId) {
+        final Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            throw new IllegalArgumentException(
+                    "Cannot find display for non-existent displayId: " + displayId);
+        }
+
+        final int callingUid = Binder.getCallingUid();
+        final int displayOwnerUid = display.getOwnerUid();
+        if (callingUid != displayOwnerUid) {
+            throw new SecurityException("The caller doesn't own the display.");
+        }
+    }
+
+    /** @see Session#reparentDisplayContent(IWindow, SurfaceControl, int)  */
+    void reparentDisplayContent(IWindow client, SurfaceControl sc, int displayId) {
+        checkCallerOwnsDisplay(displayId);
+
+        synchronized (mGlobalLock) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final WindowState win = windowForClientLocked(null, client, false);
+                if (win == null) {
+                    Slog.w(TAG_WM, "Bad requesting window " + client);
+                    return;
+                }
+                getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    /** @see Session#updateDisplayContentLocation(IWindow, int, int, int)  */
+    void updateDisplayContentLocation(IWindow client, int x, int y, int displayId) {
+        checkCallerOwnsDisplay(displayId);
+
+        synchronized (mGlobalLock) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final WindowState win = windowForClientLocked(null, client, false);
+                if (win == null) {
+                    Slog.w(TAG_WM, "Bad requesting window " + client);
+                    return;
+                }
+                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+                if (displayContent != null) {
+                    displayContent.updateLocation(win, x, y);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
     /**
      * Update a tap exclude region in the window identified by the provided id. Touches down on this
      * region will not:
@@ -7538,31 +7590,6 @@
     }
 
     @Override
-    public void reparentDisplayContent(int displayId, SurfaceControl sc) {
-        final Display display = mDisplayManager.getDisplay(displayId);
-        if (display == null) {
-            throw new IllegalArgumentException(
-                    "Can't reparent display for non-existent displayId: " + displayId);
-        }
-
-        final int callingUid = Binder.getCallingUid();
-        final int displayOwnerUid = display.getOwnerUid();
-        if (callingUid != displayOwnerUid) {
-            throw new SecurityException("Only owner of the display can reparent surfaces to it.");
-        }
-
-        synchronized (mGlobalLock) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
-                displayContent.reparentDisplayContent(sc);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-
-    @Override
     public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
         boolean shouldWaitForAnimToComplete = false;
         if (ev instanceof KeyEvent) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e39cd56..497c91a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -251,18 +251,33 @@
     int mSeq;
     int mViewVisibility;
     int mSystemUiVisibility;
+
     /**
-     * The visibility of the window based on policy like {@link WindowManagerPolicy}.
+     * The visibility flag of the window based on policy like {@link WindowManagerPolicy}.
      * Normally set by calling {@link #showLw} and {@link #hideLw}.
+     *
+     * TODO: b/131253938 This will eventually be split into individual visibility policy flags.
      */
-    boolean mPolicyVisibility = true;
+    static final int LEGACY_POLICY_VISIBILITY = 1;
     /**
-     * What {@link #mPolicyVisibility} should be set to after a transition animation.
-     * For example, {@link #mPolicyVisibility} might true during an exit animation to hide it and
-     * then set to the value of {@link #mPolicyVisibilityAfterAnim} which is false after the exit
-     * animation is done.
+     * The visibility flag that determines whether this window is visible for the current user.
      */
-    boolean mPolicyVisibilityAfterAnim = true;
+    private static final int VISIBLE_FOR_USER = 1 << 1;
+    private static final int POLICY_VISIBILITY_ALL = VISIBLE_FOR_USER | LEGACY_POLICY_VISIBILITY;
+    /**
+     * The Bitwise-or of flags that contribute to visibility of the WindowState
+     */
+    private int mPolicyVisibility = POLICY_VISIBILITY_ALL;
+
+    /**
+     * Whether {@link #LEGACY_POLICY_VISIBILITY} flag should be set after a transition animation.
+     * For example, {@link #LEGACY_POLICY_VISIBILITY} might be set during an exit animation to hide
+     * it and then unset when the value of {@link #mLegacyPolicyVisibilityAfterAnim} is false
+     * after the exit animation is done.
+     *
+     * TODO: b/131253938 Determine whether this can be changed to use a visibility flag instead.
+     */
+    boolean mLegacyPolicyVisibilityAfterAnim = true;
     // overlay window is hidden because the owning app is suspended
     private boolean mHiddenWhileSuspended;
     private boolean mAppOpVisibility = true;
@@ -1414,13 +1429,33 @@
 
     @Override
     boolean isVisible() {
-        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility
+        return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
                 // If we don't have a provider, this window isn't used as a window generating
                 // insets, so nobody can hide it over the inset APIs.
                 && (mInsetProvider == null || mInsetProvider.isClientVisible());
     }
 
     /**
+     * Ensures that all the policy visibility bits are set.
+     * @return {@code true} if all flags about visiblity are set
+     */
+    boolean isVisibleByPolicy() {
+        return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL;
+    }
+
+    void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
+        mPolicyVisibility &= ~policyVisibilityFlag;
+    }
+
+    void setPolicyVisibilityFlag(int policyVisibilityFlag) {
+        mPolicyVisibility |= policyVisibilityFlag;
+    }
+
+    private boolean isLegacyPolicyVisibility() {
+        return (mPolicyVisibility & LEGACY_POLICY_VISIBILITY) != 0;
+    }
+
+    /**
      * @return {@code true} if the window would be visible if we'd ignore policy visibility,
      *         {@code false} otherwise.
      */
@@ -1470,7 +1505,7 @@
     boolean isVisibleOrAdding() {
         final AppWindowToken atoken = mAppToken;
         return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
-                && mPolicyVisibility && !isParentWindowHidden()
+                && isVisibleByPolicy() && !isParentWindowHidden()
                 && (atoken == null || !atoken.hiddenRequested)
                 && !mAnimatingExit && !mDestroying;
     }
@@ -1481,7 +1516,7 @@
      * being visible.
      */
     boolean isOnScreen() {
-        if (!mHasSurface || mDestroying || !mPolicyVisibility) {
+        if (!mHasSurface || mDestroying || !isVisibleByPolicy()) {
             return false;
         }
         final AppWindowToken atoken = mAppToken;
@@ -1522,7 +1557,7 @@
         }
         final boolean parentAndClientVisible = !isParentWindowHidden()
                 && mViewVisibility == View.VISIBLE && !mToken.isHidden();
-        return mHasSurface && mPolicyVisibility && !mDestroying
+        return mHasSurface && isVisibleByPolicy() && !mDestroying
                 && (parentAndClientVisible || isAnimating());
     }
 
@@ -1551,7 +1586,7 @@
     @Override
     public boolean isDisplayedLw() {
         final AppWindowToken atoken = mAppToken;
-        return isDrawnLw() && mPolicyVisibility
+        return isDrawnLw() && isVisibleByPolicy()
                 && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
                         || isAnimating());
     }
@@ -2057,8 +2092,8 @@
                 Slog.i(TAG_WM, "  mSurfaceController=" + mWinAnimator.mSurfaceController
                         + " relayoutCalled=" + mRelayoutCalled
                         + " viewVis=" + mViewVisibility
-                        + " policyVis=" + mPolicyVisibility
-                        + " policyVisAfterAnim=" + mPolicyVisibilityAfterAnim
+                        + " policyVis=" + isVisibleByPolicy()
+                        + " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                         + " parentHidden=" + isParentWindowHidden()
                         + " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
                 if (mAppToken != null) {
@@ -2192,7 +2227,9 @@
         if (isHiddenFromUserLocked()) {
             if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this
                     + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid);
-            hideLw(false);
+            clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
+        } else {
+            setPolicyVisibilityFlag(VISIBLE_FOR_USER);
         }
     }
 
@@ -2284,13 +2321,17 @@
     }
 
     void checkPolicyVisibilityChange() {
-        if (mPolicyVisibility != mPolicyVisibilityAfterAnim) {
+        if (isLegacyPolicyVisibility() != mLegacyPolicyVisibilityAfterAnim) {
             if (DEBUG_VISIBILITY) {
                 Slog.v(TAG, "Policy visibility changing after anim in " +
-                        mWinAnimator + ": " + mPolicyVisibilityAfterAnim);
+                        mWinAnimator + ": " + mLegacyPolicyVisibilityAfterAnim);
             }
-            mPolicyVisibility = mPolicyVisibilityAfterAnim;
-            if (!mPolicyVisibility) {
+            if (mLegacyPolicyVisibilityAfterAnim) {
+                setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+            } else {
+                clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+            }
+            if (!isVisibleByPolicy()) {
                 mWinAnimator.hide("checkPolicyVisibilityChange");
                 if (isFocused()) {
                     if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
@@ -2531,7 +2572,7 @@
     }
 
     boolean showLw(boolean doAnimation, boolean requestAnim) {
-        if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+        if (isLegacyPolicyVisibility() && mLegacyPolicyVisibilityAfterAnim) {
             // Already showing.
             return false;
         }
@@ -2558,18 +2599,18 @@
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
         if (doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
-                    + mPolicyVisibility + " animating=" + isAnimating());
+                    + isLegacyPolicyVisibility() + " animating=" + isAnimating());
             if (!mToken.okToAnimate()) {
                 doAnimation = false;
-            } else if (mPolicyVisibility && !isAnimating()) {
+            } else if (isLegacyPolicyVisibility() && !isAnimating()) {
                 // Check for the case where we are currently visible and
                 // not animating; we do not want to do animation at such a
                 // point to become visible when we already are.
                 doAnimation = false;
             }
         }
-        mPolicyVisibility = true;
-        mPolicyVisibilityAfterAnim = true;
+        setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+        mLegacyPolicyVisibilityAfterAnim = true;
         if (doAnimation) {
             mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
         }
@@ -2593,7 +2634,8 @@
                 doAnimation = false;
             }
         }
-        boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility;
+        boolean current =
+                doAnimation ? mLegacyPolicyVisibilityAfterAnim : isLegacyPolicyVisibility();
         if (!current) {
             // Already hiding.
             return false;
@@ -2604,11 +2646,11 @@
                 doAnimation = false;
             }
         }
-        mPolicyVisibilityAfterAnim = false;
+        mLegacyPolicyVisibilityAfterAnim = false;
         final boolean isFocused = isFocused();
         if (!doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
-            mPolicyVisibility = false;
+            clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
             // Window is no longer visible -- make sure if we were waiting
             // for it to be displayed before enabling the display, that
             // we allow the display to be enabled now.
@@ -3100,7 +3142,8 @@
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
-            if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+            if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
+                    || getDisplayContent().getParentWindow() != null)) {
                 mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
             }
 
@@ -3443,11 +3486,11 @@
             pw.println(prefix + "mSeq=" + mSeq
                     + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
         }
-        if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility
+        if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
                 || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow
                 || mHiddenWhileSuspended) {
-            pw.println(prefix + "mPolicyVisibility=" + mPolicyVisibility
-                    + " mPolicyVisibilityAfterAnim=" + mPolicyVisibilityAfterAnim
+            pw.println(prefix + "mPolicyVisibility=" + isVisibleByPolicy()
+                    + " mLegacyPolicyVisibilityAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                     + " mAppOpVisibility=" + mAppOpVisibility
                     + " parentHidden=" + isParentWindowHidden()
                     + " mPermanentlyHidden=" + mPermanentlyHidden
@@ -3904,7 +3947,7 @@
                     + ": mDrawState=" + mWinAnimator.drawStateToString()
                     + " readyForDisplay=" + isReadyForDisplay()
                     + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING)
-                    + " during animation: policyVis=" + mPolicyVisibility
+                    + " during animation: policyVis=" + isVisibleByPolicy()
                     + " parentHidden=" + isParentWindowHidden()
                     + " tok.hiddenRequested="
                     + (mAppToken != null && mAppToken.hiddenRequested)
@@ -4165,7 +4208,8 @@
         }
 
         //TODO (multidisplay): Accessibility is supported only for the default display.
-        if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+        if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
+                || getDisplayContent().getParentWindow() != null)) {
             mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
         }
 
@@ -4313,7 +4357,7 @@
                     + ", animating=" + isAnimating());
             if (!isDrawnLw()) {
                 Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
-                        + " pv=" + mPolicyVisibility
+                        + " pv=" + isVisibleByPolicy()
                         + " mDrawState=" + mWinAnimator.mDrawState
                         + " ph=" + isParentWindowHidden()
                         + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
@@ -4555,7 +4599,7 @@
         anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
         final AnimationAdapter adapter = new LocalAnimationAdapter(
                 new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
-                        mToken.getWindowCornerRadiusForAnimation()),
+                        0 /* windowCornerRadius */),
                 mWmService.mSurfaceAnimationRunner);
         startAnimation(mPendingTransaction, adapter);
         commitPendingTransaction();
@@ -4604,6 +4648,18 @@
         int x = mSurfacePosition.x;
         int y = mSurfacePosition.y;
 
+        // We might be on a display which has been re-parented to a view in another window, so here
+        // computes the global location of our display.
+        DisplayContent dc = getDisplayContent();
+        while (dc != null && dc.getParentWindow() != null) {
+            final WindowState displayParent = dc.getParentWindow();
+            x += displayParent.mWindowFrames.mFrame.left - displayParent.mAttrs.surfaceInsets.left
+                    + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f);
+            y += displayParent.mWindowFrames.mFrame.top - displayParent.mAttrs.surfaceInsets.top
+                    + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f);
+            dc = displayParent.getDisplayContent();
+        }
+
         // If changed, also adjust transformFrameToSurfacePosition
         final WindowContainer parent = getParent();
         if (isChildWindow()) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 780d471..20e1ac6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -256,7 +256,7 @@
 
         mWin.checkPolicyVisibilityChange();
         final DisplayContent displayContent = mWin.getDisplayContent();
-        if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
+        if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.isVisibleByPolicy()) {
             // Upon completion of a not-visible to visible status bar animation a relayout is
             // required.
             if (displayContent != null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f65f0ab..f0b9c62 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -345,8 +345,4 @@
                 mOwnerCanManageAppTokens);
         return mOwnerCanManageAppTokens && (layer > navLayer);
     }
-
-    float getWindowCornerRadiusForAnimation() {
-        return mDisplayContent.isDefaultDisplay ? mWmService.mWindowCornerRadius : 0;
-    }
 }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4d37f1a..98c620c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -45,6 +45,13 @@
 #include <string.h>
 #include <utils/SystemClock.h>
 
+static jclass class_gnssMeasurementsEvent;
+static jclass class_gnssMeasurement;
+static jclass class_location;
+static jclass class_gnssNavigationMessage;
+static jclass class_gnssClock;
+static jclass class_gnssConfiguration_halInterfaceVersion;
+
 static jobject mCallbacksObj = nullptr;
 
 static jmethodID method_reportLocation;
@@ -95,6 +102,12 @@
 static jmethodID method_correctionPlaneAzimDeg;
 static jmethodID method_reportNfwNotification;
 static jmethodID method_isInEmergencySession;
+static jmethodID method_gnssMeasurementsEventCtor;
+static jmethodID method_locationCtor;
+static jmethodID method_gnssNavigationMessageCtor;
+static jmethodID method_gnssClockCtor;
+static jmethodID method_gnssMeasurementCtor;
+static jmethodID method_halInterfaceVersionCtor;
 
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -255,11 +268,11 @@
 
 class JavaObject {
  public:
-    JavaObject(JNIEnv* env, const char* class_name);
-    JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1);
-    JavaObject(JNIEnv* env, const char* class_name, jobject object);
+    JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor);
+    JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1);
+    JavaObject(JNIEnv* env, jclass clazz, jobject object);
 
-    virtual ~JavaObject();
+    virtual ~JavaObject() = default;
 
     template<class T>
     void callSetter(const char* method_name, T value);
@@ -273,25 +286,20 @@
     jobject object_;
 };
 
-JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) {
-    clazz_ = env_->FindClass(class_name);
-    jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V");
-    object_ = env_->NewObject(clazz_, ctor);
+JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor) : env_(env),
+        clazz_(clazz) {
+    object_ = env_->NewObject(clazz_, defaultCtor);
 }
 
-JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1) : env_(env) {
-    clazz_ = env_->FindClass(class_name);
-    jmethodID ctor = env->GetMethodID(clazz_, "<init>", "(Ljava/lang/String;)V");
-    object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1));
+
+JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1)
+        : env_(env), clazz_(clazz) {
+    object_ = env_->NewObject(clazz_, stringCtor, env->NewStringUTF(sz_arg_1));
 }
 
-JavaObject::JavaObject(JNIEnv* env, const char* class_name, jobject object)
-    : env_(env), object_(object) {
-    clazz_ = env_->FindClass(class_name);
-}
 
-JavaObject::~JavaObject() {
-    env_->DeleteLocalRef(clazz_);
+JavaObject::JavaObject(JNIEnv* env, jclass clazz, jobject object)
+    : env_(env), clazz_(clazz), object_(object) {
 }
 
 template<class T>
@@ -358,11 +366,8 @@
 }
 
 static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
-    jclass versionClass =
-            env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
-    jmethodID versionCtor = env->GetMethodID(versionClass, "<init>", "(II)V");
-    jobject version = env->NewObject(versionClass, versionCtor, major, minor);
-    env->DeleteLocalRef(versionClass);
+    jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion,
+            method_halInterfaceVersionCtor, major, minor);
     return version;
 }
 
@@ -452,7 +457,7 @@
 
 static jobject translateGnssLocation(JNIEnv* env,
                                      const GnssLocation_V1_0& location) {
-    JavaObject object(env, "android/location/Location", "gps");
+    JavaObject object(env, class_location, method_locationCtor, "gps");
 
     uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
     if (flags & GnssLocationFlags::HAS_LAT_LONG) {
@@ -488,8 +493,7 @@
 
 static jobject translateGnssLocation(JNIEnv* env,
                                      const GnssLocation_V2_0& location) {
-    JavaObject object(env, "android/location/Location",
-                      translateGnssLocation(env, location.v1_0));
+    JavaObject object(env, class_location, translateGnssLocation(env, location.v1_0));
 
     const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags);
 
@@ -946,7 +950,7 @@
       return Void();
     }
 
-    JavaObject object(env, "android/location/GnssNavigationMessage");
+    JavaObject object(env, class_gnssNavigationMessage, method_gnssNavigationMessageCtor);
     SET(Type, static_cast<int32_t>(message.type));
     SET(Svid, static_cast<int32_t>(message.svid));
     SET(MessageId, static_cast<int32_t>(message.messageId));
@@ -1013,7 +1017,7 @@
 void GnssMeasurementCallback::translateAndSetGnssData(const T& data) {
     JNIEnv* env = getJniEnv();
 
-    JavaObject gnssClockJavaObject(env, "android/location/GnssClock");
+    JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor);
     translateGnssClock(gnssClockJavaObject, data);
     jobject clock = gnssClockJavaObject.get();
 
@@ -1175,41 +1179,30 @@
         return nullptr;
     }
 
-    jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
     jobjectArray gnssMeasurementArray = env->NewObjectArray(
             count,
-            gnssMeasurementClass,
+            class_gnssMeasurement,
             nullptr /* initialElement */);
 
     for (uint16_t i = 0; i < count; ++i) {
-        JavaObject object(env, "android/location/GnssMeasurement");
+        JavaObject object(env, class_gnssMeasurement, method_gnssMeasurementCtor);
         translateSingleGnssMeasurement(&(measurements[i]), object);
         env->SetObjectArrayElement(gnssMeasurementArray, i, object.get());
     }
 
-    env->DeleteLocalRef(gnssMeasurementClass);
     return gnssMeasurementArray;
 }
 
 void GnssMeasurementCallback::setMeasurementData(JNIEnv* env, jobject clock,
                              jobjectArray measurementArray) {
-    jclass gnssMeasurementsEventClass =
-            env->FindClass("android/location/GnssMeasurementsEvent");
-    jmethodID gnssMeasurementsEventCtor =
-            env->GetMethodID(
-                    gnssMeasurementsEventClass,
-                    "<init>",
-                    "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
-
-    jobject gnssMeasurementsEvent = env->NewObject(gnssMeasurementsEventClass,
-                                                   gnssMeasurementsEventCtor,
+    jobject gnssMeasurementsEvent = env->NewObject(class_gnssMeasurementsEvent,
+                                                   method_gnssMeasurementsEventCtor,
                                                    clock,
                                                    measurementArray);
 
     env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData,
                       gnssMeasurementsEvent);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    env->DeleteLocalRef(gnssMeasurementsEventClass);
     env->DeleteLocalRef(gnssMeasurementsEvent);
 }
 
@@ -1464,8 +1457,7 @@
 Return<void> GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(const hidl_vec<T>& locations) {
     JNIEnv* env = getJniEnv();
 
-    jobjectArray jLocations = env->NewObjectArray(locations.size(),
-            env->FindClass("android/location/Location"), nullptr);
+    jobjectArray jLocations = env->NewObjectArray(locations.size(), class_location, nullptr);
 
     for (uint16_t i = 0; i < locations.size(); ++i) {
         jobject jLocation = translateGnssLocation(env, locations[i]);
@@ -1617,6 +1609,36 @@
     method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D");
     method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D");
 
+    jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
+    class_gnssMeasurementsEvent= (jclass) env->NewGlobalRef(gnssMeasurementsEventClass);
+    method_gnssMeasurementsEventCtor = env->GetMethodID(
+                    class_gnssMeasurementsEvent,
+                    "<init>",
+                    "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
+
+    jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
+    class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass);
+    method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V");
+
+    jclass locationClass = env->FindClass("android/location/Location");
+    class_location = (jclass) env->NewGlobalRef(locationClass);
+    method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V");
+
+    jclass gnssNavigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
+    class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass);
+    method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
+
+    jclass gnssClockClass = env->FindClass("android/location/GnssClock");
+    class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass);
+    method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V");
+
+    jclass gnssConfiguration_halInterfaceVersionClass =
+            env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
+    class_gnssConfiguration_halInterfaceVersion =
+            (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
+    method_halInterfaceVersionCtor =
+            env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
+
     /*
      * Save a pointer to JVM.
      */
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c5a2068..22231c0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8034,6 +8034,7 @@
             throw new IllegalArgumentException("Component " + who
                     + " not installed for userId:" + userHandle);
         }
+
         final boolean hasIncompatibleAccountsOrNonAdb =
                 hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who);
         synchronized (getLockObject()) {
@@ -8539,9 +8540,30 @@
             return;
         }
         enforceCanManageProfileAndDeviceOwners();
-        if ((mIsWatch || hasUserSetupCompleted(userHandle)) && !isCallerWithSystemUid()) {
-            throw new IllegalStateException("Cannot set the profile owner on a user which is "
-                    + "already set-up");
+
+        if ((mIsWatch || hasUserSetupCompleted(userHandle))) {
+            if (!isCallerWithSystemUid()) {
+                throw new IllegalStateException("Cannot set the profile owner on a user which is "
+                        + "already set-up");
+            }
+
+            if (!mIsWatch) {
+                // Only the default supervision profile owner can be set as profile owner after SUW
+                final String supervisor = mContext.getResources().getString(
+                        com.android.internal.R.string
+                                .config_defaultSupervisionProfileOwnerComponent);
+                if (supervisor == null) {
+                    throw new IllegalStateException("Unable to set profile owner post-setup, no"
+                            + "default supervisor profile owner defined");
+                }
+
+                final ComponentName supervisorComponent = ComponentName.unflattenFromString(
+                        supervisor);
+                if (!owner.equals(supervisorComponent)) {
+                    throw new IllegalStateException("Unable to set non-default profile owner"
+                            + " post-setup " + owner);
+                }
+            }
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 6ec864c..f4a6231 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
 import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
 import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
@@ -821,6 +822,41 @@
     }
 
     @Test
+    public void testInitialForegroundServiceTimeout() throws Exception {
+        setChargingState(mController, false);
+
+        mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
+        // Make sure app is in NEVER bucket
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+                REASON_MAIN_FORCED, mInjector.mElapsedRealtime);
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_NEVER);
+
+        mInjector.mElapsedRealtime += 100;
+
+        // Trigger a FOREGROUND_SERVICE_START and verify bucket
+        reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Verify it's still in ACTIVE close to end of timeout
+        mInjector.mElapsedRealtime += mController.mInitialForegroundServiceStartTimeoutMillis - 100;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Verify bucket moves to RARE after timeout
+        mInjector.mElapsedRealtime += 200;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_RARE);
+
+        // Trigger a FOREGROUND_SERVICE_START again
+        reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
+        mController.checkIdleStates(USER_ID);
+        // Bucket should not be immediately elevated on subsequent service starts
+        assertBucket(STANDBY_BUCKET_RARE);
+    }
+
+    @Test
     public void testPredictionNotOverridden() throws Exception {
         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cbca087..8d56bc4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -62,6 +62,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -85,6 +86,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Person;
+import android.app.RemoteInput;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
@@ -96,6 +98,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.Icon;
@@ -327,6 +330,8 @@
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
 
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+
         mService = new TestableNotificationManagerService(mContext);
 
         // Use this testable looper.
@@ -375,20 +380,16 @@
 
         when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
 
-        try {
-            mService.init(mTestableLooper.getLooper(),
-                    mPackageManager, mPackageManagerClient, mockLightsManager,
-                    mListeners, mAssistants, mConditionProviders,
-                    mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
-                    mGroupHelper, mAm, mAppUsageStats,
-                    mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
-                    mAppOpsManager, mUm);
-            mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mService.init(mTestableLooper.getLooper(),
+                mPackageManager, mPackageManagerClient, mockLightsManager,
+                mListeners, mAssistants, mConditionProviders,
+                mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
+                mGroupHelper, mAm, mAppUsageStats,
+                mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
+                mAppOpsManager, mUm);
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
         mService.setAudioManager(mAudioManager);
 
         // Tests call directly into the Binder.
@@ -2080,14 +2081,8 @@
     public void testSetListenerAccessForUser() throws Exception {
         UserHandle user = UserHandle.of(10);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationListenerAccessGrantedForUser(
-                    c, user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true);
+
 
         verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
@@ -2101,15 +2096,14 @@
     @Test
     public void testSetAssistantAccessForUser() throws Exception {
         UserHandle user = UserHandle.of(10);
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 10;
+        uis.add(ui);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationAssistantAccessGrantedForUser(
-                    c, user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        when(mUm.getEnabledProfiles(10)).thenReturn(uis);
+
+        mBinderService.setNotificationAssistantAccessGrantedForUser(c, user.getIdentifier(), true);
 
         verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
@@ -2150,14 +2144,8 @@
     public void testSetDndAccessForUser() throws Exception {
         UserHandle user = UserHandle.of(10);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationPolicyAccessGrantedForUser(
-                    c.getPackageName(), user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationPolicyAccessGrantedForUser(
+                c.getPackageName(), user.getIdentifier(), true);
 
         verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
@@ -2171,13 +2159,7 @@
     @Test
     public void testSetListenerAccess() throws Exception {
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationListenerAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationListenerAccessGranted(c, true);
 
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, true);
@@ -2189,14 +2171,14 @@
 
     @Test
     public void testSetAssistantAccess() throws Exception {
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationAssistantAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, true);
@@ -2207,19 +2189,44 @@
     }
 
     @Test
+    public void testSetAssistantAccess_multiProfile() throws Exception {
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        UserInfo ui10 = new UserInfo();
+        ui10.id = 10;
+        uis.add(ui10);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+        ComponentName c = ComponentName.unflattenFromString("package/Component");
+
+        mBinderService.setNotificationAssistantAccessGranted(c, true);
+
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 0, true, true);
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 10, true, true);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 0, false, true);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 10, false, true);
+        verify(mListeners, never()).setPackageOrComponentEnabled(
+                any(), anyInt(), anyBoolean(), anyBoolean());
+    }
+
+    @Test
     public void testSetAssistantAccess_nullWithAllowedAssistant() throws Exception {
         ArrayList<ComponentName> componentList = new ArrayList<>();
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         componentList.add(c);
         when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
 
-        try {
-            mBinderService.setNotificationAssistantAccessGranted(null, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationAssistantAccessGranted(null, true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, false);
@@ -2231,23 +2238,23 @@
 
     @Test
     public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception {
-        UserHandle user = UserHandle.of(10);
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 10;
+        uis.add(ui);
+        UserHandle user = ui.getUserHandle();
         ArrayList<ComponentName> componentList = new ArrayList<>();
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         componentList.add(c);
         when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+        when(mUm.getEnabledProfiles(10)).thenReturn(uis);
 
-        try {
-            mBinderService.setNotificationAssistantAccessGrantedForUser(
-                    null, user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationAssistantAccessGrantedForUser(
+                null, user.getIdentifier(), true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), true, false);
+        verify(mAssistants).setUserSet(10, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), false,  false);
         verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -2255,15 +2262,44 @@
     }
 
     @Test
+    public void testSetAssistantAccessForUser_workProfile_nullWithAllowedAssistant()
+            throws Exception {
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        UserInfo ui10 = new UserInfo();
+        ui10.id = 10;
+        uis.add(ui10);
+        UserHandle user = ui.getUserHandle();
+        ArrayList<ComponentName> componentList = new ArrayList<>();
+        ComponentName c = ComponentName.unflattenFromString("package/Component");
+        componentList.add(c);
+        when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
+        mBinderService.setNotificationAssistantAccessGrantedForUser(
+                    null, user.getIdentifier(), true);
+
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), user.getIdentifier(), true, false);
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), ui10.id, true, false);
+        verify(mAssistants).setUserSet(0, true);
+        verify(mAssistants).setUserSet(10, true);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), user.getIdentifier(), false,  false);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), ui10.id, false,  false);
+        verify(mListeners, never()).setPackageOrComponentEnabled(
+                any(), anyInt(), anyBoolean(), anyBoolean());
+    }
+
+    @Test
     public void testSetDndAccess() throws Exception {
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
 
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
                 c.getPackageName(), 0, true, true);
@@ -2291,6 +2327,12 @@
     public void testSetAssistantAccess_doesNothingOnLowRam() throws Exception {
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
         mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -2320,13 +2362,8 @@
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationListenerAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationListenerAccessGranted(c, true);
 
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, true);
@@ -2341,13 +2378,13 @@
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationAssistantAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
+        mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 anyString(), anyInt(), anyBoolean(), anyBoolean());
@@ -2362,13 +2399,8 @@
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
 
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 anyString(), anyInt(), anyBoolean(), anyBoolean());
@@ -4492,6 +4524,13 @@
         Person person = new Person.Builder()
                 .setName("bubblebot")
                 .build();
+        // It needs remote input to be bubble-able
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
         // Make it messaging style
         Notification.Builder nb = new Notification.Builder(mContext,
                 mTestNotificationChannel.getId())
@@ -4504,6 +4543,7 @@
                         .addMessage("Is it me you're looking for?",
                                 SystemClock.currentThreadTimeMillis(), person)
                 )
+                .setActions(replyAction)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 45d5219..5803385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -79,7 +79,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
-import com.android.server.wm.TaskRecord.TaskRecordFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -330,21 +329,14 @@
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
-        // instrument the stack and task used.
-        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor)
-                .setCreateStack(false)
-                .build();
-
-        // use factory that only returns spy task.
-        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-
-        // return task when created.
-        doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
+        // Use factory that only returns spy task.
+        mockTaskRecordFactory();
 
         if (mockGetLaunchStack) {
+            // Instrument the stack and task used.
+            final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
             // Direct starter to use spy stack.
             doReturn(stack).when(mRootActivityContainer)
                     .getLaunchStack(any(), any(), any(), anyBoolean());
@@ -707,6 +699,36 @@
     }
 
     /**
+     * This test ensures that {@link ActivityStarter#setTargetStackAndMoveToFrontIfNeeded} will
+     * move the existing task to front if the current focused stack doesn't have running task.
+     */
+    @Test
+    public void testBringTaskToFrontWhenFocusedStackIsFinising() {
+        // Put 2 tasks in the same stack (simulate the behavior of home stack).
+        final ActivityRecord activity = new ActivityBuilder(mService)
+                .setCreateTask(true).build();
+        new ActivityBuilder(mService)
+                .setStack(activity.getActivityStack())
+                .setCreateTask(true).build();
+
+        // Create a top finishing activity.
+        final ActivityRecord finishingTopActivity = new ActivityBuilder(mService)
+                .setCreateTask(true).build();
+        finishingTopActivity.getActivityStack().moveToFront("finishingTopActivity");
+
+        assertEquals(finishingTopActivity, mRootActivityContainer.topRunningActivity());
+        finishingTopActivity.finishing = true;
+
+        // Launch the bottom task of the target stack.
+        prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetLaunchStack */)
+                .setReason("testBringTaskToFrontWhenTopStackIsFinising")
+                .setIntent(activity.intent)
+                .execute();
+        // The hierarchies of the activity should move to front.
+        assertEquals(activity, mRootActivityContainer.topRunningActivity());
+    }
+
+    /**
      * This test ensures that when starting an existing single task activity on secondary display
      * which is not the top focused display, it should deliver new intent to the activity and not
      * create a new stack.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index afd4ec1..09b511a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -41,6 +41,7 @@
 
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
+import android.app.AppOpsManager;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
@@ -71,6 +72,7 @@
 import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.TaskRecord.TaskRecordFactory;
 import com.android.server.wm.utils.MockTracker;
 
 import org.junit.After;
@@ -159,6 +161,19 @@
     }
 
     /**
+     * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
+     * when starting activity in unit tests.
+     */
+    void mockTaskRecordFactory() {
+        final TaskRecord task = new TaskBuilder(mSupervisor).setCreateStack(false).build();
+        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
+        TaskRecord.setTaskRecordFactory(factory);
+        doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
+                any() /* info */, any() /* intent */, any() /* voiceSession */,
+                any() /* voiceInteractor */);
+    }
+
+    /**
      * Builder for creating new activities.
      */
     protected static class ActivityBuilder {
@@ -413,12 +428,18 @@
         ActivityStackSupervisor mTestStackSupervisor;
 
         ActivityDisplay mDefaultDisplay;
+        AppOpsService mAppOpsService;
 
         TestActivityTaskManagerService(Context context) {
             super(context);
             spyOn(this);
 
             mUgmInternal = mock(UriGrantsManagerInternal.class);
+            mAppOpsService = mock(AppOpsService.class);
+
+            // Make sure permission checks aren't overridden.
+            doReturn(AppOpsManager.MODE_DEFAULT)
+                    .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString());
 
             mSupportsMultiWindow = true;
             mSupportsMultiDisplay = true;
@@ -482,6 +503,11 @@
         }
 
         @Override
+        AppOpsService getAppOpsService() {
+            return mAppOpsService;
+        }
+
+        @Override
         void updateCpuStats() {
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 2d1dd39..20379a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -105,6 +105,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void testTransitWithinTask() {
         synchronized (mWm.mGlobalLock) {
             final AppWindowToken opening = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 9cdea9a..5fc7f44 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -65,6 +65,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void testKeyguardOverride() {
         mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
         mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
@@ -72,6 +73,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void testKeyguardKeep() {
         mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
         mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
@@ -172,6 +174,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void testLoadAnimationSafely() {
         DisplayContent dc = createNewDisplay(Display.STATE_ON);
         assertNull(dc.mAppTransition.loadAnimationSafely(
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index 623559e..70d9b5f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -30,6 +30,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
@@ -65,6 +66,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void clipAfterAnim_boundsLayerIsCreated() {
         mToken.mNeedsAnimationBoundsLayer = true;
 
@@ -86,6 +88,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void clipAfterAnim_boundsLayerIsDestroyed() {
         mToken.mNeedsAnimationBoundsLayer = true;
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
@@ -116,6 +119,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void clipNoneAnim_boundsLayerIsNotCreated() {
         mToken.mNeedsAnimationBoundsLayer = false;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 064b553..fe45411 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -150,6 +150,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 131005232)
     public void testLandscapeSeascapeRotationByApp() {
         // Some plumbing to get the service ready for rotation updates.
         mWm.mDisplayReady = true;
@@ -504,14 +505,14 @@
 
     private void assertHasStartingWindow(AppWindowToken atoken) {
         assertNotNull(atoken.startingSurface);
-        assertNotNull(atoken.startingData);
+        assertNotNull(atoken.mStartingData);
         assertNotNull(atoken.startingWindow);
     }
 
     private void assertNoStartingWindow(AppWindowToken atoken) {
         assertNull(atoken.startingSurface);
         assertNull(atoken.startingWindow);
-        assertNull(atoken.startingData);
+        assertNull(atoken.mStartingData);
         atoken.forAllWindows(windowState -> {
             assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
         }, true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index bac1ecd..0f7b35c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -25,7 +25,6 @@
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -62,6 +61,7 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.app.ResolverActivity;
+import com.android.server.wm.ActivityStack.ActivityState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -71,10 +71,10 @@
 import java.util.List;
 
 /**
- * Tests for the {@link ActivityStackSupervisor} class.
+ * Tests for the {@link RootActivityContainer} class.
  *
  * Build/Install/Run:
- *  atest WmTests:ActivityStackSupervisorTests
+ *  atest WmTests:RootActivityContainerTests
  */
 @MediumTest
 @Presubmit
@@ -395,21 +395,65 @@
     }
 
     /**
+     * Verify that a lingering transition is being executed in case the activity to be resumed is
+     * already resumed
+     */
+    @Test
+    public void testResumeActivityLingeringTransition() {
+        // Create a stack at top.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+        activity.setState(ActivityState.RESUMED, "test");
+
+        // Assume the stack is at the topmost position
+        assertTrue(targetStack.isTopStackOnDisplay());
+
+        // Use the stack as target to resume.
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+        // Verify the lingering app transition is being executed because it's already resumed
+        verify(targetStack, times(1)).executeAppTransition(any());
+    }
+
+    @Test
+    public void testResumeActivityLingeringTransition_notExecuted() {
+        // Create a stack at bottom.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+        activity.setState(ActivityState.RESUMED, "test");
+        display.positionChildAtBottom(targetStack);
+
+        // Assume the stack is at the topmost position
+        assertFalse(targetStack.isTopStackOnDisplay());
+        doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
+
+        // Use the stack as target to resume.
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+        // Verify the lingering app transition is being executed because it's already resumed
+        verify(targetStack, never()).executeAppTransition(any());
+    }
+
+    /**
      * Tests that home activities can be started on the displays that supports system decorations.
      */
     @Test
     public void testStartHomeOnAllDisplays() {
+        mockResolveHomeActivity();
+
         // Create secondary displays.
         final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
         mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
         doReturn(true).when(secondDisplay).supportsSystemDecorations();
 
         // Create mock tasks and other necessary mocks.
-        TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
-        final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-        doAnswer(i -> taskBuilder.build()).when(factory)
-                .create(any(), anyInt(), any(), any(), any(), any());
+        mockTaskRecordFactory();
         doReturn(true).when(mRootActivityContainer)
                 .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
         doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
@@ -510,6 +554,26 @@
     }
 
     /**
+     * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+     * activity type (in a new stack) so the order of back stack won't be broken.
+     */
+    @Test
+    public void testStartResolverActivityForHome() {
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.packageName = "android";
+        info.name = ResolverActivity.class.getName();
+        doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any());
+        mockTaskRecordFactory();
+
+        mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
+        final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
+
+        assertEquals(info, resolverActivity.info);
+        assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType());
+    }
+
+    /**
      * Tests that secondary home should be selected if default home not set.
      */
     @Test
@@ -542,13 +606,7 @@
      */
     @Test
     public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
-        final Intent defaultHomeIntent = mService.getHomeIntent();
-        final ActivityInfo aInfoDefault = new ActivityInfo();
-        aInfoDefault.name = "fakeHomeActivity";
-        aInfoDefault.applicationInfo = new ApplicationInfo();
-        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
-        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
-                refEq(defaultHomeIntent));
+        mockResolveHomeActivity();
 
         final List<ResolveInfo> resolutions = new ArrayList<>();
         doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
@@ -575,13 +633,7 @@
      */
     @Test
     public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
-        final Intent homeIntent = mService.getHomeIntent();
-        final ActivityInfo aInfoDefault = new ActivityInfo();
-        aInfoDefault.name = "fakeHomeActivity";
-        aInfoDefault.applicationInfo = new ApplicationInfo();
-        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
-        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
-                refEq(homeIntent));
+        final ActivityInfo aInfoDefault = mockResolveHomeActivity();
 
         final List<ResolveInfo> resolutions = new ArrayList<>();
         final ResolveInfo infoFake1 = new ResolveInfo();
@@ -612,13 +664,7 @@
      */
     @Test
     public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
-        final Intent homeIntent = mService.getHomeIntent();
-        final ActivityInfo aInfoDefault = new ActivityInfo();
-        aInfoDefault.name = "fakeHomeActivity";
-        aInfoDefault.applicationInfo = new ApplicationInfo();
-        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
-        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
-                refEq(homeIntent));
+        mockResolveHomeActivity();
 
         final List<ResolveInfo> resolutions = new ArrayList<>();
         final ResolveInfo infoFake1 = new ResolveInfo();
@@ -646,4 +692,19 @@
                 resolvedInfo.first.applicationInfo.packageName);
         assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
     }
+
+    /**
+     * Mock {@link RootActivityContainerTests#resolveHomeActivity} for returning consistent activity
+     * info for test cases (the original implementation will resolve from the real package manager).
+     */
+    private ActivityInfo mockResolveHomeActivity() {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = "fakeHomeActivity";
+        aInfoDefault.applicationInfo = new ApplicationInfo();
+        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(homeIntent));
+        return aInfoDefault;
+    }
 }
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 715353e..3a8d3b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -476,4 +476,47 @@
         root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
         verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
     }
+
+    @Test
+    public void testVisibilityChangeSwitchUser() {
+        final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
+        window.mHasSurface = true;
+        window.setShowToOwnerOnlyLocked(true);
+
+        mWm.mCurrentUserId = 1;
+        window.switchUser();
+        assertFalse(window.isVisible());
+        assertFalse(window.isVisibleByPolicy());
+
+        mWm.mCurrentUserId = 0;
+        window.switchUser();
+        assertTrue(window.isVisible());
+        assertTrue(window.isVisibleByPolicy());
+    }
+
+    @Test
+    public void testGetTransformationMatrix() {
+        synchronized (mWm.mGlobalLock) {
+            final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+            win0.getFrameLw().offsetTo(1, 0);
+
+            final DisplayContent dc = createNewDisplay();
+            dc.reparentDisplayContent(win0, win0.getSurfaceControl());
+            dc.updateLocation(win0, 2, 0);
+
+            final float[] values = new float[9];
+            final Matrix matrix = new Matrix();
+            final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+            final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
+            win1.mHasSurface = true;
+            win1.mSurfaceControl = mock(SurfaceControl.class);
+            win1.getFrameLw().offsetTo(3, 0);
+            win1.updateSurfacePosition(t);
+            win1.getTransformationMatrix(values, matrix);
+
+            matrix.getValues(values);
+            assertEquals(6f, values[Matrix.MTRANS_X], 0f);
+            assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
+        }
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4b33e16..7786627 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,6 +27,7 @@
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -238,6 +239,11 @@
     long mUnexemptedSyncScheduledTimeoutMillis;
     /** Maximum time a system interaction should keep the buckets elevated. */
     long mSystemInteractionTimeoutMillis;
+    /**
+     * Maximum time a foreground service start should keep the buckets elevated if the service
+     * start is the first usage of the app
+     */
+    long mInitialForegroundServiceStartTimeoutMillis;
     /** The length of time phone must be charging before considered stable enough to run jobs  */
     long mStableChargingThresholdMillis;
 
@@ -877,7 +883,8 @@
                     || event.mEventType == UsageEvents.Event.USER_INTERACTION
                     || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
                     || event.mEventType == UsageEvents.Event.SLICE_PINNED
-                    || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) {
+                    || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV
+                    || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
 
                 final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
                         event.mPackage, userId, elapsedRealtime);
@@ -898,6 +905,13 @@
                             STANDBY_BUCKET_ACTIVE, subReason,
                             0, elapsedRealtime + mSystemInteractionTimeoutMillis);
                     nextCheckTime = mSystemInteractionTimeoutMillis;
+                } else if (event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
+                    // Only elevate bucket if this is the first usage of the app
+                    if (prevBucket != STANDBY_BUCKET_NEVER) return;
+                    mAppIdleHistory.reportUsage(appHistory, event.mPackage,
+                            STANDBY_BUCKET_ACTIVE, subReason,
+                            0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
+                    nextCheckTime = mInitialForegroundServiceStartTimeoutMillis;
                 } else {
                     mAppIdleHistory.reportUsage(appHistory, event.mPackage,
                             STANDBY_BUCKET_ACTIVE, subReason,
@@ -930,6 +944,8 @@
             case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
             case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
             case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
+            case UsageEvents.Event.FOREGROUND_SERVICE_START:
+                return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
             default: return 0;
         }
     }
@@ -1509,6 +1525,9 @@
         pw.print("  mSystemInteractionTimeoutMillis=");
         TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
         pw.println();
+        pw.print("  mInitialForegroundServiceStartTimeoutMillis=");
+        TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw);
+        pw.println();
 
         pw.print("  mPredictionTimeoutMillis=");
         TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
@@ -1848,6 +1867,8 @@
                 "unexempted_sync_scheduled_duration";
         private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
                 "system_interaction_duration";
+        private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
+                "initial_foreground_service_start_duration";
         private static final String KEY_STABLE_CHARGING_THRESHOLD = "stable_charging_threshold";
         public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
         public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
@@ -1859,6 +1880,7 @@
         public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
         public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
         public static final long DEFAULT_STABLE_CHARGING_THRESHOLD = 10 * ONE_MINUTE;
+        public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
 
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -1964,6 +1986,12 @@
                 mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
                         KEY_SYSTEM_INTERACTION_HOLD_DURATION,
                                 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
+
+                mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis(
+                        KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
+                        COMPRESS_TIME ? ONE_MINUTE :
+                                DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
+
                 mStableChargingThresholdMillis = mParser.getDurationMillis(
                         KEY_STABLE_CHARGING_THRESHOLD,
                                 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index a814c03..5cd46ca 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -301,4 +301,9 @@
 
     void setTestAutoModeApp(String packageName);
 
+    /**
+     * @see TelecomServiceImpl#setTestDefaultDialer
+     */
+    void setTestDefaultDialer(in String packageName);
+
 }
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 785d7ae..9a8d7cd 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -140,6 +140,10 @@
         return mIsEuicc;
     }
 
+    /**
+     * Returns the ICCID of a the UICC in the given slot, or the EID if it is an eUICC. Note that if
+     * the value is unavailble this will return null.
+     */
     public String getCardId() {
         return mCardId;
     }
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index e556b0a..1932871 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -29,6 +29,14 @@
 }
 
 android_test_helper_app {
+    name: "RollbackTestAppAv3",
+    manifest: "TestApp/Av3.xml",
+    sdk_version: "current",
+    srcs: ["TestApp/src/**/*.java"],
+    resource_dirs: ["TestApp/res_v3"],
+}
+
+android_test_helper_app {
     name: "RollbackTestAppACrashingV2",
     manifest: "TestApp/ACrashingV2.xml",
     sdk_version: "current",
@@ -118,6 +126,7 @@
     java_resources: [
         ":RollbackTestAppAv1",
         ":RollbackTestAppAv2",
+        ":RollbackTestAppAv3",
         ":RollbackTestAppACrashingV2",
         ":RollbackTestAppBv1",
         ":RollbackTestAppBv2",
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index f9304f2..13b5b9a 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -775,6 +775,42 @@
         }
     }
 
+    /**
+     * Test failure to enable rollback for multi-package installs.
+     * If any one of the packages fail to enable rollback, we shouldn't enable
+     * rollback for any package.
+     */
+    @Test
+    public void testMultiPackageEnableFail() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.TEST_MANAGE_ROLLBACKS);
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.uninstall(TEST_APP_B);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+
+            // We should fail to enable rollback here because TestApp B is not
+            // already installed.
+            RollbackTestUtils.installMultiPackage(true,
+                    "RollbackTestAppAv2.apk",
+                    "RollbackTestAppBv2.apk");
+
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            assertNull(getUniqueRollbackInfoForPackage(
+                    rm.getAvailableRollbacks(), TEST_APP_A));
+            assertNull(getUniqueRollbackInfoForPackage(
+                    rm.getAvailableRollbacks(), TEST_APP_B));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
     @Test
     @Ignore("b/120200473")
     /**
@@ -871,6 +907,51 @@
         }
     }
 
+    /**
+     * Test race between roll back and roll forward.
+     */
+    @Test
+    public void testRollForwardRace() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.TEST_MANAGE_ROLLBACKS,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+                    rm.getAvailableRollbacks(), TEST_APP_A);
+            assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+
+            // Install a new version of package A, then immediately rollback
+            // the previous version. We expect the rollback to fail, because
+            // it is no longer available.
+            // There are a couple different ways this could fail depending on
+            // thread interleaving, so don't ignore flaky failures.
+            RollbackTestUtils.install("RollbackTestAppAv3.apk", false);
+            try {
+                RollbackTestUtils.rollback(rollback.getRollbackId());
+                // Note: Don't ignore flaky failures here.
+                fail("Expected rollback to fail, but it did not.");
+            } catch (AssertionError e) {
+                Log.i(TAG, "Note expected failure: ", e);
+                // Expected
+            }
+
+            // Note: Don't ignore flaky failures here.
+            assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
     // Helper function to test that the given rollback info is a rollback for
     // the atomic set {A2, B2} -> {A1, B1}.
     private void assertRollbackInfoForAandB(RollbackInfo rollback) {
diff --git a/tests/RollbackTest/TestApp/Av3.xml b/tests/RollbackTest/TestApp/Av3.xml
new file mode 100644
index 0000000..9725c9f7
--- /dev/null
+++ b/tests/RollbackTest/TestApp/Av3.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.rollback.testapp.A"
+    android:versionCode="3"
+    android:versionName="3.0" >
+
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <application android:label="Rollback Test App A v3">
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
+        <activity android:name="com.android.tests.rollback.testapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml
new file mode 100644
index 0000000..f2d8992
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <integer name="split_version">3</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v3/values/values.xml b/tests/RollbackTest/TestApp/res_v3/values/values.xml
new file mode 100644
index 0000000..968168a
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v3/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <integer name="app_version">3</integer>
+    <integer name="split_version">0</integer>
+</resources>
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index 1a91f52..d80c2e7 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -290,8 +290,14 @@
                 m_currentAction->target->name.c_str(), className.c_str(),
                 testName.c_str(), g_escapeEndColor);
 
-        string stack = get_bundle_string(results, &found, "stack", NULL);
-        if (found) {
+        bool stackFound;
+        string stack = get_bundle_string(results, &stackFound, "stack", NULL);
+        if (status.has_logcat()) {
+            const string logcat = status.logcat();
+            if (logcat.length() > 0) {
+                printf("%s\n", logcat.c_str());
+            }
+        } else if (stackFound) {
             printf("%s\n", stack.c_str());
         }
     }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index be227e7..ed41642 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1906,7 +1906,8 @@
         if (this.meteredOverride != METERED_OVERRIDE_NONE) {
             sbuf.append(" meteredOverride ").append(meteredOverride).append("\n");
         }
-        sbuf.append(" macRandomizationSetting ").append(macRandomizationSetting).append("\n");
+        sbuf.append(" macRandomizationSetting: ").append(macRandomizationSetting).append("\n");
+        sbuf.append(" mRandomizedMacAddress: ").append(mRandomizedMacAddress).append("\n");
         sbuf.append(" KeyMgmt:");
         for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
             if (this.allowedKeyManagement.get(k)) {