Merge "Add jank test for IME animations" into rvc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f8b2f32..ac58f3d 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1224,7 +1224,7 @@
                 IDLE_FACTOR = mParser.getFloat(KEY_IDLE_FACTOR,
                         2f);
                 MIN_TIME_TO_ALARM = mParser.getDurationMillis(KEY_MIN_TIME_TO_ALARM,
-                        !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);
+                        !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L);
                 MAX_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_MAX_TEMP_APP_WHITELIST_DURATION, 5 * 60 * 1000L);
                 MMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 07a9908..2aa2275 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2182,17 +2182,18 @@
         }
 
         final boolean jobExists = mJobs.containsJob(job);
-
         final boolean userStarted = areUsersStartedLocked(job);
+        final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
 
         if (DEBUG) {
             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
-                    + " exists=" + jobExists + " userStarted=" + userStarted);
+                    + " exists=" + jobExists + " userStarted=" + userStarted
+                    + " backingUp=" + backingUp);
         }
 
         // These are also fairly cheap to check, though they typically will not
         // be conditions we fail.
-        if (!jobExists || !userStarted) {
+        if (!jobExists || !userStarted || backingUp) {
             return false;
         }
 
@@ -2265,15 +2266,17 @@
 
         final boolean jobExists = mJobs.containsJob(job);
         final boolean userStarted = areUsersStartedLocked(job);
+        final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
 
         if (DEBUG) {
             Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
-                    + " exists=" + jobExists + " userStarted=" + userStarted);
+                    + " exists=" + jobExists + " userStarted=" + userStarted
+                    + " backingUp=" + backingUp);
         }
 
         // These are also fairly cheap to check, though they typically will not
         // be conditions we fail.
-        if (!jobExists || !userStarted) {
+        if (!jobExists || !userStarted || backingUp) {
             return false;
         }
 
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index b3c82bc..073fddf 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -850,7 +850,7 @@
     private final InputReadingDataSource mDataSource;
     private final DataReaderAdapter mScratchDataReaderAdapter;
     private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
-    private String mExtractorName;
+    private String mParserName;
     private Extractor mExtractor;
     private ExtractorInput mExtractorInput;
     private long mPendingSeekPosition;
@@ -924,7 +924,7 @@
     @NonNull
     @ParserName
     public String getParserName() {
-        return mExtractorName;
+        return mParserName;
     }
 
     /**
@@ -958,15 +958,15 @@
 
         // TODO: Apply parameters when creating extractor instances.
         if (mExtractor == null) {
-            if (!mExtractorName.equals(PARSER_NAME_UNKNOWN)) {
-                mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
+            if (!mParserName.equals(PARSER_NAME_UNKNOWN)) {
+                mExtractor = createExtractor(mParserName);
                 mExtractor.init(new ExtractorOutputAdapter());
             } else {
                 for (String parserName : mParserNamesPool) {
                     Extractor extractor = createExtractor(parserName);
                     try {
                         if (extractor.sniff(mExtractorInput)) {
-                            mExtractorName = parserName;
+                            mParserName = parserName;
                             mExtractor = extractor;
                             mExtractor.init(new ExtractorOutputAdapter());
                             break;
@@ -1044,7 +1044,7 @@
         mParserParameters = new HashMap<>();
         mOutputConsumer = outputConsumer;
         mParserNamesPool = parserNamesPool;
-        mExtractorName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0];
+        mParserName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0];
         mPositionHolder = new PositionHolder();
         mDataSource = new InputReadingDataSource();
         removePendingSeek();
@@ -1090,7 +1090,7 @@
                         getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
                                 ? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
                                 : 0;
-                return new Mp4Extractor();
+                return new Mp4Extractor(flags);
             case PARSER_NAME_MP3:
                 flags |=
                         getBooleanParameter(PARAMETER_MP3_DISABLE_ID3)
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 6e8ceb7..b4519b7 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -291,7 +291,14 @@
 cc_test {
     name: "statsd_test",
     defaults: ["statsd_defaults"],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "mts"],
+
+    //TODO(b/153588990): Remove when the build system properly separates 
+    //32bit and 64bit architectures.
+    multilib: {
+        lib32: { suffix: "32", },
+        lib64: { suffix: "64", },
+    },
 
     cflags: [
         "-Wall",
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 92e09ea..e251399 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -181,6 +181,7 @@
 
         return false;
     }
+
     bool matches(const Matcher& that) const;
 };
 
@@ -360,7 +361,9 @@
 
 class Annotations {
 public:
-    Annotations() {}
+    Annotations() {
+        setNested(true);  // Nested = true by default
+    }
 
     // This enum stores where particular annotations can be found in the
     // bitmask. Note that these pos do not correspond to annotation ids.
@@ -379,7 +382,9 @@
 
     inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
 
-    inline void setResetState(int resetState) { mResetState = resetState; }
+    inline void setResetState(int32_t resetState) {
+        mResetState = resetState;
+    }
 
     // Default value = false
     inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
@@ -395,7 +400,9 @@
 
     // If a reset state is not sent in the StatsEvent, returns -1. Note that a
     // reset satate is only sent if and only if a reset should be triggered.
-    inline int getResetState() const { return mResetState; }
+    inline int32_t getResetState() const {
+        return mResetState;
+    }
 
 private:
     inline void setBitmaskAtPos(int pos, bool value) {
@@ -411,7 +418,7 @@
     // there are only 4 booleans, just one byte is required.
     uint8_t mBooleanBitmask = 0;
 
-    int mResetState = -1;
+    int32_t mResetState = -1;
 };
 
 /**
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 29249f4..eba66e0 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -180,6 +180,23 @@
     return num_matches > 0;
 }
 
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) {
+    size_t num_matches = 0;
+    const int32_t simpleFieldMask = 0xff7f0000;
+    const int32_t attributionUidFieldMask = 0xff7f7f7f;
+    for (const auto& value : values) {
+        if (value.mAnnotations.isPrimaryField()) {
+            output->addValue(value);
+            output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
+            const int32_t mask =
+                    isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask;
+            output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask);
+            num_matches++;
+        }
+    }
+    return num_matches > 0;
+}
+
 void filterGaugeValues(const std::vector<Matcher>& matcherFields,
                        const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
     for (const auto& field : matcherFields) {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 33a5024..bd01100 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -154,6 +154,18 @@
                   HashableDimensionKey* output);
 
 /**
+ * Creating HashableDimensionKeys from State Primary Keys in FieldValues.
+ *
+ * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
+ * in it. This is because: for example, when we create dimension from last uid in attribution chain,
+ * In one event, uid 1000 is at position 5 and it's the last
+ * In another event, uid 1000 is at position 6, and it's the last
+ * these 2 events should be mapped to the same dimension.  So we will remove the original position
+ * from the dimension key for the uid field (by applying 0x80 bit mask).
+ */
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output);
+
+/**
  * Filter the values from FieldValues using the matchers.
  *
  * In contrast to the above function, this function will not do any modification to the original
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 8b6a864..61cd017 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -293,7 +293,8 @@
     }
 
     const bool exclusiveState = readNextValue<uint8_t>();
-    mValues[mValues.size() - 1].mAnnotations.setExclusiveState(exclusiveState);
+    mExclusiveStateFieldIndex = mValues.size() - 1;
+    mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
 }
 
 void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 4eeb7d6..41fdcc2 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -145,7 +145,7 @@
     }
 
     // Default value = false
-    inline bool shouldTruncateTimestamp() {
+    inline bool shouldTruncateTimestamp() const {
         return mTruncateTimestamp;
     }
 
@@ -170,6 +170,20 @@
         return mAttributionChainIndex;
     }
 
+    // Returns the index of the exclusive state field within the FieldValues vector if
+    // an exclusive state exists. If there is no exclusive state field, returns -1.
+    //
+    // If the index within the atom definition is desired, do the following:
+    //    int vectorIndex = LogEvent.getExclusiveStateFieldIndex();
+    //    if (vectorIndex != -1) {
+    //        FieldValue& v = LogEvent.getValues()[vectorIndex];
+    //        int atomIndex = v.mField.getPosAtDepth(0);
+    //    }
+    // Note that atomIndex is 1-indexed.
+    inline int getExclusiveStateFieldIndex() const {
+        return mExclusiveStateFieldIndex;
+    }
+
     inline LogEvent makeCopy() {
         return LogEvent(*this);
     }
@@ -297,6 +311,7 @@
     bool mTruncateTimestamp = false;
     int mUidFieldIndex = -1;
     int mAttributionChainIndex = -1;
+    int mExclusiveStateFieldIndex = -1;
 };
 
 void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index d79b6a2..e394533 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -38,20 +38,27 @@
 
 shared_ptr<StatsService> gStatsService = nullptr;
 
-void sigHandler(int sig) {
-    if (gStatsService != nullptr) {
-        gStatsService->Terminate();
+void signalHandler(int sig) {
+    if (sig == SIGPIPE) {
+        // ShellSubscriber uses SIGPIPE as a signal to detect the end of the
+        // client process. Don't prematurely exit(1) here. Instead, ignore the
+        // signal and allow the write call to return EPIPE.
+        ALOGI("statsd received SIGPIPE. Ignoring signal.");
+        return;
     }
+
+    if (gStatsService != nullptr) gStatsService->Terminate();
     ALOGW("statsd terminated on receiving signal %d.", sig);
     exit(1);
 }
 
-void registerSigHandler()
+void registerSignalHandlers()
 {
     struct sigaction sa;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = 0;
-    sa.sa_handler = sigHandler;
+    sa.sa_handler = signalHandler;
+    sigaction(SIGPIPE, &sa, nullptr);
     sigaction(SIGHUP, &sa, nullptr);
     sigaction(SIGINT, &sa, nullptr);
     sigaction(SIGQUIT, &sa, nullptr);
@@ -79,7 +86,7 @@
         return -1;
     }
 
-    registerSigHandler();
+    registerSignalHandlers();
 
     gStatsService->sayHiToStatsCompanion();
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 6833f8d..d68f64a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -151,8 +151,7 @@
 
     uint64_t wrapperToken =
             mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
-    const int64_t elapsedTimeNs = truncateTimestampIfNecessary(
-            event.GetTagId(), event.GetElapsedTimestampNs());
+    const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs);
 
     uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 42bbd8e..c4bd054 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -270,11 +270,9 @@
                     protoOutput->end(atomsToken);
                 }
                 for (const auto& atom : bucket.mGaugeAtoms) {
-                    const int64_t elapsedTimestampNs =
-                            truncateTimestampIfNecessary(mAtomId, atom.mElapsedTimestamps);
-                    protoOutput->write(
-                        FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
-                        (long long)elapsedTimestampNs);
+                    protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
+                                               FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
+                                       (long long)atom.mElapsedTimestampNs);
                 }
             }
             protoOutput->end(bucketInfoToken);
@@ -477,7 +475,9 @@
     if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
         return;
     }
-    GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
+
+    const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event);
+    GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs);
     (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
     // Anomaly detection on gauge metric only works when there is one numeric
     // field specified.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 79ec711..aa0cae2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -35,10 +35,10 @@
 
 struct GaugeAtom {
     GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
-        : mFields(fields), mElapsedTimestamps(elapsedTimeNs) {
+        : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
     }
     std::shared_ptr<vector<FieldValue>> mFields;
-    int64_t mElapsedTimestamps;
+    int64_t mElapsedTimestampNs;
 };
 
 struct GaugeBucket {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 4550e65..6aba13ca 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -451,7 +451,7 @@
     std::vector<int32_t> mSlicedStateAtoms;
 
     // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
-    std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
+    const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
 
     // MetricStateLinks defined in statsd_config that link fields in the state
     // atom to fields in the "what" atom.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 2fcb13b..88616dd 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -795,9 +795,7 @@
     for (const auto& it : allMetricProducers) {
         // Register metrics to StateTrackers
         for (int atomId : it->getSlicedStateAtoms()) {
-            if (!StateManager::getInstance().registerListener(atomId, it)) {
-                return false;
-            }
+            StateManager::getInstance().registerListener(atomId, it);
         }
     }
     return true;
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index ea776fa..5514b446 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -38,20 +38,12 @@
     }
 }
 
-bool StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
+void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
     // Check if state tracker already exists.
     if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
-        // Create a new state tracker iff atom is a state atom.
-        auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
-        if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
-            mStateTrackers[atomId] = new StateTracker(atomId, it->second);
-        } else {
-            ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId);
-            return false;
-        }
+        mStateTrackers[atomId] = new StateTracker(atomId);
     }
     mStateTrackers[atomId]->registerListener(listener);
-    return true;
 }
 
 void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index 8b3a421..577a0f5 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -45,10 +45,11 @@
     // Notifies the correct StateTracker of an event.
     void onLogEvent(const LogEvent& event);
 
-    // Returns true if atomId is being tracked and is associated with a state
-    // atom. StateManager notifies the correct StateTracker to register listener.
+    // Notifies the StateTracker for the given atomId to register listener.
     // If the correct StateTracker does not exist, a new StateTracker is created.
-    bool registerListener(const int32_t atomId, wp<StateListener> listener);
+    // Note: StateTrackers can be created for non-state atoms. They are essentially empty and
+    // do not perform any actions.
+    void registerListener(const int32_t atomId, wp<StateListener> listener);
 
     // Notifies the correct StateTracker to unregister a listener
     // and removes the tracker if it no longer has any listeners.
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index ab86127..b7f314a 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -25,81 +25,43 @@
 namespace os {
 namespace statsd {
 
-StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo)
-    : mAtomId(atomId),
-      mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)),
-      mNested(stateAtomInfo.nested) {
-    // create matcher for each primary field
-    for (const auto& primaryField : stateAtomInfo.primaryFields) {
-        if (primaryField == util::FIRST_UID_IN_CHAIN) {
-            Matcher matcher = getFirstUidMatcher(atomId);
-            mPrimaryFields.push_back(matcher);
-        } else {
-            Matcher matcher = getSimpleMatcher(atomId, primaryField);
-            mPrimaryFields.push_back(matcher);
-        }
-    }
-
-    if (stateAtomInfo.defaultState != util::UNSET_VALUE) {
-        mDefaultState = stateAtomInfo.defaultState;
-    }
-
-    if (stateAtomInfo.resetState != util::UNSET_VALUE) {
-        mResetState = stateAtomInfo.resetState;
-    }
+StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
 }
 
 void StateTracker::onLogEvent(const LogEvent& event) {
-    int64_t eventTimeNs = event.GetElapsedTimestampNs();
+    const int64_t eventTimeNs = event.GetElapsedTimestampNs();
 
     // Parse event for primary field values i.e. primary key.
     HashableDimensionKey primaryKey;
-    if (mPrimaryFields.size() > 0) {
-        if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) ||
-            primaryKey.getValues().size() != mPrimaryFields.size()) {
-            ALOGE("StateTracker error extracting primary key from log event.");
-            handleReset(eventTimeNs);
-            return;
-        }
-    } else {
-        // Use an empty HashableDimensionKey if atom has no primary fields.
-        primaryKey = DEFAULT_DIMENSION_KEY;
+    filterPrimaryKey(event.getValues(), &primaryKey);
+
+    FieldValue stateValue;
+    if (!getStateFieldValueFromLogEvent(event, &stateValue)) {
+        ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
+        clearStateForPrimaryKey(eventTimeNs, primaryKey);
+        return;
     }
 
-    // Parse event for state value.
-    FieldValue stateValue;
-    if (!filterValues(mStateField, event.getValues(), &stateValue) ||
-        stateValue.mValue.getType() != INT) {
+    mField.setField(stateValue.mField.getField());
+
+    if (stateValue.mValue.getType() != INT) {
         ALOGE("StateTracker error extracting state from log event. Type: %d",
               stateValue.mValue.getType());
-        handlePartialReset(eventTimeNs, primaryKey);
+        clearStateForPrimaryKey(eventTimeNs, primaryKey);
         return;
     }
 
-    int32_t state = stateValue.mValue.int_value;
-    if (state == mResetState) {
-        VLOG("StateTracker Reset state: %s", stateValue.mValue.toString().c_str());
-        handleReset(eventTimeNs);
+    const int32_t resetState = stateValue.mAnnotations.getResetState();
+    if (resetState != -1) {
+        VLOG("StateTracker new reset state: %d", resetState);
+        handleReset(eventTimeNs, resetState);
         return;
     }
 
-    // Track and update state.
-    int32_t oldState = 0;
-    int32_t newState = 0;
-    updateState(primaryKey, state, &oldState, &newState);
-
-    // Notify all listeners if state has changed.
-    if (oldState != newState) {
-        VLOG("StateTracker updated state");
-        for (auto listener : mListeners) {
-            auto sListener = listener.promote();  // safe access to wp<>
-            if (sListener != nullptr) {
-                sListener->onStateChanged(eventTimeNs, mAtomId, primaryKey, oldState, newState);
-            }
-        }
-    } else {
-        VLOG("StateTracker NO updated state");
-    }
+    const int32_t newState = stateValue.mValue.int_value;
+    const bool nested = stateValue.mAnnotations.isNested();
+    StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
+    updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
 }
 
 void StateTracker::registerListener(wp<StateListener> listener) {
@@ -111,81 +73,62 @@
 }
 
 bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
-    output->mField = mStateField.mMatcher;
+    output->mField = mField;
 
-    // Check that the query key has the correct number of primary fields.
-    if (queryKey.getValues().size() == mPrimaryFields.size()) {
-        auto it = mStateMap.find(queryKey);
-        if (it != mStateMap.end()) {
-            output->mValue = it->second.state;
-            return true;
-        }
-    } else if (queryKey.getValues().size() > mPrimaryFields.size()) {
-        ALOGE("StateTracker query key size %zu > primary key size %zu is illegal",
-              queryKey.getValues().size(), mPrimaryFields.size());
-    } else {
-        ALOGE("StateTracker query key size %zu < primary key size %zu is not supported",
-              queryKey.getValues().size(), mPrimaryFields.size());
+    if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
+        output->mValue = it->second.state;
+        return true;
     }
 
-    // Set the state value to default state if:
-    // - query key size is incorrect
-    // - query key is not found in state map
-    output->mValue = mDefaultState;
+    // Set the state value to kStateUnknown if query key is not found in state map.
+    output->mValue = kStateUnknown;
     return false;
 }
 
-void StateTracker::handleReset(const int64_t eventTimeNs) {
+void StateTracker::handleReset(const int64_t eventTimeNs, const int32_t newState) {
     VLOG("StateTracker handle reset");
-    for (const auto pair : mStateMap) {
-        for (auto l : mListeners) {
-            auto sl = l.promote();
-            if (sl != nullptr) {
-                sl->onStateChanged(eventTimeNs, mAtomId, pair.first, pair.second.state,
-                                   mDefaultState);
-            }
-        }
-    }
-    mStateMap.clear();
-}
-
-void StateTracker::handlePartialReset(const int64_t eventTimeNs,
-                                      const HashableDimensionKey& primaryKey) {
-    VLOG("StateTracker handle partial reset");
-    if (mStateMap.find(primaryKey) != mStateMap.end()) {
-        for (auto l : mListeners) {
-            auto sl = l.promote();
-            if (sl != nullptr) {
-                sl->onStateChanged(eventTimeNs, mAtomId, primaryKey,
-                                   mStateMap.find(primaryKey)->second.state, mDefaultState);
-            }
-        }
-        mStateMap.erase(primaryKey);
+    for (auto& [primaryKey, stateValueInfo] : mStateMap) {
+        updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
+                                 false /* nested; treat this state change as not nested */,
+                                 &stateValueInfo);
     }
 }
 
-void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
-                               int32_t* oldState, int32_t* newState) {
-    // get old state (either current state in map or default state)
-    auto it = mStateMap.find(primaryKey);
+void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
+                                           const HashableDimensionKey& primaryKey) {
+    VLOG("StateTracker clear state for primary key");
+    const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
+            mStateMap.find(primaryKey);
+
+    // If there is no entry for the primaryKey in mStateMap, then the state is already
+    // kStateUnknown.
     if (it != mStateMap.end()) {
-        *oldState = it->second.state;
-    } else {
-        *oldState = mDefaultState;
+        updateStateForPrimaryKey(eventTimeNs, primaryKey, kStateUnknown,
+                                 false /* nested; treat this state change as not nested */,
+                                 &it->second);
+    }
+}
+
+void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
+                                            const HashableDimensionKey& primaryKey,
+                                            const int32_t newState, const bool nested,
+                                            StateValueInfo* stateValueInfo) {
+    const int32_t oldState = stateValueInfo->state;
+
+    if (kStateUnknown == newState) {
+        mStateMap.erase(primaryKey);
     }
 
     // Update state map for non-nested counting case.
     // Every state event triggers a state overwrite.
-    if (!mNested) {
-        if (eventState == mDefaultState) {
-            // remove (key, state) pair if state returns to default state
-            VLOG("\t StateTracker changed to default state")
-            mStateMap.erase(primaryKey);
-        } else {
-            mStateMap[primaryKey].state = eventState;
-            mStateMap[primaryKey].count = 1;
+    if (!nested) {
+        stateValueInfo->state = newState;
+        stateValueInfo->count = 1;
+
+        // Notify listeners if state has changed.
+        if (oldState != newState) {
+            notifyListeners(eventTimeNs, primaryKey, oldState, newState);
         }
-        *newState = eventState;
         return;
     }
 
@@ -197,31 +140,47 @@
     // number of OFF events as ON events.
     //
     // In atoms.proto, a state atom with nested counting enabled
-    // must only have 2 states and one of the states must be the default state.
-    it = mStateMap.find(primaryKey);
-    if (it != mStateMap.end()) {
-        *newState = it->second.state;
-        if (eventState == it->second.state) {
-            it->second.count++;
-        } else if (eventState == mDefaultState) {
-            if ((--it->second.count) == 0) {
-                mStateMap.erase(primaryKey);
-                *newState = mDefaultState;
-            }
-        } else {
-            ALOGE("StateTracker Nest counting state has a third state instead of the binary state "
-                  "limit.");
-            return;
+    // must only have 2 states. There is no enforcemnt here of this requirement.
+    // The atom must be logged correctly.
+    if (kStateUnknown == newState) {
+        if (kStateUnknown != oldState) {
+            notifyListeners(eventTimeNs, primaryKey, oldState, newState);
         }
-    } else {
-        if (eventState != mDefaultState) {
-            mStateMap[primaryKey].state = eventState;
-            mStateMap[primaryKey].count = 1;
-        }
-        *newState = eventState;
+    } else if (oldState == kStateUnknown) {
+        stateValueInfo->state = newState;
+        stateValueInfo->count = 1;
+        notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+    } else if (oldState == newState) {
+        stateValueInfo->count++;
+    } else if (--stateValueInfo->count == 0) {
+        stateValueInfo->state = newState;
+        stateValueInfo->count = 1;
+        notifyListeners(eventTimeNs, primaryKey, oldState, newState);
     }
 }
 
+void StateTracker::notifyListeners(const int64_t eventTimeNs,
+                                   const HashableDimensionKey& primaryKey, const int32_t oldState,
+                                   const int32_t newState) {
+    for (auto l : mListeners) {
+        auto sl = l.promote();
+        if (sl != nullptr) {
+            sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
+        }
+    }
+}
+
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
+    const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
+    if (-1 == exclusiveStateFieldIndex) {
+        ALOGE("error extracting state from log event. Missing exclusive state field.");
+        return false;
+    }
+
+    *output = event.getValues()[exclusiveStateFieldIndex];
+    return true;
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index 154750e..c5f6315 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -15,7 +15,6 @@
  */
 #pragma once
 
-#include <atoms_info.h>
 #include <utils/RefBase.h>
 #include "HashableDimensionKey.h"
 #include "logd/LogEvent.h"
@@ -30,7 +29,7 @@
 
 class StateTracker : public virtual RefBase {
 public:
-    StateTracker(const int32_t atomId, const android::util::StateAtomFieldOptions& stateAtomInfo);
+    StateTracker(const int32_t atomId);
 
     virtual ~StateTracker(){};
 
@@ -60,21 +59,11 @@
 
 private:
     struct StateValueInfo {
-        int32_t state;  // state value
-        int count;      // nested count (only used for binary states)
+        int32_t state = kStateUnknown;  // state value
+        int count = 0;                  // nested count (only used for binary states)
     };
 
-    const int32_t mAtomId;  // id of the state atom being tracked
-
-    Matcher mStateField;  // matches the atom's exclusive state field
-
-    std::vector<Matcher> mPrimaryFields;  // matches the atom's primary fields
-
-    int32_t mDefaultState = kStateUnknown;
-
-    int32_t mResetState = kStateUnknown;
-
-    const bool mNested;
+    Field mField;
 
     // Maps primary key to state value info
     std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
@@ -82,20 +71,24 @@
     // Set of all StateListeners (objects listening for state changes)
     std::set<wp<StateListener>> mListeners;
 
-    // Reset all state values in map to default state.
-    void handleReset(const int64_t eventTimeNs);
+    // Reset all state values in map to the given state.
+    void handleReset(const int64_t eventTimeNs, const int32_t newState);
 
-    // Reset only the state value mapped to the given primary key to default state.
-    // Partial resets are used when we only need to update the state of one primary
-    // key instead of clearing/reseting every key in the map.
-    void handlePartialReset(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
+    // Clears the state value mapped to the given primary key by setting it to kStateUnknown.
+    void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
 
     // Update the StateMap based on the received state value.
-    // Store the old and new states.
-    void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
-                     int32_t* oldState, int32_t* newState);
+    void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+                                  const int32_t newState, const bool nested,
+                                  StateValueInfo* stateValueInfo);
+
+    // Notify registered state listeners of state change.
+    void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+                         const int32_t oldState, const int32_t newState);
 };
 
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 77a3eb3..5635313 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -549,14 +549,13 @@
     return time(nullptr) * MS_PER_SEC;
 }
 
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs) {
-    if (AtomsInfo::kTruncatingTimestampAtomBlackList.find(atomId) !=
-            AtomsInfo::kTruncatingTimestampAtomBlackList.end() ||
-        (atomId >= StatsdStats::kTimestampTruncationStartTag &&
-         atomId <= StatsdStats::kTimestampTruncationEndTag)) {
-        return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
+int64_t truncateTimestampIfNecessary(const LogEvent& event) {
+    if (event.shouldTruncateTimestamp() ||
+        (event.GetTagId() >= StatsdStats::kTimestampTruncationStartTag &&
+         event.GetTagId() <= StatsdStats::kTimestampTruncationEndTag)) {
+        return event.GetElapsedTimestampNs() / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
     } else {
-        return timestampNs;
+        return event.GetElapsedTimestampNs();
     }
 }
 
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index ade25d6..20d93b5 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -17,11 +17,12 @@
 #pragma once
 
 #include <android/util/ProtoOutputStream.h>
+
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
-#include "atoms_info.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
 
 using android::util::ProtoOutputStream;
 
@@ -93,9 +94,9 @@
     return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
 }
 
-// Checks the blacklist of atoms as well as the blacklisted range of 300,000 - 304,999.
+// Checks the truncate timestamp annotation as well as the blacklisted range of 300,000 - 304,999.
 // Returns the truncated timestamp to the nearest 5 minutes if needed.
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs);
+int64_t truncateTimestampIfNecessary(const LogEvent& event);
 
 // Checks permission for given pid and uid.
 bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid);
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
index a5da9c8..b1461a1 100644
--- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -232,7 +232,7 @@
     StateMap map = state.map();
     for (auto group : map.group()) {
         for (auto value : group.value()) {
-            EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+            EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
                       group.group_id());
         }
     }
@@ -614,7 +614,7 @@
     StateMap map = state1.map();
     for (auto group : map.group()) {
         for (auto value : group.value()) {
-            EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+            EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
                       group.group_id());
         }
     }
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index ba09a35..d59ec3e 100644
--- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -921,18 +921,18 @@
                bucket #1                      bucket #2
     |       1       2       3       4       5     6     7     8  (minutes)
     |---------------------------------------|------------------
-         ON                              OFF    ON             (BatterySaverMode)
+             ON                          OFF    ON             (BatterySaverMode)
                   T            F    T                          (DeviceUnpluggedPredicate)
-             |          |              |                       (ScreenIsOnEvent)
+         |              |              |                       (ScreenIsOnEvent)
                 |           |                       |          (ScreenIsOffEvent)
                                 |                              (ScreenDozeEvent)
     */
     // Initialize log events.
     std::vector<std::unique_ptr<LogEvent>> events;
-    events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC));  // 0:30
     events.push_back(CreateScreenStateChangedEvent(
-            bucketStartTimeNs + 60 * NS_PER_SEC,
-            android::view::DisplayStateEnum::DISPLAY_STATE_ON));  // 1:10
+            bucketStartTimeNs + 20 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON));                       // 0:30
+    events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 60 * NS_PER_SEC));  // 1:10
     events.push_back(CreateScreenStateChangedEvent(
             bucketStartTimeNs + 80 * NS_PER_SEC,
             android::view::DisplayStateEnum::DISPLAY_STATE_OFF));  // 1:30
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index 78c80bc..ba2a4cf 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -19,6 +19,7 @@
 
 #include "state/StateListener.h"
 #include "state/StateManager.h"
+#include "state/StateTracker.h"
 #include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
@@ -127,23 +128,23 @@
 
     // Register listener to non-existing StateTracker
     EXPECT_EQ(0, mgr.getStateTrackersCount());
-    EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1));
+    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
 
     // Register listener to existing StateTracker
-    EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2));
+    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
 
     // Register already registered listener to existing StateTracker
-    EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2));
+    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
 
     // Register listener to non-state atom
-    EXPECT_FALSE(mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2));
-    EXPECT_EQ(1, mgr.getStateTrackersCount());
+    mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2);
+    EXPECT_EQ(2, mgr.getStateTrackersCount());
 }
 
 /**
@@ -249,6 +250,9 @@
     EXPECT_EQ(1, listener->updates.size());
     EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
     EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+    FieldValue stateFieldValue;
+    mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
+    EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
     listener->updates.clear();
 
     std::unique_ptr<LogEvent> event2 =
@@ -258,6 +262,8 @@
     EXPECT_EQ(1, listener->updates.size());
     EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
     EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+    mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
+    EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
     listener->updates.clear();
 
     std::unique_ptr<LogEvent> event3 =
@@ -265,8 +271,12 @@
                                            BleScanStateChanged::RESET, false, false, false);
     mgr.onLogEvent(*event3);
     EXPECT_EQ(2, listener->updates.size());
-    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
-    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
+    for (const TestStateListener::Update& update : listener->updates) {
+        EXPECT_EQ(BleScanStateChanged::OFF, update.mState);
+
+        mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, update.mKey, &stateFieldValue);
+        EXPECT_EQ(BleScanStateChanged::OFF, stateFieldValue.mValue.int_value);
+    }
 }
 
 /**
@@ -352,13 +362,13 @@
     // No state stored for this query key.
     HashableDimensionKey queryKey2;
     getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
-    EXPECT_EQ(WakelockStateChanged::RELEASE,
+    EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
               getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
 
     // Partial query fails.
     HashableDimensionKey queryKey3;
     getPartialWakelockKey(1001 /* uid */, &queryKey3);
-    EXPECT_EQ(WakelockStateChanged::RELEASE,
+    EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
               getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
 }
 
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 687014f..7216e1d 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -569,6 +569,8 @@
     AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
     AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
 
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
     parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -662,9 +664,14 @@
     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
 
     writeAttribution(statsEvent, attributionUids, attributionTags);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
     AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
     AStatsEvent_writeString(statsEvent, wakelockName.c_str());
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
     AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
 
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
     parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -803,7 +810,11 @@
     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
 
     AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
     AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
 
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
     parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -821,10 +832,20 @@
     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
 
     writeAttribution(statsEvent, attributionUids, attributionTags);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
     AStatsEvent_writeInt32(statsEvent, state);
-    AStatsEvent_writeInt32(statsEvent, filtered);       // filtered
-    AStatsEvent_writeInt32(statsEvent, firstMatch);     // first match
-    AStatsEvent_writeInt32(statsEvent, opportunistic);  // opportunistic
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
+    if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) {
+        AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET,
+                                       util::BLE_SCAN_STATE_CHANGED__STATE__OFF);
+    }
+    AStatsEvent_writeBool(statsEvent, filtered);  // filtered
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+    AStatsEvent_writeBool(statsEvent, firstMatch);  // first match
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+    AStatsEvent_writeBool(statsEvent, opportunistic);  // opportunistic
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
 
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
     parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -840,9 +861,14 @@
     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
 
     AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
     AStatsEvent_writeString(statsEvent, packageName.c_str());
-    AStatsEvent_writeInt32(statsEvent, usingAlertWindow);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+    AStatsEvent_writeBool(statsEvent, usingAlertWindow);
     AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+    AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
 
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
     parseStatsEventToLogEvent(statsEvent, logEvent.get());
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index f216db6..29a98fa 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -980,16 +980,10 @@
                 @Override
                 protected Integer recompute(Void query) {
                     try {
-                        mServiceLock.readLock().lock();
-                        if (mService != null) {
-                            return mService.getState();
-                        }
+                        return mService.getState();
                     } catch (RemoteException e) {
-                        Log.e(TAG, "", e);
-                    } finally {
-                        mServiceLock.readLock().unlock();
+                        throw e.rethrowFromSystemServer();
                     }
-                    return BluetoothAdapter.STATE_OFF;
                 }
             };
 
@@ -1004,6 +998,30 @@
     }
 
     /**
+     * Fetch the current bluetooth state.  If the service is down, return
+     * OFF.
+     */
+    @AdapterState
+    private int getStateInternal() {
+        int state = BluetoothAdapter.STATE_OFF;
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                state = mBluetoothGetStateCache.query(null);
+            }
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof RemoteException) {
+                Log.e(TAG, "", e.getCause());
+            } else {
+                throw e;
+            }
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return state;
+    }
+
+    /**
      * Get the current state of the local Bluetooth adapter.
      * <p>Possible return values are
      * {@link #STATE_OFF},
@@ -1016,7 +1034,7 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @AdapterState
     public int getState() {
-        int state = mBluetoothGetStateCache.query(null);
+        int state = getStateInternal();
 
         // Consider all internal states as OFF
         if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
@@ -1054,7 +1072,7 @@
     @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
             + "whether you can use BLE & BT classic.")
     public int getLeState() {
-        int state = mBluetoothGetStateCache.query(null);
+        int state = getStateInternal();
 
         if (VDBG) {
             Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9ca2db9..d36d583 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3158,6 +3158,23 @@
             "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE";
 
     /**
+     * Extra field name for the Merkle tree root hash of a package.
+     * <p>Passed to a package verifier both prior to verification and as a result
+     * of verification.
+     * <p>The value of the extra is a specially formatted list:
+     * {@code filename1:HASH_1;filename2:HASH_2;...;filenameN:HASH_N}
+     * <p>The extra must include an entry for every APK within an installation. If
+     * a hash is not physically present, a hash value of {@code 0} will be used.
+     * <p>The root hash is generated using SHA-256, no salt with a 4096 byte block
+     * size. See the description of the
+     * <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#merkle-tree">fs-verity merkle-tree</a>
+     * for more details.
+     * @hide
+     */
+    public static final String EXTRA_VERIFICATION_ROOT_HASH =
+            "android.content.pm.extra.EXTRA_VERIFICATION_ROOT_HASH";
+
+    /**
      * Extra field name for the ID of a intent filter pending verification.
      * Passed to an intent filter verifier and is used to call back to
      * {@link #verifyIntentFilter}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 4c95532..2ee0ad6 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -34,6 +34,7 @@
 import android.content.pm.parsing.component.ParsedService;
 import android.os.Bundle;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import java.security.PublicKey;
 import java.util.Map;
@@ -258,6 +259,8 @@
 
     ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName);
 
+    ParsingPackage setMinExtensionVersions(@Nullable SparseIntArray minExtensionVersions);
+
     ParsingPackage setMinSdkVersion(int minSdkVersion);
 
     ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index be1817d..1a1395c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -51,6 +51,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -340,6 +341,8 @@
     private String manageSpaceActivityName;
     private float maxAspectRatio;
     private float minAspectRatio;
+    @Nullable
+    private SparseIntArray minExtensionVersions;
     private int minSdkVersion;
     private int networkSecurityConfigRes;
     @Nullable
@@ -1100,6 +1103,7 @@
         dest.writeBoolean(this.preserveLegacyExternalStorage);
         dest.writeArraySet(this.mimeGroups);
         dest.writeInt(this.gwpAsanMode);
+        dest.writeSparseIntArray(this.minExtensionVersions);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1259,6 +1263,7 @@
         this.preserveLegacyExternalStorage = in.readBoolean();
         this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
         this.gwpAsanMode = in.readInt();
+        this.minExtensionVersions = in.readSparseIntArray();
     }
 
     public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
@@ -1767,6 +1772,12 @@
         return minAspectRatio;
     }
 
+    @Nullable
+    @Override
+    public SparseIntArray getMinExtensionVersions() {
+        return minExtensionVersions;
+    }
+
     @Override
     public int getMinSdkVersion() {
         return minSdkVersion;
@@ -2215,6 +2226,12 @@
     }
 
     @Override
+    public ParsingPackageImpl setMinExtensionVersions(@Nullable SparseIntArray value) {
+        minExtensionVersions = value;
+        return this;
+    }
+
+    @Override
     public ParsingPackageImpl setMinSdkVersion(int value) {
         minSdkVersion = value;
         return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 687bc23..1ded8d4 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -41,6 +41,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.R;
 
@@ -609,6 +610,13 @@
     String getManageSpaceActivityName();
 
     /**
+     * @see ApplicationInfo#minExtensionVersions
+     * @see R.styleable#AndroidManifestExtensionSdk
+     */
+    @Nullable
+    SparseIntArray getMinExtensionVersions();
+
+    /**
      * @see ApplicationInfo#minSdkVersion
      * @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
      */
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index c61362f..29ece49 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -95,6 +95,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.util.apk.ApkSignatureVerifier;
 
@@ -1255,6 +1256,7 @@
 
                 int type;
                 final int innerDepth = parser.getDepth();
+                SparseIntArray minExtensionVersions = null;
                 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                         && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
                     if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1263,7 +1265,10 @@
 
                     final ParseResult result;
                     if (parser.getName().equals("extension-sdk")) {
-                        result = parseExtensionSdk(input, pkg, res, parser);
+                        if (minExtensionVersions == null) {
+                            minExtensionVersions = new SparseIntArray();
+                        }
+                        result = parseExtensionSdk(input, res, parser, minExtensionVersions);
                         XmlUtils.skipCurrentTag(parser);
                     } else {
                         result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
@@ -1273,6 +1278,7 @@
                         return input.error(result);
                     }
                 }
+                pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
             } finally {
                 sa.recycle();
             }
@@ -1280,8 +1286,21 @@
         return input.success(pkg);
     }
 
-    private static ParseResult parseExtensionSdk(ParseInput input, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser) {
+    @Nullable
+    private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
+        if (input == null) {
+            return null;
+        }
+        SparseIntArray output = new SparseIntArray(input.size());
+        for (int i = 0; i < input.size(); i++) {
+            output.put(input.keyAt(i), input.valueAt(i));
+        }
+        return output;
+    }
+
+    private static ParseResult<SparseIntArray> parseExtensionSdk(
+            ParseInput input, Resources res, XmlResourceParser parser,
+            SparseIntArray minExtensionVersions) {
         int sdkVersion;
         int minVersion;
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
@@ -1316,7 +1335,8 @@
                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "Specified sdkVersion " + sdkVersion + " is not valid");
         }
-        return input.success(pkg);
+        minExtensionVersions.put(sdkVersion, minVersion);
+        return input.success(minExtensionVersions);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
index 882a7f4..b3b4549 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
@@ -233,8 +233,10 @@
         Camera.Parameters params = legacyRequest.parameters;
 
         Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-        ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
-                request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+        ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
+                request.get(CaptureRequest.SCALER_CROP_REGION),
+                request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+                previewSize, params);
 
         List<Face> convertedFaces = new ArrayList<>();
         if (faces != null) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 6953a5b..362ddfa 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -771,6 +771,7 @@
                     CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     ,
                     CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE                      ,
                     CameraCharacteristics.CONTROL_MAX_REGIONS                             ,
+                    CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE                        ,
                     CameraCharacteristics.FLASH_INFO_AVAILABLE                            ,
                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   ,
                     CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  ,
@@ -828,6 +829,7 @@
                     CaptureRequest.CONTROL_MODE,
                     CaptureRequest.CONTROL_SCENE_MODE,
                     CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
+                    CaptureRequest.CONTROL_ZOOM_RATIO,
                     CaptureRequest.FLASH_MODE,
                     CaptureRequest.JPEG_GPS_COORDINATES,
                     CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
@@ -872,6 +874,7 @@
                     CaptureResult.CONTROL_AWB_MODE                                 ,
                     CaptureResult.CONTROL_AWB_LOCK                                 ,
                     CaptureResult.CONTROL_MODE                                     ,
+                    CaptureResult.CONTROL_ZOOM_RATIO                               ,
                     CaptureResult.FLASH_MODE                                       ,
                     CaptureResult.JPEG_GPS_COORDINATES                             ,
                     CaptureResult.JPEG_GPS_PROCESSING_METHOD                       ,
@@ -935,6 +938,12 @@
 
     private static void mapScaler(CameraMetadataNative m, Parameters p) {
         /*
+         * control.zoomRatioRange
+         */
+        Range<Float> zoomRatioRange = new Range<Float>(1.0f, ParameterUtils.getMaxZoomRatio(p));
+        m.set(CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange);
+
+        /*
          * scaler.availableMaxDigitalZoom
          */
         m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p));
@@ -1383,6 +1392,9 @@
         // control.sceneMode -- DISABLED is always available
         m.set(CaptureRequest.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
 
+        // control.zoomRatio -- 1.0
+        m.set(CaptureRequest.CONTROL_ZOOM_RATIO, 1.0f);
+
         /*
          * statistics.*
          */
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 2e06d5f..3a46379 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -68,8 +68,9 @@
          */
         ParameterUtils.ZoomData zoomData;
         {
-            zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
+            zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
                     request.get(SCALER_CROP_REGION),
+                    request.get(CONTROL_ZOOM_RATIO),
                     previewSize,
                     params);
 
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index dc5823d..09edf74 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -118,8 +118,10 @@
 
         Rect activeArraySize = characteristics.get(
                 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-        ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize,
-                request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+        ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArraySize,
+                request.get(CaptureRequest.SCALER_CROP_REGION),
+                request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+                previewSize, params);
 
         /*
          * colorCorrection
@@ -516,5 +518,12 @@
         {
             m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
         }
+
+        /*
+         * control.zoomRatio
+         */
+        {
+            m.set(CONTROL_ZOOM_RATIO, zoomData.reportedZoomRatio);
+        }
     }
 }
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 3cfd020..eb43598 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -73,11 +73,15 @@
         public final Rect previewCrop;
         /** Reported crop-region given the zoom index, coordinates relative to active-array */
         public final Rect reportedCrop;
+        /** Reported zoom ratio given the zoom index */
+        public final float reportedZoomRatio;
 
-        public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop) {
+        public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop,
+                float reportedZoomRatio) {
             this.zoomIndex = zoomIndex;
             this.previewCrop = previewCrop;
             this.reportedCrop = reportedCrop;
+            this.reportedZoomRatio = reportedZoomRatio;
         }
     }
 
@@ -371,7 +375,8 @@
      * @throws NullPointerException if any of the args were {@code null}
      */
     public static int getClosestAvailableZoomCrop(
-            Camera.Parameters params, Rect activeArray, Size streamSize, Rect cropRegion,
+            Camera.Parameters params, Rect activeArray,
+            Size streamSize, Rect cropRegion,
             /*out*/
             Rect reportedCropRegion,
             Rect previewCropRegion) {
@@ -733,6 +738,92 @@
     }
 
     /**
+     * Convert the user-specified crop region/zoom into zoom data; which can be used
+     * to set the parameters to a specific zoom index, or to report back to the user what
+     * the actual zoom was, or for other calculations requiring the current preview crop region.
+     *
+     * <p>None of the parameters are mutated.<p>
+     *
+     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+     * @param cropRegion the user-specified crop region
+     * @param zoomRatio the user-specified zoom ratio
+     * @param previewSize the current preview size (in pixels)
+     * @param params the current camera parameters (not mutated)
+     *
+     * @return the zoom index, and the effective/reported crop regions (relative to active array)
+     */
+    public static ZoomData convertToLegacyZoom(Rect activeArraySize, Rect
+            cropRegion, Float zoomRatio, Size previewSize, Camera.Parameters params) {
+        final float FLOAT_EQUAL_THRESHOLD = 0.0001f;
+        if (zoomRatio != null &&
+                Math.abs(1.0f - zoomRatio) > FLOAT_EQUAL_THRESHOLD) {
+            // User uses CONTROL_ZOOM_RATIO to control zoom
+            return convertZoomRatio(activeArraySize, zoomRatio, previewSize, params);
+        }
+
+        return convertScalerCropRegion(activeArraySize, cropRegion, previewSize, params);
+    }
+
+    /**
+     * Convert the user-specified zoom ratio into zoom data; which can be used
+     * to set the parameters to a specific zoom index, or to report back to the user what the
+     * actual zoom was, or for other calculations requiring the current preview crop region.
+     *
+     * <p>None of the parameters are mutated.</p>
+     *
+     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+     * @param zoomRatio the current zoom ratio
+     * @param previewSize the current preview size (in pixels)
+     * @param params the current camera parameters (not mutated)
+     *
+     * @return the zoom index, and the effective/reported crop regions (relative to active array)
+     */
+    public static ZoomData convertZoomRatio(Rect activeArraySize, float zoomRatio,
+            Size previewSize, Camera.Parameters params) {
+        if (DEBUG) {
+            Log.v(TAG, "convertZoomRatio - user zoom ratio was " + zoomRatio);
+        }
+
+        List<Rect> availableReportedCropRegions =
+                getAvailableZoomCropRectangles(params, activeArraySize);
+        List<Rect> availablePreviewCropRegions =
+                getAvailablePreviewZoomCropRectangles(params, activeArraySize, previewSize);
+        if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
+            throw new AssertionError("available reported/preview crop region size mismatch");
+        }
+
+        // Find the best matched legacy zoom ratio for the requested camera2 zoom ratio.
+        int bestZoomIndex = 0;
+        Rect reportedCropRegion = new Rect(availableReportedCropRegions.get(0));
+        Rect previewCropRegion = new Rect(availablePreviewCropRegions.get(0));
+        float reportedZoomRatio = 1.0f;
+        if (params.isZoomSupported()) {
+            List<Integer> zoomRatios = params.getZoomRatios();
+            for (int i = 1; i < zoomRatios.size(); i++) {
+                if (zoomRatio * ZOOM_RATIO_MULTIPLIER >= zoomRatios.get(i)) {
+                    bestZoomIndex = i;
+                    reportedCropRegion = availableReportedCropRegions.get(i);
+                    previewCropRegion = availablePreviewCropRegions.get(i);
+                    reportedZoomRatio = zoomRatios.get(i);
+                } else {
+                    break;
+                }
+            }
+        }
+
+        if (DEBUG) {
+            Log.v(TAG, "convertZoomRatio - zoom calculated to: " +
+                    "zoomIndex = " + bestZoomIndex +
+                    ", reported crop region = " + reportedCropRegion +
+                    ", preview crop region = " + previewCropRegion +
+                    ", reported zoom ratio = " + reportedZoomRatio);
+        }
+
+        return new ZoomData(bestZoomIndex, reportedCropRegion,
+                previewCropRegion, reportedZoomRatio);
+    }
+
+    /**
      * Convert the user-specified crop region into zoom data; which can be used
      * to set the parameters to a specific zoom index, or to report back to the user what the
      * actual zoom was, or for other calculations requiring the current preview crop region.
@@ -767,15 +858,17 @@
         final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
                 previewSize, userCropRegion,
                 /*out*/reportedCropRegion, /*out*/previewCropRegion);
+        final float reportedZoomRatio = 1.0f;
 
         if (DEBUG) {
             Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
                     "zoomIndex = " + zoomIdx +
                     ", reported crop region = " + reportedCropRegion +
-                    ", preview crop region = " + previewCropRegion);
+                    ", preview crop region = " + previewCropRegion +
+                    ", reported zoom ratio = " + reportedZoomRatio);
         }
 
-        return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion);
+        return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion, reportedZoomRatio);
     }
 
     /**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 5cc73ca..d35ce5b 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -16,6 +16,8 @@
 
 package android.os.incremental;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.ParcelFileDescriptor;
 
 import java.io.ByteArrayInputStream;
@@ -45,8 +47,8 @@
     public static class HashingInfo {
         public final int hashAlgorithm; // only 1 == SHA256 supported
         public final byte log2BlockSize; // only 12 (block size 4096) supported now
-        public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
-        public final byte[] rawRootHash; // salted digest of the first Merkle tree page
+        @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
+        @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page
 
         HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
             this.hashAlgorithm = hashAlgorithm;
@@ -58,7 +60,8 @@
         /**
          * Constructs HashingInfo from byte array.
          */
-        public static HashingInfo fromByteArray(byte[] bytes) throws IOException {
+        @NonNull
+        public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException {
             ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
             final int hashAlgorithm = buffer.getInt();
             final byte log2BlockSize = buffer.get();
@@ -106,8 +109,18 @@
     }
 
     public final int version; // Always 2 for now.
-    public final byte[] hashingInfo;
-    public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later.
+    /**
+     * Raw byte array containing the IncFS hashing data.
+     * @see HashingInfo#fromByteArray(byte[])
+     */
+    @Nullable public final byte[] hashingInfo;
+
+    /**
+     * Raw byte array containing the V4 signature data.
+     * <p>Passed as-is to the kernel. Can be retrieved later.
+     * @see SigningInfo#fromByteArray(byte[])
+     */
+    @Nullable public final byte[] signingInfo;
 
     /**
      * Construct a V4Signature from .idsig file.
@@ -121,7 +134,8 @@
     /**
      * Construct a V4Signature from a byte array.
      */
-    public static V4Signature readFrom(byte[] bytes) throws IOException {
+    @NonNull
+    public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException {
         try (InputStream stream = new ByteArrayInputStream(bytes)) {
             return readFrom(stream);
         }
@@ -169,7 +183,7 @@
         return this.version == SUPPORTED_VERSION;
     }
 
-    private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) {
+    private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) {
         this.version = version;
         this.hashingInfo = hashingInfo;
         this.signingInfo = signingInfo;
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 63b3804..9cf1b87 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Size;
 import android.app.slice.Slice;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.widget.inline.InlinePresentationSpec;
@@ -67,18 +66,6 @@
         return hints.toArray(new String[hints.size()]);
     }
 
-    /**
-     * @hide
-     * @removed
-     */
-    @UnsupportedAppUsage
-    public InlinePresentation(
-            @NonNull Slice slice,
-            @NonNull android.view.inline.InlinePresentationSpec inlinePresentationSpec,
-            boolean pinned) {
-        this(slice, inlinePresentationSpec.toWidget(), pinned);
-    }
-
 
 
     // Code below generated by codegen v1.0.15.
@@ -245,7 +232,7 @@
     };
 
     @DataClass.Generated(
-            time = 1585633564226L,
+            time = 1586992400667L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
             inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final  boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 3f07873..337027e 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -182,7 +182,6 @@
     private Window mWindow;
     private Activity mActivity;
     private boolean mInteractive;
-    private boolean mLowProfile = true;
     private boolean mFullscreen;
     private boolean mScreenBright = true;
     private boolean mStarted;
@@ -530,32 +529,6 @@
     }
 
     /**
-     * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view.
-     *
-     * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE
-     * @hide There is no reason to have this -- dreams can set this flag
-     * on their own content view, and from there can actually do the
-     * correct interactions with it (seeing when it is cleared etc).
-     */
-    public void setLowProfile(boolean lowProfile) {
-        if (mLowProfile != lowProfile) {
-            mLowProfile = lowProfile;
-            int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE;
-            applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag);
-        }
-    }
-
-    /**
-     * Returns whether or not this dream is in low profile mode. Defaults to true.
-     *
-     * @see #setLowProfile(boolean)
-     * @hide
-     */
-    public boolean isLowProfile() {
-        return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile);
-    }
-
-    /**
      * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN}
      * on the dream's window.
      *
@@ -1094,10 +1067,6 @@
         // along well. Dreams usually don't need such bars anyways, so disable them by default.
         mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 
-        applySystemUiVisibilityFlags(
-                (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
-                View.SYSTEM_UI_FLAG_LOW_PROFILE);
-
         mWindow.getDecorView().addOnAttachStateChangeListener(
                 new View.OnAttachStateChangeListener() {
                     @Override
@@ -1126,18 +1095,6 @@
         }
     }
 
-    private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) {
-        View v = mWindow == null ? null : mWindow.getDecorView();
-        return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0;
-    }
-
-    private void applySystemUiVisibilityFlags(int flags, int mask) {
-        View v = mWindow == null ? null : mWindow.getDecorView();
-        if (v != null) {
-            v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask));
-        }
-    }
-
     private int applyFlags(int oldFlags, int flags, int mask) {
         return (oldFlags&~mask) | (flags&mask);
     }
@@ -1163,7 +1120,6 @@
         pw.println("  window: " + mWindow);
         pw.print("  flags:");
         if (isInteractive()) pw.print(" interactive");
-        if (isLowProfile()) pw.print(" lowprofile");
         if (isFullscreen()) pw.print(" fullscreen");
         if (isScreenBright()) pw.print(" bright");
         if (isWindowless()) pw.print(" windowless");
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 21b83c6..5c43f8f 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -292,6 +292,18 @@
         return this.user.getIdentifier();
     }
 
+    /**
+     * Like {@link #getUserId()} but handles special users.
+     * @hide
+     */
+    public int getNormalizedUserId() {
+        int userId = getUserId();
+        if (userId == UserHandle.USER_ALL) {
+            userId = UserHandle.USER_SYSTEM;
+        }
+        return userId;
+    }
+
     /** The package that the notification belongs to. */
     public String getPackageName() {
         return pkg;
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 362b94b..3b5a6d5 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -413,6 +413,10 @@
      * {@link #ACTION_VOICE_SEARCH_HANDS_FREE}, {@link #ACTION_WEB_SEARCH} to indicate whether to
      * only use an offline speech recognition engine. The default is false, meaning that either
      * network or offline recognition engines may be used.
+     *
+     * <p>Depending on the recognizer implementation, these values may have
+     * no effect.</p>
+     *
      */
     public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
 }
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index dbbe4b6..d6d9fc6 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -123,7 +123,7 @@
         }
         // Update mNextServedView when focusedView changed.
         final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
-        onViewFocusChanged(viewForWindowFocus, true);
+        onViewFocusChanged(viewForWindowFocus, viewForWindowFocus.hasFocus());
 
         immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus,
                 windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index a9f3e04..0c50cb7 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import android.annotation.Nullable;
-import android.app.AppOpsManager;
 import android.app.Notification;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -27,7 +26,6 @@
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -396,6 +394,7 @@
             addRectAroundView(mIcon);
             mExpandButtonRect = addRectAroundView(mExpandButton);
             mAppOpsRect = addRectAroundView(mAppOps);
+            setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps));
             addWidthRect();
             mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
         }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 1086774..76ed37c 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -228,6 +228,7 @@
      */
     public long mNativeObject;
     private long mNativeHandle;
+    private Throwable mReleaseStack = null;
 
     // TODO: Move this to native.
     private final Object mSizeLock = new Object();
@@ -426,11 +427,18 @@
         if (mNativeObject != 0) {
             release();
         }
-      	if (nativeObject != 0) {
+        if (nativeObject != 0) {
             mCloseGuard.open("release");
         }
         mNativeObject = nativeObject;
         mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
+        if (mNativeObject == 0) {
+            if (Build.IS_DEBUGGABLE) {
+                mReleaseStack = new Throwable("assigned zero nativeObject here");
+            }
+        } else {
+            mReleaseStack = null;
+        }
     }
 
     /**
@@ -989,11 +997,22 @@
             nativeRelease(mNativeObject);
             mNativeObject = 0;
             mNativeHandle = 0;
+            if (Build.IS_DEBUGGABLE) {
+                mReleaseStack = new Throwable("released here");
+            }
             mCloseGuard.close();
         }
     }
 
     /**
+     * Returns the call stack that assigned mNativeObject to zero.
+     * @hide
+     */
+    public Throwable getReleaseStack() {
+        return mReleaseStack;
+    }
+
+    /**
      * Disconnect any client still connected to the surface.
      * @hide
      */
@@ -1004,8 +1023,11 @@
     }
 
     private void checkNotReleased() {
-        if (mNativeObject == 0) throw new NullPointerException(
-                "mNativeObject is null. Have you called release() already?");
+        if (mNativeObject == 0) {
+            Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack);
+            throw new NullPointerException(
+                "mNativeObject of " + this + " is null. Have you called release() already?");
+        }
     }
 
     /**
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 83a7934..6d3dbfe 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1258,13 +1258,6 @@
                 }
             }
 
-            if (mForAugmentedAutofillOnly) {
-                if (sVerbose) {
-                    Log.v(TAG,  "notifyValueChanged(): not notifying system server on "
-                            + "augmented-only mode");
-                }
-                return;
-            }
             if (!mEnabled || !isActiveLocked()) {
                 if (!startAutofillIfNeededLocked(view)) {
                     if (sVerbose) {
@@ -1299,10 +1292,6 @@
             return;
         }
         synchronized (mLock) {
-            if (mForAugmentedAutofillOnly) {
-                if (sVerbose) Log.v(TAG,  "notifyValueChanged(): ignoring on augmented only mode");
-                return;
-            }
             if (!mEnabled || !isActiveLocked()) {
                 if (sVerbose) {
                     Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
diff --git a/core/java/android/view/inline/InlineContentView.java b/core/java/android/view/inline/InlineContentView.java
deleted file mode 100644
index 3df201c..0000000
--- a/core/java/android/view/inline/InlineContentView.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inline;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.util.AttributeSet;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewGroup;
-
-/**
- * This class represents a view that holds opaque content from another app that
- * you can inline in your UI.
- *
- * <p>Since the content presented by this view is from another security domain,it is
- * shown on a remote surface preventing the host application from accessing that content.
- * Also the host application cannot interact with the inlined content by injecting touch
- * events or clicking programmatically.
- *
- * <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
- * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating
- * transitions.
- *
- * <p>By default the surface backing this view is shown on top of the hosting window such
- * that the inlined content is interactive. However, you can temporarily move the surface
- * under the hosting window which could be useful in some cases, e.g. animating transitions.
- * At this point the inlined content will not be interactive and the touch events would
- * be delivered to your app.
- *
- * @hide
- * @removed
- */
-public class InlineContentView extends ViewGroup {
-
-    /**
-     * Callback for observing the lifecycle of the surface control
-     * that manipulates the backing secure embedded UI surface.
-     */
-    public interface SurfaceControlCallback {
-        /**
-         * Called when the backing surface is being created.
-         *
-         * @param surfaceControl The surface control to manipulate the surface.
-         */
-        void onCreated(@NonNull SurfaceControl surfaceControl);
-
-        /**
-         * Called when the backing surface is being destroyed.
-         *
-         * @param surfaceControl The surface control to manipulate the surface.
-         */
-        void onDestroyed(@NonNull SurfaceControl surfaceControl);
-    }
-
-    private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
-        @Override
-        public void surfaceCreated(@NonNull SurfaceHolder holder) {
-            mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
-        }
-
-        @Override
-        public void surfaceChanged(@NonNull SurfaceHolder holder,
-                int format, int width, int height) {
-            /* do nothing */
-        }
-
-        @Override
-        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
-            mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl());
-        }
-    };
-
-    private final @NonNull SurfaceView mSurfaceView;
-
-    private @Nullable SurfaceControlCallback mSurfaceControlCallback;
-
-    /**
-     * @inheritDoc
-     *
-     * @hide
-     */
-    public InlineContentView(@NonNull Context context) {
-        this(context, null);
-    }
-
-    /**
-     * @inheritDoc
-     *
-     * @hide
-     */
-    public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    /**
-     * @inheritDoc
-     *
-     * @hide
-     */
-    public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    /**
-     * Gets the surface control. If the surface is not created this method
-     * returns {@code null}.
-     *
-     * @return The surface control.
-     *
-     * @see #setSurfaceControlCallback(SurfaceControlCallback) 
-     */
-    public @Nullable SurfaceControl getSurfaceControl() {
-        return mSurfaceView.getSurfaceControl();
-    }
-
-    /**
-     * @inheritDoc
-     *
-     * @hide
-     */
-    public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes);
-        mSurfaceView.setZOrderOnTop(true);
-        mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
-        addView(mSurfaceView);
-    }
-
-    /**
-     * Sets the embedded UI.
-     * @param surfacePackage The embedded UI.
-     *
-     * @hide
-     */
-    public void setChildSurfacePackage(
-            @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
-        mSurfaceView.setChildSurfacePackage(surfacePackage);
-    }
-
-    @Override
-    public void onLayout(boolean changed, int l, int t, int r, int b) {
-        mSurfaceView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
-    }
-
-    /**
-     * Sets a callback to observe the lifecycle of the surface control for
-     * managing the backing surface.
-     *
-     * @param callback The callback to set or {@code null} to clear.
-     */
-    public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) {
-        if (mSurfaceControlCallback != null) {
-            mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
-        }
-        mSurfaceControlCallback = callback;
-        if (mSurfaceControlCallback != null) {
-            mSurfaceView.getHolder().addCallback(mSurfaceCallback);
-        }
-    }
-
-    /**
-     * @return Whether the surface backing this view appears on top of its parent.
-     *
-     * @see #setZOrderedOnTop(boolean)
-     */
-    public boolean isZOrderedOnTop() {
-        return mSurfaceView.isZOrderedOnTop();
-    }
-
-    /**
-     * Controls whether the backing surface is placed on top of this view's window.
-     * Normally, it is placed on top of the window, to allow interaction
-     * with the inlined UI. Via this method, you can place the surface below the
-     * window. This means that all of the contents of the window this view is in
-     * will be visible on top of its surface.
-     *
-     * <p> The Z ordering can be changed dynamically if the backing surface is
-     * created, otherwise the ordering would be applied at surface construction time.
-     *
-     * @param onTop Whether to show the surface on top of this view's window.
-     *
-     * @see #isZOrderedOnTop()
-     */
-    public boolean setZOrderedOnTop(boolean onTop) {
-        return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
-    }
-}
diff --git a/core/java/android/view/inline/InlinePresentationSpec.aidl b/core/java/android/view/inline/InlinePresentationSpec.aidl
deleted file mode 100644
index 680ee4e..0000000
--- a/core/java/android/view/inline/InlinePresentationSpec.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inline;
-
-/**
- * @hide
- * @removed
- */
-parcelable InlinePresentationSpec;
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
deleted file mode 100644
index d777cb8..0000000
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inline;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Size;
-
-import com.android.internal.util.DataClass;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class represents the presentation specification by which an inline suggestion
- * should abide when constructing its UI. Since suggestions are inlined in a
- * host application while provided by another source, they need to be consistent
- * with the host's look at feel to allow building smooth and integrated UIs.
- *
- * @hide
- * @removed
- */
-@DataClass(genEqualsHashCode = true, genToString = true, genBuilder = true)
-public final class InlinePresentationSpec implements Parcelable {
-
-    /** The minimal size of the suggestion. */
-    @NonNull
-    private final Size mMinSize;
-    /** The maximal size of the suggestion. */
-    @NonNull
-    private final Size mMaxSize;
-
-    /**
-     * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
-     * the default system UI style will be used.
-     */
-    @NonNull
-    private final Bundle mStyle;
-
-    private static Bundle defaultStyle() {
-        return Bundle.EMPTY;
-    }
-
-    /** @hide */
-    @DataClass.Suppress({"setMaxSize", "setMinSize"})
-    abstract static class BaseBuilder {
-    }
-
-    /**
-     * @hide
-     */
-    public android.widget.inline.InlinePresentationSpec toWidget() {
-        final android.widget.inline.InlinePresentationSpec.Builder builder =
-                new android.widget.inline.InlinePresentationSpec.Builder(
-                        getMinSize(), getMaxSize());
-        final Bundle style = getStyle();
-        if (style != null) {
-            builder.setStyle(style);
-        }
-        return builder.build();
-    }
-
-    /**
-     * @hide
-     */
-    public static android.view.inline.InlinePresentationSpec fromWidget(
-            android.widget.inline.InlinePresentationSpec widget) {
-        final android.view.inline.InlinePresentationSpec.Builder builder =
-                new android.view.inline.InlinePresentationSpec.Builder(
-                        widget.getMinSize(), widget.getMaxSize());
-        final Bundle style = widget.getStyle();
-        if (style != null) {
-            builder.setStyle(style);
-        }
-        return builder.build();
-    }
-
-    /**
-     * @hide
-     */
-    public static List<android.view.inline.InlinePresentationSpec> fromWidgets(
-            List<android.widget.inline.InlinePresentationSpec> widgets) {
-        final ArrayList<android.view.inline.InlinePresentationSpec> convertedSpecs =
-                new ArrayList<>();
-        for (int i = 0; i < widgets.size(); i++) {
-            convertedSpecs.add(fromWidget(widgets.get(i)));
-        }
-        return convertedSpecs;
-    }
-
-
-
-    // Code below generated by codegen v1.0.15.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    /* package-private */ InlinePresentationSpec(
-            @NonNull Size minSize,
-            @NonNull Size maxSize,
-            @NonNull Bundle style) {
-        this.mMinSize = minSize;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mMinSize);
-        this.mMaxSize = maxSize;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mMaxSize);
-        this.mStyle = style;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mStyle);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    /**
-     * The minimal size of the suggestion.
-     */
-    @UnsupportedAppUsage
-    @DataClass.Generated.Member
-    public @NonNull Size getMinSize() {
-        return mMinSize;
-    }
-
-    /**
-     * The maximal size of the suggestion.
-     */
-    @UnsupportedAppUsage
-    @DataClass.Generated.Member
-    public @NonNull Size getMaxSize() {
-        return mMaxSize;
-    }
-
-    /**
-     * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
-     * the default system UI style will be used.
-     */
-    @DataClass.Generated.Member
-    public @NonNull Bundle getStyle() {
-        return mStyle;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public String toString() {
-        // You can override field toString logic by defining methods like:
-        // String fieldNameToString() { ... }
-
-        return "InlinePresentationSpec { " +
-                "minSize = " + mMinSize + ", " +
-                "maxSize = " + mMaxSize + ", " +
-                "style = " + mStyle +
-        " }";
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public boolean equals(@Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(InlinePresentationSpec other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
-
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
-        InlinePresentationSpec that = (InlinePresentationSpec) o;
-        //noinspection PointlessBooleanExpression
-        return true
-                && java.util.Objects.equals(mMinSize, that.mMinSize)
-                && java.util.Objects.equals(mMaxSize, that.mMaxSize)
-                && java.util.Objects.equals(mStyle, that.mStyle);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
-        int _hash = 1;
-        _hash = 31 * _hash + java.util.Objects.hashCode(mMinSize);
-        _hash = 31 * _hash + java.util.Objects.hashCode(mMaxSize);
-        _hash = 31 * _hash + java.util.Objects.hashCode(mStyle);
-        return _hash;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeSize(mMinSize);
-        dest.writeSize(mMaxSize);
-        dest.writeBundle(mStyle);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    /* package-private */ InlinePresentationSpec(@NonNull android.os.Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        Size minSize = (Size) in.readSize();
-        Size maxSize = (Size) in.readSize();
-        Bundle style = in.readBundle();
-
-        this.mMinSize = minSize;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mMinSize);
-        this.mMaxSize = maxSize;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mMaxSize);
-        this.mStyle = style;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mStyle);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<InlinePresentationSpec> CREATOR
-            = new Parcelable.Creator<InlinePresentationSpec>() {
-        @Override
-        public InlinePresentationSpec[] newArray(int size) {
-            return new InlinePresentationSpec[size];
-        }
-
-        @Override
-        public InlinePresentationSpec createFromParcel(@NonNull android.os.Parcel in) {
-            return new InlinePresentationSpec(in);
-        }
-    };
-
-    /**
-     * A builder for {@link InlinePresentationSpec}
-     */
-    @SuppressWarnings("WeakerAccess")
-    @DataClass.Generated.Member
-    public static final class Builder extends BaseBuilder {
-
-        private @NonNull Size mMinSize;
-        private @NonNull Size mMaxSize;
-        private @NonNull Bundle mStyle;
-
-        private long mBuilderFieldsSet = 0L;
-
-        /**
-         * Creates a new Builder.
-         *
-         * @param minSize
-         *   The minimal size of the suggestion.
-         * @param maxSize
-         *   The maximal size of the suggestion.
-         */
-        @UnsupportedAppUsage
-        public Builder(
-                @NonNull Size minSize,
-                @NonNull Size maxSize) {
-            mMinSize = minSize;
-            com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mMinSize);
-            mMaxSize = maxSize;
-            com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mMaxSize);
-        }
-
-        /**
-         * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
-         * the default system UI style will be used.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setStyle(@NonNull Bundle value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x4;
-            mStyle = value;
-            return this;
-        }
-
-        /** Builds the instance. This builder should not be touched after calling this! */
-        @UnsupportedAppUsage
-        @NonNull
-        public InlinePresentationSpec build() {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x8; // Mark builder used
-
-            if ((mBuilderFieldsSet & 0x4) == 0) {
-                mStyle = defaultStyle();
-            }
-            InlinePresentationSpec o = new InlinePresentationSpec(
-                    mMinSize,
-                    mMaxSize,
-                    mStyle);
-            return o;
-        }
-
-        private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x8) != 0) {
-                throw new IllegalStateException(
-                        "This Builder should not be reused. Use a new Builder instance instead");
-            }
-        }
-    }
-
-    @DataClass.Generated(
-            time = 1585691139012L,
-            codegenVersion = "1.0.15",
-            sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
-            inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static  android.os.Bundle defaultStyle()\npublic  android.widget.inline.InlinePresentationSpec toWidget()\npublic static  android.view.inline.InlinePresentationSpec fromWidget(android.widget.inline.InlinePresentationSpec)\npublic static  java.util.List<android.view.inline.InlinePresentationSpec> fromWidgets(java.util.List<android.widget.inline.InlinePresentationSpec>)\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 3e9ffa7..1c703ec 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcelable;
 import android.widget.inline.InlinePresentationSpec;
 
@@ -87,17 +86,6 @@
         return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
     }
 
-    /**
-     * The presentation spec to which the inflated suggestion view abides.
-     *
-     * @hide
-     * @removed
-     */
-    @UnsupportedAppUsage
-    public @NonNull android.view.inline.InlinePresentationSpec getPresentationSpec() {
-        return android.view.inline.InlinePresentationSpec.fromWidget(mInlinePresentationSpec);
-    }
-
 
 
     // Code below generated by codegen v1.0.15.
@@ -358,10 +346,10 @@
     };
 
     @DataClass.Generated(
-            time = 1585633580662L,
+            time = 1586992414034L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
-            inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final  boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inline.InlinePresentationSpec getPresentationSpec()\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+            inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final  boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index af896fc..d282b56 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.LocaleList;
@@ -93,20 +92,6 @@
     private int mHostDisplayId;
 
     /**
-     * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
-     * count is larger than the number of specs in the list, then the last spec is used for the
-     * remainder of the suggestions. The list should not be empty.
-     *
-     * @hide
-     * @removed
-     */
-    @UnsupportedAppUsage
-    @NonNull
-    public List<android.view.inline.InlinePresentationSpec> getPresentationSpecs() {
-        return android.view.inline.InlinePresentationSpec.fromWidgets(mInlinePresentationSpecs);
-    }
-
-    /**
      * @hide
      * @see {@link #mHostInputToken}.
      */
@@ -170,17 +155,6 @@
 
     /** @hide */
     abstract static class BaseBuilder {
-        /**
-         * @hide
-         * @removed
-         */
-        @UnsupportedAppUsage
-        @NonNull
-        public Builder addPresentationSpecs(
-                @NonNull android.view.inline.InlinePresentationSpec value) {
-            return ((Builder) this).addInlinePresentationSpecs(value.toWidget());
-        }
-
         abstract Builder setInlinePresentationSpecs(
                 @NonNull List<android.widget.inline.InlinePresentationSpec> specs);
 
@@ -608,10 +582,10 @@
     }
 
     @DataClass.Generated(
-            time = 1585768018462L,
+            time = 1586992395497L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs()\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(android.view.inline.InlinePresentationSpec)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 62dd192..51d37a5 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5440,6 +5440,9 @@
 
         @Override
         public boolean onTouchEvent(MotionEvent ev) {
+            if (!mTextView.isFromPrimePointer(ev, true)) {
+                return true;
+            }
             if (mFlagInsertionHandleGesturesEnabled && mFlagCursorDragFromAnywhereEnabled) {
                 // Should only enable touch through when cursor drag is enabled.
                 // Otherwise the insertion handle view cannot be moved.
@@ -5908,6 +5911,9 @@
 
         @Override
         public boolean onTouchEvent(MotionEvent event) {
+            if (!mTextView.isFromPrimePointer(event, true)) {
+                return true;
+            }
             boolean superResult = super.onTouchEvent(event);
 
             switch (event.getActionMasked()) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e178318..4be9e1a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -855,6 +855,19 @@
     int mTextEditSuggestionContainerLayout;
     int mTextEditSuggestionHighlightStyle;
 
+    private static final int NO_POINTER_ID = -1;
+    /**
+     * The prime (the 1st finger) pointer id which is used as a lock to prevent multi touch among
+     * TextView and the handle views which are rendered on popup windows.
+     */
+    private int mPrimePointerId = NO_POINTER_ID;
+
+    /**
+     * Whether the prime pointer is from the event delivered to selection handle or insertion
+     * handle.
+     */
+    private boolean mIsPrimePointerFromHandleView;
+
     /**
      * {@link EditText} specific data, created on demand when one of the Editor fields is used.
      * See {@link #createEditorIfNeeded()}.
@@ -10886,6 +10899,36 @@
         }
     }
 
+    /**
+     * Called from onTouchEvent() to prevent the touches by secondary fingers.
+     * Dragging on handles can revise cursor/selection, so can dragging on the text view.
+     * This method is a lock to avoid processing multiple fingers on both text view and handles.
+     * Note: multiple fingers on handles (e.g. 2 fingers on the 2 selection handles) should work.
+     *
+     * @param event The motion event that is being handled and carries the pointer info.
+     * @param fromHandleView true if the event is delivered to selection handle or insertion
+     * handle; false if this event is delivered to TextView.
+     * @return Returns true to indicate that onTouchEvent() can continue processing the motion
+     * event, otherwise false.
+     *  - Always returns true for the first finger.
+     *  - For secondary fingers, if the first or current finger is from TextView, returns false.
+     *    This is to make touch mutually exclusive between the TextView and the handles, but
+     *    not among the handles.
+     */
+    boolean isFromPrimePointer(MotionEvent event, boolean fromHandleView) {
+        if (mPrimePointerId == NO_POINTER_ID)  {
+            mPrimePointerId = event.getPointerId(0);
+            mIsPrimePointerFromHandleView = fromHandleView;
+        } else if (mPrimePointerId != event.getPointerId(0)) {
+            return mIsPrimePointerFromHandleView && fromHandleView;
+        }
+        if (event.getActionMasked() == MotionEvent.ACTION_UP
+            || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+            mPrimePointerId = -1;
+        }
+        return true;
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (DEBUG_CURSOR) {
@@ -10894,6 +10937,9 @@
                     MotionEvent.actionToString(event.getActionMasked()),
                     event.getX(), event.getY());
         }
+        if (!isFromPrimePointer(event, false)) {
+            return true;
+        }
 
         final int action = event.getActionMasked();
         if (mEditor != null) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index d64b5f1..be66d0c 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -232,9 +232,8 @@
     }
 
     /**
-     * Show toast if current assigned shortcut target is an accessibility service and its target
-     * sdk version is less than or equal to Q, or greater than Q and does not request
-     * accessibility button.
+     * Show toast to alert the user that the accessibility shortcut turned on or off an
+     * accessibility service.
      */
     private void showToast() {
         final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
@@ -247,12 +246,15 @@
         }
         final boolean requestA11yButton = (serviceInfo.flags
                 & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
-        if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
-                .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton) {
+        final boolean isServiceEnabled = isServiceEnabled(serviceInfo);
+        if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
+                > Build.VERSION_CODES.Q && requestA11yButton && isServiceEnabled) {
+            // An accessibility button callback is sent to the target accessibility service.
+            // No need to show up a toast in this case.
             return;
         }
         // For accessibility services, show a toast explaining what we're doing.
-        String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+        String toastMessageFormatString = mContext.getString(isServiceEnabled
                 ? R.string.accessibility_shortcut_disabling_service
                 : R.string.accessibility_shortcut_enabling_service);
         String toastMessage = String.format(toastMessageFormatString, serviceName);
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index d43333e..b1e356d 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -300,30 +300,26 @@
     }
 
     private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
-        UserHandle listUserHandle = activeListAdapter.getUserHandle();
-
-        if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
-            if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
-                    UserHandle.myUserId(), listUserHandle.getIdentifier())) {
-                if (listUserHandle.equals(mPersonalProfileUserHandle)) {
-                    DevicePolicyEventLogger.createEvent(
-                                DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
-                            .setStrings(getMetricsCategory())
-                            .write();
-                    showNoWorkToPersonalIntentsEmptyState(activeListAdapter);
-                } else {
-                    DevicePolicyEventLogger.createEvent(
-                            DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
-                            .setStrings(getMetricsCategory())
-                            .write();
-                    showNoPersonalToWorkIntentsEmptyState(activeListAdapter);
-                }
-                return false;
-            }
+        if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) {
+            activeListAdapter.postListReadyRunnable(doPostProcessing);
+            return false;
         }
         return activeListAdapter.rebuildList(doPostProcessing);
     }
 
+    private boolean shouldShowNoCrossProfileIntentsEmptyState(
+            ResolverListAdapter activeListAdapter) {
+        UserHandle listUserHandle = activeListAdapter.getUserHandle();
+        return UserHandle.myUserId() != listUserHandle.getIdentifier()
+                && allowShowNoCrossProfileIntentsEmptyState()
+                && !mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
+                        UserHandle.myUserId(), listUserHandle.getIdentifier());
+    }
+
+    boolean allowShowNoCrossProfileIntentsEmptyState() {
+        return true;
+    }
+
     protected abstract void showWorkProfileOffEmptyState(
             ResolverListAdapter activeListAdapter, View.OnClickListener listener);
 
@@ -353,12 +349,35 @@
      * anyway.
      */
     void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) {
+        if (maybeShowNoCrossProfileIntentsEmptyState(listAdapter)) {
+            return;
+        }
         if (maybeShowWorkProfileOffEmptyState(listAdapter)) {
             return;
         }
         maybeShowNoAppsAvailableEmptyState(listAdapter);
     }
 
+    private boolean maybeShowNoCrossProfileIntentsEmptyState(ResolverListAdapter listAdapter) {
+        if (!shouldShowNoCrossProfileIntentsEmptyState(listAdapter)) {
+            return false;
+        }
+        if (listAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
+            DevicePolicyEventLogger.createEvent(
+                    DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
+                    .setStrings(getMetricsCategory())
+                    .write();
+            showNoWorkToPersonalIntentsEmptyState(listAdapter);
+        } else {
+            DevicePolicyEventLogger.createEvent(
+                    DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
+                    .setStrings(getMetricsCategory())
+                    .write();
+            showNoPersonalToWorkIntentsEmptyState(listAdapter);
+        }
+        return true;
+    }
+
     /**
      * Returns {@code true} if the work profile off empty state screen is shown.
      */
@@ -429,16 +448,16 @@
             subtitle.setVisibility(View.GONE);
         }
 
-        ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
         Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
+        button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+        button.setOnClickListener(buttonOnClick);
+
+        ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
         if (!getContext().getResources().getBoolean(R.bool.resolver_landscape_phone)) {
             icon.setVisibility(View.VISIBLE);
             icon.setImageResource(iconRes);
-            button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
-            button.setOnClickListener(buttonOnClick);
         } else {
             icon.setVisibility(View.GONE);
-            button.setVisibility(View.GONE);
         }
 
         activeListAdapter.markTabLoaded();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3e7f24b..d851a09 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2171,7 +2171,7 @@
                         mChooserMultiProfilePagerAdapter.getActiveListAdapter();
                 if (currentListAdapter != null) {
                     currentListAdapter.updateModel(info.getResolvedComponentName());
-                    currentListAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
+                    currentListAdapter.updateChooserCounts(ri.activityInfo.packageName,
                             targetIntent.getAction());
                 }
                 if (DEBUG) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index dd3a660..1bc982c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -179,6 +179,7 @@
     public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
 
     private BroadcastReceiver mWorkProfileStateReceiver;
+    private boolean mIsHeaderCreated;
 
     /**
      * Get the string resource to be used as a label for the link to the resolver activity for an
@@ -479,13 +480,42 @@
                         == workProfileUserHandle.getIdentifier()),
                 mUseLayoutForBrowsables,
                 /* userHandle */ workProfileUserHandle);
+        // In the edge case when we have 0 apps in the current profile and >1 apps in the other,
+        // the intent resolver is started in the other profile. Since this is the only case when
+        // this happens, we check for it here and set the current profile's tab.
+        int selectedProfile = getCurrentProfile();
+        UserHandle intentUser = UserHandle.of(getLaunchingUserId());
+        if (!getUser().equals(intentUser)) {
+            if (getPersonalProfileUserHandle().equals(intentUser)) {
+                selectedProfile = PROFILE_PERSONAL;
+            } else if (getWorkProfileUserHandle().equals(intentUser)) {
+                selectedProfile = PROFILE_WORK;
+            }
+        }
         return new ResolverMultiProfilePagerAdapter(
                 /* context */ this,
                 personalAdapter,
                 workAdapter,
-                /* defaultProfile */ getCurrentProfile(),
+                selectedProfile,
                 getPersonalProfileUserHandle(),
-                getWorkProfileUserHandle());
+                getWorkProfileUserHandle(),
+                /* shouldShowNoCrossProfileIntentsEmptyState= */ getUser().equals(intentUser));
+    }
+
+    /**
+     * Returns the user id of the user that the starting intent originated from.
+     * <p>This is not necessarily equal to {@link #getUserId()} or {@link UserHandle#myUserId()},
+     * as there are edge cases when the intent resolver is launched in the other profile.
+     * For example, when we have 0 resolved apps in current profile and multiple resolved apps
+     * in the other profile, opening a link from the current profile launches the intent resolver
+     * in the other one. b/148536209 for more info.
+     */
+    private int getLaunchingUserId() {
+        int contentUserHint = getIntent().getContentUserHint();
+        if (contentUserHint == UserHandle.USER_CURRENT) {
+            return UserHandle.myUserId();
+        }
+        return contentUserHint;
     }
 
     protected @Profile int getCurrentProfile() {
@@ -856,7 +886,7 @@
 
     private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
             boolean filtered) {
-        if (mMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) {
+        if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
             // Never allow the inactive profile to always open an app.
             mAlwaysButton.setEnabled(false);
             return;
@@ -995,10 +1025,7 @@
             mMultiProfilePagerAdapter.showListView(listAdapter);
         }
         if (doPostProcessing) {
-            if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
-                    == UserHandle.myUserId()) {
-                setHeader();
-            }
+            maybeCreateHeader(listAdapter);
             resetButtonBar();
             onListRebuilt(listAdapter);
         }
@@ -1679,10 +1706,15 @@
 
     /**
      * Configure the area above the app selection list (title, content preview, etc).
+     * <p>The header is created once when first launching the activity and whenever a package is
+     * installed or uninstalled.
      */
-    public void setHeader() {
-        if (mMultiProfilePagerAdapter.getActiveListAdapter().getCount() == 0
-                && mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0) {
+    private void maybeCreateHeader(ResolverListAdapter listAdapter) {
+        if (mIsHeaderCreated) {
+            return;
+        }
+        if (!shouldShowTabs()
+                && listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
             final TextView titleView = findViewById(R.id.title);
             if (titleView != null) {
                 titleView.setVisibility(View.GONE);
@@ -1703,8 +1735,9 @@
 
         final ImageView iconView = findViewById(R.id.icon);
         if (iconView != null) {
-            mMultiProfilePagerAdapter.getActiveListAdapter().loadFilteredItemIconTaskAsync(iconView);
+            listAdapter.loadFilteredItemIconTaskAsync(iconView);
         }
+        mIsHeaderCreated = true;
     }
 
     protected void resetButtonBar() {
@@ -1804,6 +1837,7 @@
                 // turning on.
                 return;
             }
+            mIsHeaderCreated = false;
             boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
             if (listRebuilt) {
                 ResolverListAdapter activeListAdapter =
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 579abee..73109c5 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -165,8 +165,9 @@
         mResolverListController.updateModel(componentName);
     }
 
-    public void updateChooserCounts(String packageName, int userId, String action) {
-        mResolverListController.updateChooserCounts(packageName, userId, action);
+    public void updateChooserCounts(String packageName, String action) {
+        mResolverListController.updateChooserCounts(
+                packageName, getUserHandle().getIdentifier(), action);
     }
 
     List<ResolvedComponentInfo> getUnfilteredResolveList() {
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index b690a18..ad31d8b 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -35,6 +35,7 @@
 public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
 
     private final ResolverProfileDescriptor[] mItems;
+    private final boolean mShouldShowNoCrossProfileIntentsEmptyState;
 
     ResolverMultiProfilePagerAdapter(Context context,
             ResolverListAdapter adapter,
@@ -44,6 +45,7 @@
         mItems = new ResolverProfileDescriptor[] {
                 createProfileDescriptor(adapter)
         };
+        mShouldShowNoCrossProfileIntentsEmptyState = true;
     }
 
     ResolverMultiProfilePagerAdapter(Context context,
@@ -51,13 +53,15 @@
             ResolverListAdapter workAdapter,
             @Profile int defaultProfile,
             UserHandle personalProfileUserHandle,
-            UserHandle workProfileUserHandle) {
+            UserHandle workProfileUserHandle,
+            boolean shouldShowNoCrossProfileIntentsEmptyState) {
         super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
                 workProfileUserHandle);
         mItems = new ResolverProfileDescriptor[] {
                 createProfileDescriptor(personalAdapter),
                 createProfileDescriptor(workAdapter)
         };
+        mShouldShowNoCrossProfileIntentsEmptyState = shouldShowNoCrossProfileIntentsEmptyState;
     }
 
     private ResolverProfileDescriptor createProfileDescriptor(
@@ -163,6 +167,11 @@
     }
 
     @Override
+    boolean allowShowNoCrossProfileIntentsEmptyState() {
+        return mShouldShowNoCrossProfileIntentsEmptyState;
+    }
+
+    @Override
     protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
             View.OnClickListener listener) {
         showEmptyState(activeListAdapter,
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index eda04a6..9bdbdc7 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -16,14 +16,39 @@
 
 package com.android.internal.app.procstats;
 
+import static com.android.internal.app.procstats.ProcessStats.ADJ_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_MOD;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
 import android.os.UserHandle;
 import android.service.procstats.ProcessStatsEnums;
 import android.service.procstats.ProcessStatsStateProto;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
-import static com.android.internal.app.procstats.ProcessStats.*;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -38,6 +63,7 @@
     public static final String[] STATE_NAMES_CSV;
     static final String[] STATE_TAGS;
     static final int[] STATE_PROTO_ENUMS;
+    private static final int[] PROCESS_STATS_STATE_TO_AGGREGATED_STATE;
 
     // Make the mapping easy to update.
     static {
@@ -126,6 +152,39 @@
         STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
                 ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
         STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
+
+        // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data
+        // aggregation / size reduction purposes.
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE = new int[STATE_COUNT];
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_PERSISTENT] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_FOREGROUND] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_BACKGROUND] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BACKUP] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+        // "Restarting" is not a real state, so this shouldn't exist.
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE_RESTARTING] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_UNKNOWN;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_RECEIVER] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_RECEIVER;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HEAVY_WEIGHT] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HOME] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
     }
 
     public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
@@ -455,4 +514,51 @@
         }
         return itemName;
     }
+
+    /**
+     * Aggregate process states to reduce size of statistics logs.
+     *
+     * <p>Involves unpacking the three parts of state (process state / device memory state /
+     * screen state), manipulating the elements, then re-packing the new values into a single
+     * int. This integer is guaranteed to be unique for any given combination of state elements.
+     *
+     * @param curState current state as used in mCurState in {@class ProcessState} ie. a value
+     *                 combined from the process's state, the device's memory pressure state, and
+     *                 the device's screen on/off state.
+     * @return an integer representing the combination of screen state and process state, where
+     *         process state has been aggregated.
+     */
+    public static int aggregateCurrentProcessState(int curState) {
+        int screenStateIndex = curState / (ADJ_SCREEN_MOD * STATE_COUNT);
+        // extract process state from the compound state variable (discarding memory state)
+        int procStateIndex = curState % STATE_COUNT;
+
+        // Remap process state per array above.
+        try {
+            procStateIndex = PROCESS_STATS_STATE_TO_AGGREGATED_STATE[procStateIndex];
+        } catch (IndexOutOfBoundsException e) {
+            procStateIndex = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_UNKNOWN;
+        }
+
+        // Pack screen & process state using bit shifting
+        return (procStateIndex << 0xf) | screenStateIndex;
+    }
+
+    /** Print aggregated tags generated via {@code #aggregateCurrentProcessState}. */
+    public static void printAggregatedProcStateTagProto(ProtoOutputStream proto, long screenId,
+            long stateId, int state) {
+        // screen state is in lowest 0xf bits, process state is in next 0xf bits up
+
+        try {
+            proto.write(stateId, STATE_PROTO_ENUMS[state >> 0xf]);
+        } catch (IndexOutOfBoundsException e) {
+            proto.write(stateId, ProcessStatsEnums.PROCESS_STATE_UNKNOWN);
+        }
+
+        try {
+            proto.write(screenId, ADJ_SCREEN_PROTO_ENUMS[state & 0xf]);
+        } catch (IndexOutOfBoundsException e) {
+            proto.write(screenId, ProcessStatsEnums.SCREEN_STATE_UNKNOWN);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index a6bed5b..79ff594 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -16,22 +16,6 @@
 
 package com.android.internal.app.procstats;
 
-import android.os.Parcel;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.service.procstats.ProcessStatsProto;
-import android.service.procstats.ProcessStatsStateProto;
-import android.util.ArrayMap;
-import android.util.DebugUtils;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.Slog;
-import android.util.SparseLongArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-
-
 import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
 import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
 import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
@@ -60,6 +44,21 @@
 import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
 import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
 
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsStateProto;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.SparseLongArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
 import com.android.internal.app.procstats.ProcessStats.PackageState;
 import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder;
 import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
@@ -1418,4 +1417,109 @@
 
         proto.end(token);
     }
+
+    /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
+    public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto, long fieldId,
+            String procName, int uid, long now) {
+        // Group proc stats by aggregated type (only screen state + process state)
+        SparseLongArray durationByState = new SparseLongArray();
+        boolean didCurState = false;
+        for (int i = 0; i < mDurations.getKeyCount(); i++) {
+            final int key = mDurations.getKeyAt(i);
+            final int type = SparseMappingTable.getIdFromKey(key);
+            final int aggregatedType = DumpUtils.aggregateCurrentProcessState(type);
+
+            long time = mDurations.getValue(key);
+            if (mCurCombinedState == type) {
+                didCurState = true;
+                time += now - mStartTime;
+            }
+            int index = durationByState.indexOfKey(aggregatedType);
+            if (index >= 0) {
+                durationByState.put(aggregatedType, time + durationByState.valueAt(index));
+            } else {
+                durationByState.put(aggregatedType, time);
+            }
+        }
+        if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+            final int aggregatedType = DumpUtils.aggregateCurrentProcessState(mCurCombinedState);
+            int index = durationByState.indexOfKey(aggregatedType);
+            if (index >= 0) {
+                durationByState.put(aggregatedType,
+                        (now - mStartTime) + durationByState.valueAt(index));
+            } else {
+                durationByState.put(aggregatedType, now - mStartTime);
+            }
+        }
+
+        // Now we have total durations, aggregate the RSS values
+        SparseLongArray meanRssByState = new SparseLongArray();
+        SparseLongArray maxRssByState = new SparseLongArray();
+        // compute weighted averages and max-of-max
+        for (int i = 0; i < mPssTable.getKeyCount(); i++) {
+            final int key = mPssTable.getKeyAt(i);
+            final int type = SparseMappingTable.getIdFromKey(key);
+            if (durationByState.indexOfKey(type) < 0) {
+                // state without duration should not have stats!
+                continue;
+            }
+            final int aggregatedType = DumpUtils.aggregateCurrentProcessState(type);
+
+            long[] rssMeanAndMax = mPssTable.getRssMeanAndMax(key);
+
+            // compute mean * duration, then store sum of that in meanRssByState
+            long meanTimesDuration = rssMeanAndMax[0] * mDurations.getValue(key);
+            if (meanRssByState.indexOfKey(aggregatedType) >= 0) {
+                meanRssByState.put(aggregatedType,
+                        meanTimesDuration + meanRssByState.get(aggregatedType));
+            } else {
+                meanRssByState.put(aggregatedType, meanTimesDuration);
+            }
+
+            // accumulate max-of-maxes in maxRssByState
+            if (maxRssByState.indexOfKey(aggregatedType) >= 0
+                    && maxRssByState.get(aggregatedType) < rssMeanAndMax[1]) {
+                maxRssByState.put(aggregatedType, rssMeanAndMax[1]);
+            } else if (maxRssByState.indexOfKey(aggregatedType) < 0) {
+                maxRssByState.put(aggregatedType, rssMeanAndMax[1]);
+            }
+        }
+
+        // divide the means by the durations to get the weighted mean-of-means
+        for (int i = 0; i < durationByState.size(); i++) {
+            int aggregatedKey = durationByState.keyAt(i);
+            if (meanRssByState.indexOfKey(aggregatedKey) < 0) {
+                // these data structures should be consistent
+                continue;
+            }
+            meanRssByState.put(aggregatedKey,
+                    meanRssByState.get(aggregatedKey) / durationByState.get(aggregatedKey));
+        }
+
+        // build the output
+        final long token = proto.start(fieldId);
+        proto.write(ProcessStatsProto.PROCESS, procName);
+        proto.write(ProcessStatsProto.UID, uid);
+
+        for (int i = 0; i < durationByState.size(); i++) {
+            final long stateToken = proto.start(ProcessStatsProto.STATES);
+
+            final int aggregatedKey = durationByState.keyAt(i);
+
+            DumpUtils.printAggregatedProcStateTagProto(proto,
+                    ProcessStatsStateProto.SCREEN_STATE,
+                    ProcessStatsStateProto.PROCESS_STATE,
+                    aggregatedKey);
+            proto.write(ProcessStatsStateProto.DURATION_MS, durationByState.get(aggregatedKey));
+
+            ProtoUtils.toAggStatsProto(proto, ProcessStatsStateProto.RSS,
+                    0, /* do not output a minimum value */
+                    meanRssByState.get(aggregatedKey),
+                    maxRssByState.get(aggregatedKey));
+
+            proto.end(stateToken);
+        }
+
+        proto.end(token);
+    }
 }
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 009b1e0..80f6272 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -2178,29 +2178,7 @@
      * Writes to ProtoOutputStream.
      */
     public void dumpDebug(ProtoOutputStream proto, long now, int section) {
-        proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
-        proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
-                mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
-        proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime);
-        proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime);
-        proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
-        proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
-        boolean partial = true;
-        if ((mFlags & FLAG_SHUTDOWN) != 0) {
-            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
-            partial = false;
-        }
-        if ((mFlags & FLAG_SYSPROPS) != 0) {
-            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
-            partial = false;
-        }
-        if ((mFlags & FLAG_COMPLETE) != 0) {
-            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
-            partial = false;
-        }
-        if (partial) {
-            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
-        }
+        dumpProtoPreamble(proto);
 
         final int NPAGETYPES = mPageTypeLabels.size();
         for (int i = 0; i < NPAGETYPES; i++) {
@@ -2247,6 +2225,49 @@
         }
     }
 
+    /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
+    public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto) {
+        dumpProtoPreamble(proto);
+        final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        for (int ip = 0; ip < procMap.size(); ip++) {
+            final String procName = procMap.keyAt(ip);
+            final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            for (int iu = 0; iu < uids.size(); iu++) {
+                final int uid = uids.keyAt(iu);
+                final ProcessState procState = uids.valueAt(iu);
+                procState.dumpAggregatedProtoForStatsd(proto,
+                        ProcessStatsSectionProto.PROCESS_STATS,
+                        procName, uid, mTimePeriodEndRealtime);
+            }
+        }
+    }
+
+    private void dumpProtoPreamble(ProtoOutputStream proto) {
+        proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
+        proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
+                mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+        proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime);
+        proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime);
+        proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
+        proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
+        boolean partial = true;
+        if ((mFlags & FLAG_SHUTDOWN) != 0) {
+            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
+            partial = false;
+        }
+        if ((mFlags & FLAG_SYSPROPS) != 0) {
+            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
+            partial = false;
+        }
+        if ((mFlags & FLAG_COMPLETE) != 0) {
+            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
+            partial = false;
+        }
+        if (partial) {
+            proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
+        }
+    }
+
     final public static class ProcessStateHolder {
         public final long appVersion;
         public ProcessState state;
diff --git a/core/java/com/android/internal/app/procstats/PssTable.java b/core/java/com/android/internal/app/procstats/PssTable.java
index fc93c3a..a6bae6e 100644
--- a/core/java/com/android/internal/app/procstats/PssTable.java
+++ b/core/java/com/android/internal/app/procstats/PssTable.java
@@ -16,17 +16,17 @@
 
 package com.android.internal.app.procstats;
 
+import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
 import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_AVERAGE;
 import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_MAXIMUM;
 import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_MINIMUM;
 import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
-import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
-import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
 import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
 import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
 
 import android.service.procstats.ProcessStatsStateProto;
 import android.util.proto.ProtoOutputStream;
@@ -133,7 +133,6 @@
             }
 
             if (stats[statsIndex + PSS_RSS_MINIMUM] > minRss) {
-                stats[statsIndex + PSS_RSS_MINIMUM] = minRss;
             }
 
             stats[statsIndex + PSS_RSS_AVERAGE] = (long)(((stats[statsIndex + PSS_RSS_AVERAGE]
@@ -167,4 +166,10 @@
                 stats[statsIndex + PSS_RSS_AVERAGE],
                 stats[statsIndex + PSS_RSS_MAXIMUM]);
     }
+
+    long[] getRssMeanAndMax(int key) {
+        final long[] stats = getArrayForKey(key);
+        final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+        return new long[]{stats[statsIndex + PSS_RSS_AVERAGE], stats[statsIndex + PSS_RSS_MAXIMUM]};
+    }
 }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 608925578..55ea315 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -250,7 +250,7 @@
     reserved 3; // activity
     optional bool fills_parent = 4;
     optional .android.graphics.RectProto bounds = 5;
-    optional .android.graphics.RectProto displayed_bounds = 6;
+    optional .android.graphics.RectProto displayed_bounds = 6 [deprecated=true];
     optional bool defer_removal = 7;
     optional int32 surface_width = 8;
     optional int32 surface_height = 9;
diff --git a/core/proto/android/service/procstats_enum.proto b/core/proto/android/service/procstats_enum.proto
index cc3fe5a..2abf373 100644
--- a/core/proto/android/service/procstats_enum.proto
+++ b/core/proto/android/service/procstats_enum.proto
@@ -76,3 +76,27 @@
     SERVICE_OPERATION_STATE_BOUND = 4;
     SERVICE_OPERATION_STATE_EXECUTING = 5;
 }
+
+// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
+// and not frameworks/base/core/java/android/app/ActivityManager.java
+enum AggregatedProcessState {
+    AGGREGATED_PROCESS_STATE_UNKNOWN = 0;
+    // Persistent system process; PERSISTENT or PERSISTENT_UI in ActivityManager
+    AGGREGATED_PROCESS_STATE_PERSISTENT = 1;
+    // Top activity; actually any visible activity; TOP or TOP_SLEEPING in ActivityManager
+    AGGREGATED_PROCESS_STATE_TOP = 2;
+    // Bound top foreground process; BOUND_TOP or BOUND_FOREGROUND_SERVICE in ActivityManager
+    AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+    // Important foreground process; FOREGROUND_SERVICE in ActivityManager
+    AGGREGATED_PROCESS_STATE_FGS = 4;
+    // Important foreground process ; IMPORTANT_FOREGROUND in ActivityManager
+    AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+    // Various background processes; IMPORTANT_BACKGROUND, TRANSIENT_BACKGROUND, BACKUP, SERVICE,
+    // HEAVY_WEIGHT in ActivityManager
+    AGGREGATED_PROCESS_STATE_BACKGROUND = 6;
+    // Process running a receiver; RECEIVER in ActivityManager
+    AGGREGATED_PROCESS_STATE_RECEIVER = 7;
+    // Various cached processes; HOME, LAST_ACTIVITY, CACHED_ACTIVITY, CACHED_RECENT,
+    // CACHED_ACTIVITY_CLIENT, CACHED_EMPTY in ActivityManager
+    AGGREGATED_PROCESS_STATE_CACHED = 8;
+}
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 6f36aae..ece59e2 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -143,34 +143,35 @@
         android:layout_height="match_parent"
         android:layout_width="wrap_content"
         android:layout_marginStart="6dp"
-        android:orientation="horizontal" >
-        <ImageButton
+        android:background="?android:selectableItemBackgroundBorderless"
+        android:orientation="horizontal">
+        <ImageView
             android:id="@+id/camera"
             android:layout_width="?attr/notificationHeaderIconSize"
             android:layout_height="?attr/notificationHeaderIconSize"
             android:src="@drawable/ic_camera"
-            android:background="?android:selectableItemBackgroundBorderless"
             android:visibility="gone"
+            android:focusable="false"
             android:contentDescription="@string/notification_appops_camera_active"
             />
-        <ImageButton
+        <ImageView
             android:id="@+id/mic"
             android:layout_width="?attr/notificationHeaderIconSize"
             android:layout_height="?attr/notificationHeaderIconSize"
             android:src="@drawable/ic_mic"
-            android:background="?android:selectableItemBackgroundBorderless"
             android:layout_marginStart="4dp"
             android:visibility="gone"
+            android:focusable="false"
             android:contentDescription="@string/notification_appops_microphone_active"
             />
-        <ImageButton
+        <ImageView
             android:id="@+id/overlay"
             android:layout_width="?attr/notificationHeaderIconSize"
             android:layout_height="?attr/notificationHeaderIconSize"
             android:src="@drawable/ic_alert_window_layer"
-            android:background="?android:selectableItemBackgroundBorderless"
             android:layout_marginStart="4dp"
             android:visibility="gone"
+            android:focusable="false"
             android:contentDescription="@string/notification_appops_overlay_active"
             />
     </LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index e986b18..8a57e05 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -195,35 +195,36 @@
                         android:layout_width="wrap_content"
                         android:paddingTop="3dp"
                         android:layout_marginStart="2dp"
+                        android:background="?android:selectableItemBackgroundBorderless"
                         android:orientation="horizontal" >
-                        <ImageButton
+                        <ImageView
                             android:layout_marginStart="4dp"
                             android:id="@+id/camera"
                             android:layout_width="?attr/notificationHeaderIconSize"
                             android:layout_height="?attr/notificationHeaderIconSize"
                             android:src="@drawable/ic_camera"
-                            android:background="?android:selectableItemBackgroundBorderless"
                             android:visibility="gone"
+                            android:focusable="false"
                             android:contentDescription="@string/notification_appops_camera_active"
                             />
-                        <ImageButton
+                        <ImageView
                             android:id="@+id/mic"
                             android:layout_width="?attr/notificationHeaderIconSize"
                             android:layout_height="?attr/notificationHeaderIconSize"
                             android:src="@drawable/ic_mic"
-                            android:background="?android:selectableItemBackgroundBorderless"
                             android:layout_marginStart="4dp"
                             android:visibility="gone"
+                            android:focusable="false"
                             android:contentDescription="@string/notification_appops_microphone_active"
                             />
-                        <ImageButton
+                        <ImageView
                             android:id="@+id/overlay"
                             android:layout_width="?attr/notificationHeaderIconSize"
                             android:layout_height="?attr/notificationHeaderIconSize"
                             android:src="@drawable/ic_alert_window_layer"
-                            android:background="?android:selectableItemBackgroundBorderless"
                             android:layout_marginStart="4dp"
                             android:visibility="gone"
+                            android:focusable="false"
                             android:contentDescription="@string/notification_appops_overlay_active"
                             />
                     </LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fba2379..8e99356 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3447,8 +3447,9 @@
          TODO: move to input HAL once ready. -->
     <string name="config_doubleTouchGestureEnableFile"></string>
 
-    <!-- Package of the unbundled tv remote service which can connect to tv
-         remote provider -->
+    <!-- Comma-separated list of unbundled packages which can connect to the
+         tv remote provider. The tv remote service is an example of such a
+         service. -->
     <string name="config_tvRemoteServicePackage" translatable="false"></string>
 
     <!-- True if the device supports persisting security logs across reboots.
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index a9f251e..89cc6e7 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -528,6 +528,47 @@
     }
 
     @Test
+    public void testCursorDrag_multiTouch() throws Throwable {
+        String text = "line1: This is the 1st line: A";
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Editor editor = tv.getEditorForTesting();
+        final int startIndex = text.indexOf("1st line");
+        Layout layout = tv.getLayout();
+        final float cursorStartX =
+                layout.getPrimaryHorizontal(startIndex) + tv.getTotalPaddingLeft();
+        final float cursorStartY = layout.getLineTop(1) + tv.getTotalPaddingTop();
+
+        // Taps to show the insertion handle.
+        tapAtPoint(tv, cursorStartX, cursorStartY);
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+        View handleView = editor.getInsertionController().getHandle();
+
+        // Taps & holds the insertion handle.
+        long handleDownTime = sTicker.addAndGet(10_000);
+        long eventTime = handleDownTime;
+        dispatchTouchEvent(handleView, downEvent(handleView, handleDownTime, eventTime++, 0, 0));
+
+        // Tries to Drag the cursor, with the pointer id > 0 (meaning the 2nd finger).
+        long cursorDownTime = eventTime++;
+        dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+                tv, MotionEvent.ACTION_DOWN, cursorDownTime, eventTime++, 1,
+                cursorStartX - 50, cursorStartY));
+        dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+                tv, MotionEvent.ACTION_MOVE, cursorDownTime, eventTime++, 1,
+                cursorStartX - 100, cursorStartY));
+        dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+                tv, MotionEvent.ACTION_UP, cursorDownTime, eventTime++, 1,
+                cursorStartX - 100, cursorStartY));
+
+        // Checks the cursor drag doesn't work while the handle is being hold.
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+
+        // Finger up on the  insertion handle.
+        dispatchTouchEvent(handleView, upEvent(handleView, handleDownTime, eventTime, 0, 0));
+    }
+
+    @Test
     public void testCursorDrag_snapDistance() throws Throwable {
         String text = "line1: This is the 1st line: A\n"
                 + "line2: This is the 2nd line: B\n"
@@ -626,6 +667,24 @@
         return event;
     }
 
+    private MotionEvent obtainTouchEventWithPointerId(
+            View view, int action, long downTime, long eventTime, int pointerId, float x, float y) {
+        Rect r = new Rect();
+        view.getBoundsOnScreen(r);
+        float rawX = x + r.left;
+        float rawY = y + r.top;
+        MotionEvent.PointerCoords coordinates = new MotionEvent.PointerCoords();
+        coordinates.x = rawX;
+        coordinates.y = rawY;
+        MotionEvent event = MotionEvent.obtain(
+                downTime, eventTime, action, 1, new int[] {pointerId},
+                new MotionEvent.PointerCoords[] {coordinates},
+                0, 1f, 1f, 0, 0, 0, 0);
+        view.toLocalMotionEvent(event);
+        mMotionEvents.add(event);
+        return event;
+    }
+
     private MotionEvent obtainMouseEvent(
             View view, int action, long downTime, long eventTime, float x, float y) {
         MotionEvent event = obtainTouchEvent(view, action, downTime, eventTime, x, y);
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 4a33da6..b21504c 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -462,6 +462,7 @@
         configureValidShortcutService();
         configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
         configureRequestAccessibilityButton();
+        configureEnabledService();
         Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
         getController().performAccessibilityShortcut();
 
@@ -610,6 +611,11 @@
         }).when(mHandler).sendMessageAtTime(any(), anyLong());
     }
 
+    private void configureEnabledService() throws Exception {
+        when(mAccessibilityManagerService.getEnabledAccessibilityServiceList(anyInt(), anyInt()))
+                .thenReturn(Collections.singletonList(mServiceInfo));
+    }
+
     private AccessibilityShortcutController getController() {
         AccessibilityShortcutController accessibilityShortcutController =
                 new AccessibilityShortcutController(mContext, mHandler, 0);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index eb39d58..07aa654 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -38,13 +38,16 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertFalse;
 
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.RelativeLayout;
+import android.widget.TextView;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.Espresso;
@@ -543,6 +546,51 @@
         assertThat(activity.getWorkListAdapter().getCount(), is(4));
     }
 
+    @Test
+    public void testWorkTab_headerIsVisibleInPersonalTab() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(1);
+        List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createOpenWebsiteIntent();
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        TextView headerText = activity.findViewById(R.id.title);
+        String initialText = headerText.getText().toString();
+        assertFalse(initialText.isEmpty(), "Header text is empty.");
+        assertThat(headerText.getVisibility(), is(View.VISIBLE));
+    }
+
+    @Test
+    public void testWorkTab_switchTabs_headerStaysSame() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(1);
+        List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createOpenWebsiteIntent();
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        TextView headerText = activity.findViewById(R.id.title);
+        String initialText = headerText.getText().toString();
+        onView(withText(R.string.resolver_work_tab))
+                .perform(click());
+
+        waitForIdle();
+        String currentText = headerText.getText().toString();
+        assertThat(headerText.getVisibility(), is(View.VISIBLE));
+        assertThat(String.format("Header text is not the same when switching tabs, personal profile"
+                        + " header was %s but work profile header is %s", initialText, currentText),
+                TextUtils.equals(initialText, currentText));
+    }
+
     @Ignore // b/148156663
     @Test
     public void testWorkTab_noPersonalApps_canStartWorkApps()
@@ -761,6 +809,13 @@
         return sendIntent;
     }
 
+    private Intent createOpenWebsiteIntent() {
+        Intent sendIntent = new Intent();
+        sendIntent.setAction(Intent.ACTION_VIEW);
+        sendIntent.setData(Uri.parse("https://google.com"));
+        return sendIntent;
+    }
+
     private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
         List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
         for (int i = 0; i < numberOfResults; i++) {
diff --git a/media/OWNERS b/media/OWNERS
index be60583..a16373e 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,7 +1,7 @@
 andrewlewis@google.com
 chz@google.com
-dwkang@google.com
 elaurent@google.com
+essick@google.com
 etalvala@google.com
 gkasten@google.com
 hdmoon@google.com
@@ -15,7 +15,7 @@
 lajos@google.com
 marcone@google.com
 sungsoo@google.com
-wjia@google.com
+wonsik@google.com
 
 # For maintaining sync with AndroidX code
 per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index a31f177..362dfa0 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -132,6 +132,8 @@
     jmethodID asReadOnlyBufferId;
     jmethodID positionId;
     jmethodID limitId;
+    jmethodID getPositionId;
+    jmethodID getLimitId;
 } gByteBufferInfo;
 
 static struct {
@@ -2033,13 +2035,11 @@
         if (env->IsInstanceOf(jvalue.get(), sFields.mStringClass)) {
             const char *tmp = env->GetStringUTFChars((jstring)jvalue.get(), nullptr);
             AString value;
-            if (tmp) {
-                value.setTo(tmp);
-            }
-            env->ReleaseStringUTFChars((jstring)jvalue.get(), tmp);
-            if (value.empty()) {
+            if (!tmp) {
                 return NO_MEMORY;
             }
+            value.setTo(tmp);
+            env->ReleaseStringUTFChars((jstring)jvalue.get(), tmp);
             result->setString(key.c_str(), value);
         } else if (env->IsInstanceOf(jvalue.get(), sFields.mIntegerClass)) {
             jint value = env->CallIntMethod(jvalue.get(), sFields.mIntegerValueId);
@@ -2051,8 +2051,8 @@
             jfloat value = env->CallFloatMethod(jvalue.get(), sFields.mFloatValueId);
             result->setFloat(key.c_str(), value);
         } else if (env->IsInstanceOf(jvalue.get(), gByteBufferInfo.clazz)) {
-            jint position = env->CallIntMethod(jvalue.get(), gByteBufferInfo.positionId);
-            jint limit = env->CallIntMethod(jvalue.get(), gByteBufferInfo.limitId);
+            jint position = env->CallIntMethod(jvalue.get(), gByteBufferInfo.getPositionId);
+            jint limit = env->CallIntMethod(jvalue.get(), gByteBufferInfo.getLimitId);
             sp<ABuffer> buffer{new ABuffer(limit - position)};
             void *data = env->GetDirectBufferAddress(jvalue.get());
             if (data != nullptr) {
@@ -2773,6 +2773,14 @@
             clazz.get(), "limit", "(I)Ljava/nio/Buffer;");
     CHECK(gByteBufferInfo.limitId != NULL);
 
+    gByteBufferInfo.getPositionId = env->GetMethodID(
+            clazz.get(), "position", "()I");
+    CHECK(gByteBufferInfo.getPositionId != NULL);
+
+    gByteBufferInfo.getLimitId = env->GetMethodID(
+            clazz.get(), "limit", "()I");
+    CHECK(gByteBufferInfo.getLimitId != NULL);
+
     clazz.reset(env->FindClass("java/util/ArrayList"));
     CHECK(clazz.get() != NULL);
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index c11e1a0..6fbee16 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -30,13 +30,17 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.permission.IPermissionManager;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * Select which activity is the first visible activity of the installation and forward the intent to
  * it.
@@ -47,6 +51,7 @@
     private static final String DOWNLOADS_AUTHORITY = "downloads";
     private IPackageManager mIPackageManager;
     private IPermissionManager mIPermissionManager;
+    private UserManager mUserManager;
     private boolean mAbortInstall = false;
 
     @Override
@@ -54,6 +59,7 @@
         super.onCreate(savedInstanceState);
         mIPackageManager = AppGlobals.getPackageManager();
         mIPermissionManager = AppGlobals.getPermissionManager();
+        mUserManager = getSystemService(UserManager.class);
         Intent intent = getIntent();
         String callingPackage = getCallingPackage();
 
@@ -144,13 +150,16 @@
             if (packages == null) {
                 return false;
             }
+            final List<UserInfo> users = mUserManager.getUsers();
             for (String packageName : packages) {
-                try {
-                    if (uid == getPackageManager().getPackageUid(packageName, 0)) {
-                        return true;
+                for (UserInfo user : users) {
+                    try {
+                        if (uid == getPackageManager().getPackageUidAsUser(packageName, user.id)) {
+                            return true;
+                        }
+                    } catch (PackageManager.NameNotFoundException e) {
+                        // Ignore and try the next package
                     }
-                } catch (PackageManager.NameNotFoundException e) {
-                    // Ignore and try the next package
                 }
             }
         } catch (RemoteException rexc) {
diff --git a/packages/SystemUI/res/drawable-nodpi/controls_btn_star.xml b/packages/SystemUI/res/drawable-nodpi/controls_btn_star.xml
deleted file mode 100644
index cfe7838..0000000
--- a/packages/SystemUI/res/drawable-nodpi/controls_btn_star.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp">
-    <item android:state_checked="true"
-          android:drawable="@drawable/star_filled"
-          android:tint="@color/control_primary_text"/>
-    <item android:drawable="@drawable/star_outline"
-          android:tint="@color/control_primary_text"/>
-</selector>
diff --git a/packages/SystemUI/res/drawable-nodpi/star_filled.xml b/packages/SystemUI/res/drawable-nodpi/star_filled.xml
deleted file mode 100644
index 62802d3..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star_filled.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-    <path
-        android:pathData="M 11.99 0.027 L 8.628 8.382 L 0.027 9.15 L 6.559 15.111 L 4.597 23.97 L 11.99 19.27 L 19.383 23.97 L 17.421 15.111 L 23.953 9.15 L 15.352 8.382 Z"
-        android:fillColor="#FFFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable-nodpi/star_outline.xml b/packages/SystemUI/res/drawable-nodpi/star_outline.xml
deleted file mode 100644
index 13983c6..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star_outline.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-    <path
-        android:pathData="M 11.99 6.491 L 13.15 9.377 L 13.713 10.776 L 15.148 10.902 L 18.103 11.167 L 15.854 13.221 L 14.766 14.216 L 15.089 15.703 L 15.759 18.74 L 13.222 17.127 L 11.99 16.321 L 10.758 17.102 L 8.222 18.715 L 8.891 15.678 L 9.215 14.191 L 8.126 13.196 L 5.877 11.141 L 8.832 10.877 L 10.267 10.751 L 10.83 9.352 L 11.99 6.491 M 11.99 0.027 L 8.628 8.382 L 0.027 9.15 L 6.559 15.111 L 4.597 23.97 L 11.99 19.27 L 19.383 23.97 L 17.421 15.111 L 23.953 9.15 L 15.352 8.382 Z"
-        android:fillColor="#FFFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml
index 29b4efa..cf298b7 100644
--- a/packages/SystemUI/res/drawable/control_background.xml
+++ b/packages/SystemUI/res/drawable/control_background.xml
@@ -17,7 +17,8 @@
 */
 -->
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-  <item>
+  <item
+      android:id="@+id/background">
     <shape>
       <solid android:color="@color/control_default_background" />
       <corners android:radius="@dimen/control_corner_radius" />
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 6a86213..fd75d91 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -101,7 +101,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="bottom|end"
-        android:button="@drawable/controls_btn_star"
         android:background="@android:color/transparent"
         android:clickable="false"
         android:selectable="false"
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 9dc502e..f8a96e7 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -108,7 +108,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_weight="1"
-                    style="@style/TextAppearance.NotificationImportanceChannel"/>
+                    style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
             </LinearLayout>
             <TextView
                 android:id="@+id/delegate_name"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 5b36382..e8e0133 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -84,7 +84,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_weight="1"
-                    style="@style/TextAppearance.NotificationImportanceChannel"/>
+                    style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
             </LinearLayout>
             <TextView
                 android:id="@+id/delegate_name"
diff --git a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
new file mode 100644
index 0000000..ccb4f78
--- /dev/null
+++ b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
@@ -0,0 +1,194 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/onboarding_half_shell_container"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal|bottom"
+    android:paddingStart="4dp"
+    android:paddingEnd="4dp"
+    >
+
+    <LinearLayout
+        android:id="@+id/half_shell"
+        android:layout_width="@dimen/qs_panel_width"
+        android:layout_height="wrap_content"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:orientation="vertical"
+        android:gravity="bottom"
+        android:layout_gravity="center_horizontal|bottom"
+        android:background="@drawable/rounded_bg_full"
+        >
+
+        <!--  We have a known number of rows that can be shown; just design them all here -->
+        <LinearLayout
+            android:id="@+id/show_at_top_tip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="4dp"
+            android:orientation="horizontal"
+            >
+            <ImageView
+                android:id="@+id/bell_icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="center_vertical"
+                android:src="@drawable/ic_notifications_alert"
+                android:tint="?android:attr/colorControlNormal" />
+
+            <TextView
+                android:id="@+id/show_at_top_text"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp"
+                android:gravity="center_vertical|start"
+                android:textSize="15sp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:text="@string/priority_onboarding_show_at_top_text"
+                style="@style/TextAppearance.NotificationInfo"
+                />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/show_avatar_tip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="4dp"
+            android:orientation="horizontal"
+            >
+            <ImageView
+                android:id="@+id/avatar_icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="center_vertical"
+                android:src="@drawable/ic_person"
+                android:tint="?android:attr/colorControlNormal" />
+
+            <TextView
+                android:id="@+id/avatar_text"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp"
+                android:gravity="center_vertical|start"
+                android:textSize="15sp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:text="@string/priority_onboarding_show_avatar_text"
+                style="@style/TextAppearance.NotificationInfo"
+                />
+
+        </LinearLayout>
+
+        <!-- These rows show optionally -->
+
+        <LinearLayout
+            android:id="@+id/floating_bubble_tip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="4dp"
+            android:orientation="horizontal"
+            >
+
+            <ImageView
+                android:id="@+id/bubble_icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="center_vertical"
+                android:src="@drawable/ic_create_bubble"
+                android:tint="?android:attr/colorControlNormal" />
+
+            <TextView
+                android:id="@+id/bubble_text"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp"
+                android:gravity="center_vertical|start"
+                android:textSize="15sp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:text="@string/priority_onboarding_appear_as_bubble_text"
+                style="@style/TextAppearance.NotificationInfo"
+                />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/ignore_dnd_tip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="4dp"
+            android:orientation="horizontal"
+            >
+
+            <ImageView
+                android:id="@+id/dnd_icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="center_vertical"
+                android:src="@drawable/moon"
+                android:tint="?android:attr/colorControlNormal" />
+
+            <TextView
+                android:id="@+id/dnd_text"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp"
+                android:gravity="center_vertical|start"
+                android:textSize="15sp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:text="@string/priority_onboarding_ignores_dnd_text"
+                style="@style/TextAppearance.NotificationInfo"
+                />
+
+        </LinearLayout>
+
+        <!-- Bottom button container -->
+        <RelativeLayout
+            android:id="@+id/button_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="4dp"
+            android:orientation="horizontal"
+            >
+            <TextView
+                android:id="@+id/done_button"
+                android:text="@string/priority_onboarding_done_button_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:gravity="end|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="125dp"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+
+        </RelativeLayout>
+
+    </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
index 716e127..e4b6e07 100644
--- a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
+++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
@@ -3,74 +3,9 @@
 // The actual wallpaper texture.
 uniform sampler2D uTexture;
 
-// The 85th percenile for the luminance histogram of the image (a value between 0 and 1).
-// This value represents the point in histogram that includes 85% of the pixels of the image.
-uniform float uPer85;
-
-// Reveal is the animation value that goes from 1 (the image is hidden) to 0 (the image is visible).
-uniform float uReveal;
-
-// The opacity of locked screen (constant value).
-uniform float uAod2Opacity;
 varying vec2 vTextureCoordinates;
 
-/*
- * Calculates the relative luminance of the pixel.
- */
-vec3 luminosity(vec3 color) {
-    float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
-    return vec3(lum);
-}
-
-vec4 transform(vec3 diffuse) {
-    // Getting the luminance for this pixel
-    vec3 lum = luminosity(diffuse);
-
-    /*
-     * while the reveal > per85, it shows the luminance image (B&W image)
-     * then when moving passed that value, the image gets colored.
-     */
-    float trans = smoothstep(0., uPer85, uReveal);
-    diffuse = mix(diffuse, lum, trans);
-
-    // 'lower' value represents the capped 'reveal' value to the range [0, per85]
-    float selector = step(uPer85, uReveal);
-    float lower = mix(uReveal, uPer85, selector);
-
-    /*
-     * Remaps image:
-     * - from reveal=1 to reveal=per85 => lower=per85, diffuse=luminance
-     *   That means that remaps black and white image pixel
-     *   from a possible values of [0,1] to [per85, 1] (if the pixel is darker than per85,
-     *   it's gonna be black, if it's between per85 and 1, it's gonna be gray
-     *   and if it's 1 it's gonna be white).
-     * - from reveal=per85 to reveal=0 => lower=reveal, 'diffuse' changes from luminance to color
-     *   That means that remaps each image pixel color (rgb)
-     *   from a possible values of [0,1] to [lower, 1] (if the pixel color is darker than 'lower',
-     *   it's gonna be 0, if it's between 'lower' and 1, it's gonna be remap to a value
-     *   between 0 and 1 and if it's 1 it's gonna be 1).
-     * - if reveal=0 => lower=0, diffuse=color image
-     *   The image is shown as it is, colored.
-     */
-    vec3 remaps = smoothstep(lower, 1., diffuse);
-
-    // Interpolate between diffuse and remaps using reveal to avoid over saturation.
-    diffuse = mix(diffuse, remaps, uReveal);
-
-    /*
-     * Fades in the pixel value:
-     * - if reveal=1 => fadeInOpacity=0
-     * - from reveal=1 to reveal=per85 => 0<=fadeInOpacity<=1
-     * - if reveal>per85 => fadeInOpacity=1
-     */
-    float fadeInOpacity = 1. - smoothstep(uPer85, 1., uReveal);
-    diffuse *= uAod2Opacity * fadeInOpacity;
-
-    return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
-}
-
 void main() {
     // gets the pixel value of the wallpaper for this uv coordinates on screen.
-    vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
-    gl_FragColor = transform(fragColor.rgb);
+    gl_FragColor = texture2D(uTexture, vTextureCoordinates);
 }
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e7ef8cc..599ed16 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1252,6 +1252,8 @@
     <dimen name="control_status_expanded">18sp</dimen>
     <dimen name="control_base_item_margin">2dp</dimen>
     <dimen name="control_status_padding">3dp</dimen>
+    <fraction name="controls_toggle_bg_intensity">5%</fraction>
+    <fraction name="controls_dimmed_alpha">40%</fraction>
 
     <!-- Home Controls activity view detail panel-->
     <dimen name="controls_activity_view_top_padding">25dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d2654d6..cb20e7a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1829,15 +1829,15 @@
     <!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary -->
     <string name="notification_channel_summary_default">Gets your attention with sound or vibration.</string>
 
+    <!-- [CHAR LIMIT=150] Conversation Notification Importance title: normal conversation level, with bubbling summary -->
+    <string name="notification_channel_summary_default_with_bubbles">Gets your attention with sound or vibration. Conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default.</string>
+
     <!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary -->
     <string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string>
 
     <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
     <string name="notification_channel_summary_priority">Shows at top of conversation section and appears as a bubble.</string>
 
-    <!--[CHAR LIMIT=150] Conversation inline controls footer shown when all conversations from the app are allowed to show as bubbles -->
-    <string name="notification_conversation_channel_all_bubble">All conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default. Manage in <xliff:g id="app_name" example="Settings">%2$s</xliff:g>.</string>
-
     <!--[CHAR LIMIT=30] Linkable text to Settings app -->
     <string name="notification_conversation_channel_settings">Settings</string>
 
@@ -2639,6 +2639,18 @@
     <!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] -->
     <string name="inattentive_sleep_warning_title">Standby</string>
 
+    <!-- Priority conversation onboarding screen -->
+    <!--  Text explaining that priority conversations show at the top of the conversation section [CHAR LIMIT=50]  -->
+    <string name="priority_onboarding_show_at_top_text">Show at top of conversation section</string>
+    <!--  Text explaining that priority conversations show an avatar on the lock screen [CHAR LIMIT=50]  -->
+    <string name="priority_onboarding_show_avatar_text">Show profile picture on lock screen</string>
+    <!--  Text explaining that priority conversations will appear as a bubble [CHAR LIMIT=50]  -->
+    <string name="priority_onboarding_appear_as_bubble_text">Appear as a floating bubble on top of apps</string>
+    <!--  Text explaining that priority conversations can interrupt DnD settings [CHAR LIMIT=50]  -->
+    <string name="priority_onboarding_ignores_dnd_text">Interrupt Do Not Disturb</string>
+    <!--  Title for the affirmative button [CHAR LIMIT=50]  -->
+    <string name="priority_onboarding_done_button_title">Got it</string>
+
     <!-- Window Magnification strings -->
     <!-- Title for Magnification Overlay Window [CHAR LIMIT=NONE] -->
     <string name="magnification_overlay_title">Magnification Overlay Window</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 118aa5b..7e24f5d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -564,7 +564,7 @@
     <style name="TextAppearance.NotificationImportanceButton">
         <item name="android:textSize">@dimen/notification_importance_button_text</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
-        <item name="android:textColor">?android:attr/colorAccent</item>
+        <item name="android:textColor">@color/notification_guts_priority_contents</item>
         <item name="android:gravity">center</item>
     </style>
 
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 5442299..71ec33e 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui;
 
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -27,17 +24,12 @@
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.util.Size;
-import android.view.DisplayInfo;
 import android.view.SurfaceHolder;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.glwallpaper.EglHelper;
 import com.android.systemui.glwallpaper.GLWallpaperRenderer;
 import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.DozeParameters;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -53,16 +45,12 @@
     // We delayed destroy render context that subsequent render requests have chance to cancel it.
     // This is to avoid destroying then recreating render context in a very short time.
     private static final int DELAY_FINISH_RENDERING = 1000;
-    private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
-    private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
-    private static final boolean DEBUG = true;
-    private final DozeParameters mDozeParameters;
+    private static final boolean DEBUG = false;
     private HandlerThread mWorker;
 
     @Inject
-    public ImageWallpaper(DozeParameters dozeParameters) {
+    public ImageWallpaper() {
         super();
-        mDozeParameters = dozeParameters;
     }
 
     @Override
@@ -74,7 +62,7 @@
 
     @Override
     public Engine onCreateEngine() {
-        return new GLEngine(this, mDozeParameters);
+        return new GLEngine();
     }
 
     @Override
@@ -84,7 +72,7 @@
         mWorker = null;
     }
 
-    class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
+    class GLEngine extends Engine {
         // Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
         // set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
         @VisibleForTesting
@@ -94,40 +82,15 @@
 
         private GLWallpaperRenderer mRenderer;
         private EglHelper mEglHelper;
-        private StatusBarStateController mController;
         private final Runnable mFinishRenderingTask = this::finishRendering;
-        private boolean mShouldStopTransition;
-        private final DisplayInfo mDisplayInfo = new DisplayInfo();
-        private final Object mMonitor = new Object();
-        @VisibleForTesting
-        boolean mIsHighEndGfx;
-        private boolean mDisplayNeedsBlanking;
-        private boolean mNeedTransition;
         private boolean mNeedRedraw;
-        // This variable can only be accessed in synchronized block.
-        private boolean mWaitingForRendering;
 
-        GLEngine(Context context, DozeParameters dozeParameters) {
-            init(dozeParameters);
+        GLEngine() {
         }
 
         @VisibleForTesting
-        GLEngine(DozeParameters dozeParameters, Handler handler) {
+        GLEngine(Handler handler) {
             super(SystemClock::elapsedRealtime, handler);
-            init(dozeParameters);
-        }
-
-        private void init(DozeParameters dozeParameters) {
-            mIsHighEndGfx = ActivityManager.isHighEndGfx();
-            mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
-            mNeedTransition = false;
-
-            // We will preserve EGL context when we are in lock screen or aod
-            // to avoid janking in following transition, we need to release when back to home.
-            mController = Dependency.get(StatusBarStateController.class);
-            if (mController != null) {
-                mController.addCallback(this /* StateListener */);
-            }
         }
 
         @Override
@@ -135,9 +98,8 @@
             mEglHelper = getEglHelperInstance();
             // Deferred init renderer because we need to get wallpaper by display context.
             mRenderer = getRendererInstance();
-            getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
             setFixedSizeAllowed(true);
-            setOffsetNotificationsEnabled(mNeedTransition);
+            setOffsetNotificationsEnabled(false);
             updateSurfaceSize();
         }
 
@@ -146,7 +108,7 @@
         }
 
         ImageWallpaperRenderer getRendererInstance() {
-            return new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
+            return new ImageWallpaperRenderer(getDisplayContext());
         }
 
         private void updateSurfaceSize() {
@@ -157,79 +119,13 @@
             holder.setFixedSize(width, height);
         }
 
-        /**
-         * Check if necessary to stop transition with current wallpaper on this device. <br/>
-         * This should only be invoked after {@link #onSurfaceCreated(SurfaceHolder)}}
-         * is invoked since it needs display context and surface frame size.
-         * @return true if need to stop transition.
-         */
-        @VisibleForTesting
-        boolean checkIfShouldStopTransition() {
-            int orientation = getDisplayContext().getResources().getConfiguration().orientation;
-            Rect frame = getSurfaceHolder().getSurfaceFrame();
-            Rect display = new Rect();
-            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
-                display.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-            } else {
-                display.set(0, 0, mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
-            }
-            return mNeedTransition
-                    && (frame.width() < display.width() || frame.height() < display.height());
-        }
-
-        @Override
-        public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
-                float yOffsetStep, int xPixelOffset, int yPixelOffset) {
-            if (mWorker == null) return;
-            mWorker.getThreadHandler().post(() -> mRenderer.updateOffsets(xOffset, yOffset));
-        }
-
-        @Override
-        public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
-            if (mWorker == null || !mNeedTransition) return;
-            final long duration = mShouldStopTransition ? 0 : animationDuration;
-            if (DEBUG) {
-                Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
-                        + ", duration=" + duration
-                        + ", mShouldStopTransition=" + mShouldStopTransition);
-            }
-            mWorker.getThreadHandler().post(
-                    () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
-            if (inAmbientMode && animationDuration == 0) {
-                // This means that we are transiting from home to aod, to avoid
-                // race condition between window visibility and transition,
-                // we don't return until the transition is finished. See b/136643341.
-                waitForBackgroundRendering();
-            }
-        }
-
         @Override
         public boolean shouldZoomOutWallpaper() {
             return true;
         }
 
-        private void waitForBackgroundRendering() {
-            synchronized (mMonitor) {
-                try {
-                    mWaitingForRendering = true;
-                    for (int patience = 1; mWaitingForRendering; patience++) {
-                        mMonitor.wait(INTERVAL_WAIT_FOR_RENDERING);
-                        mWaitingForRendering &= patience < PATIENCE_WAIT_FOR_RENDERING;
-                    }
-                } catch (InterruptedException ex) {
-                } finally {
-                    mWaitingForRendering = false;
-                }
-            }
-        }
-
         @Override
         public void onDestroy() {
-            if (mController != null) {
-                mController.removeCallback(this /* StateListener */);
-            }
-            mController = null;
-
             mWorker.getThreadHandler().post(() -> {
                 mRenderer.finish();
                 mRenderer = null;
@@ -240,7 +136,6 @@
 
         @Override
         public void onSurfaceCreated(SurfaceHolder holder) {
-            mShouldStopTransition = checkIfShouldStopTransition();
             if (mWorker == null) return;
             mWorker.getThreadHandler().post(() -> {
                 mEglHelper.init(holder, needSupportWideColorGamut());
@@ -251,32 +146,13 @@
         @Override
         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
             if (mWorker == null) return;
-            mWorker.getThreadHandler().post(() -> {
-                mRenderer.onSurfaceChanged(width, height);
-                mNeedRedraw = true;
-            });
+            mWorker.getThreadHandler().post(() -> mRenderer.onSurfaceChanged(width, height));
         }
 
         @Override
         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
             if (mWorker == null) return;
-            if (DEBUG) {
-                Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw);
-            }
-
-            mWorker.getThreadHandler().post(() -> {
-                if (mNeedRedraw) {
-                    drawFrame();
-                    mNeedRedraw = false;
-                }
-            });
-        }
-
-        @Override
-        public void onVisibilityChanged(boolean visible) {
-            if (DEBUG) {
-                Log.d(TAG, "wallpaper visibility changes: " + visible);
-            }
+            mWorker.getThreadHandler().post(this::drawFrame);
         }
 
         private void drawFrame() {
@@ -285,15 +161,6 @@
             postRender();
         }
 
-        @Override
-        public void onStatePostChange() {
-            // When back to home, we try to release EGL, which is preserved in lock screen or aod.
-            if (mWorker != null && mController.getState() == StatusBarState.SHADE) {
-                mWorker.getThreadHandler().post(this::scheduleFinishRendering);
-            }
-        }
-
-        @Override
         public void preRender() {
             // This method should only be invoked from worker thread.
             Trace.beginSection("ImageWallpaper#preRender");
@@ -330,7 +197,6 @@
             }
         }
 
-        @Override
         public void requestRender() {
             // This method should only be invoked from worker thread.
             Trace.beginSection("ImageWallpaper#requestRender");
@@ -355,27 +221,13 @@
             }
         }
 
-        @Override
         public void postRender() {
             // This method should only be invoked from worker thread.
             Trace.beginSection("ImageWallpaper#postRender");
-            notifyWaitingThread();
             scheduleFinishRendering();
             Trace.endSection();
         }
 
-        private void notifyWaitingThread() {
-            synchronized (mMonitor) {
-                if (mWaitingForRendering) {
-                    try {
-                        mWaitingForRendering = false;
-                        mMonitor.notify();
-                    } catch (IllegalMonitorStateException ex) {
-                    }
-                }
-            }
-        }
-
         private void cancelFinishRenderingTask() {
             if (mWorker == null) return;
             mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
@@ -391,18 +243,11 @@
             Trace.beginSection("ImageWallpaper#finishRendering");
             if (mEglHelper != null) {
                 mEglHelper.destroyEglSurface();
-                if (!needPreserveEglContext()) {
-                    mEglHelper.destroyEglContext();
-                }
+                mEglHelper.destroyEglContext();
             }
             Trace.endSection();
         }
 
-        private boolean needPreserveEglContext() {
-            return mNeedTransition && mController != null
-                    && mController.getState() == StatusBarState.KEYGUARD;
-        }
-
         private boolean needSupportWideColorGamut() {
             return mRenderer.isWcgContent();
         }
@@ -411,16 +256,6 @@
         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
             super.dump(prefix, fd, out, args);
             out.print(prefix); out.print("Engine="); out.println(this);
-            out.print(prefix); out.print("isHighEndGfx="); out.println(mIsHighEndGfx);
-            out.print(prefix); out.print("displayNeedsBlanking=");
-            out.println(mDisplayNeedsBlanking);
-            out.print(prefix); out.print("displayInfo="); out.print(mDisplayInfo);
-            out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
-            out.print(prefix); out.print("mShouldStopTransition=");
-            out.println(mShouldStopTransition);
-            out.print(prefix); out.print("StatusBarState=");
-            out.println(mController != null ? mController.getState() : "null");
-
             out.print(prefix); out.print("valid surface=");
             out.println(getSurfaceHolder() != null && getSurfaceHolder().getSurface() != null
                     ? getSurfaceHolder().getSurface().isValid()
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 6aa2326..87990cd 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -21,11 +21,25 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 
+import com.android.systemui.settings.CurrentUserContextTracker;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
 import java.util.Set;
 
+/**
+ * A helper class to store simple preferences for SystemUI. Its main use case is things such as
+ * feature education, e.g. "has the user seen this tooltip".
+ *
+ * As of this writing, feature education settings are *intentionally exempted* from backup and
+ * restore because there is not a great way to know which subset of features the user _should_ see
+ * again if, for instance, they are coming from multiple OSes back or switching OEMs.
+ *
+ * NOTE: Clients of this class should take care to pass in the correct user context when querying
+ * settings, otherwise you will always read/write for user 0 which is almost never what you want.
+ * See {@link CurrentUserContextTracker} for a simple way to get the current context
+ */
 public final class Prefs {
     private Prefs() {} // no instantation
 
@@ -109,6 +123,8 @@
         String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding";
         String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
         String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
+        /** Tracks whether the user has seen the onboarding screen for priority conversations */
+        String HAS_SEEN_PRIORITY_ONBOARDING = "HasSeenPriorityOnboarding";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 922fb69b..7861211 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -948,7 +948,12 @@
             int dw = flipped ? lh : lw;
             int dh = flipped ? lw : lh;
 
-            mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh));
+            Path path = DisplayCutout.pathFromResources(getResources(), dw, dh);
+            if (path != null) {
+                mBoundingPath.set(path);
+            } else {
+                mBoundingPath.reset();
+            }
             Matrix m = new Matrix();
             transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
             mBoundingPath.transform(m);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index d8a11d3..e6a62c2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics;
 
 import android.content.Context;
+import android.os.UserHandle;
 import android.text.InputType;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
@@ -68,6 +69,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
+        mPasswordField.setTextOperationUser(UserHandle.of(mUserId));
         if (mCredentialType == Utils.CREDENTIAL_PIN) {
             mPasswordField.setInputType(
                     InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 8bf2591..496e60d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -286,6 +286,7 @@
 
         if (matched) {
             mClearErrorRunnable.run();
+            mLockPatternUtils.userPresent(mEffectiveUserId);
             mCallback.onCredentialMatched(attestation);
         } else {
             if (timeoutMs > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 74b94e7..319a6e0 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -25,7 +25,6 @@
 import android.os.Message
 import android.os.UserHandle
 import android.text.TextUtils
-import android.util.Log
 import android.util.SparseArray
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.Dumpable
@@ -34,6 +33,7 @@
 import com.android.systemui.dump.DumpManager
 import java.io.FileDescriptor
 import java.io.PrintWriter
+import java.lang.IllegalStateException
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -189,8 +189,8 @@
                         data.user.identifier
                     }
                     if (userId < UserHandle.USER_ALL) {
-                        if (DEBUG) Log.w(TAG, "Register receiver for invalid user: $userId")
-                        return
+                        throw IllegalStateException(
+                                "Attempting to register receiver for invalid user {$userId}")
                     }
                     val uBR = receiversByUser.get(userId, createUBRForUser(userId))
                     receiversByUser.put(userId, uBR)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index da5c296..c8e9a68 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -108,7 +108,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -162,14 +161,6 @@
     // Used when ranking updates occur and we check if things should bubble / unbubble
     private NotificationListenerService.Ranking mTmpRanking;
 
-    // Saves notification keys of user created "fake" bubbles so that we can allow notifications
-    // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
-    private final HashSet<String> mUserCreatedBubbles;
-    // If we're auto-bubbling bubbles via a whitelist, we need to track which notifs from that app
-    // have been "demoted" back to a notification so that we don't auto-bubbles those again.
-    // Doesn't persist across reboots, not a long-term solution.
-    private final HashSet<String> mUserBlockedBubbles;
-
     // Bubbles get added to the status bar view
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final ZenModeController mZenModeController;
@@ -412,9 +403,6 @@
                     }
                 });
 
-        mUserCreatedBubbles = new HashSet<>();
-        mUserBlockedBubbles = new HashSet<>();
-
         mBubbleIconFactory = new BubbleIconFactory(context);
     }
 
@@ -474,8 +462,7 @@
                                 (entry != null && entry.isRowDismissed() && !isAppCancel)
                                 || isClearAll || isUserDimiss || isSummaryCancel;
 
-                        if (userRemovedNotif || isUserCreatedBubble(key)
-                                || isSummaryOfUserCreatedBubble(entry)) {
+                        if (userRemovedNotif) {
                             return handleDismissalInterception(entry);
                         }
 
@@ -860,27 +847,6 @@
     }
 
     /**
-     * Whether this bubble was explicitly created by the user via a SysUI affordance.
-     */
-    boolean isUserCreatedBubble(String key) {
-        return mUserCreatedBubbles.contains(key);
-    }
-
-    boolean isSummaryOfUserCreatedBubble(NotificationEntry entry) {
-        if (isSummaryOfBubbles(entry)) {
-            List<Bubble> bubbleChildren =
-                    mBubbleData.getBubblesInGroup(entry.getSbn().getGroupKey());
-            for (int i = 0; i < bubbleChildren.size(); i++) {
-                // Check if any are user-created (i.e. experimental bubbles)
-                if (isUserCreatedBubble(bubbleChildren.get(i).getKey())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
      * Removes the bubble with the given NotificationEntry.
      * <p>
      * Must be called from the main thread.
@@ -893,37 +859,19 @@
     }
 
     private void onEntryAdded(NotificationEntry entry) {
-        boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
-        boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
-        boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
-                mContext, entry, previouslyUserCreated, userBlocked);
-
         if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
-                && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
-            if (wasAdjusted && !previouslyUserCreated) {
-                // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
-                mUserCreatedBubbles.add(entry.getKey());
-            }
+                && canLaunchInActivityView(mContext, entry)) {
             updateBubble(entry);
         }
     }
 
     private void onEntryUpdated(NotificationEntry entry) {
-        boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
-        boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
-        boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
-                mContext, entry, previouslyUserCreated, userBlocked);
-
         boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
-                && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
+                && canLaunchInActivityView(mContext, entry);
         if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
             // It was previously a bubble but no longer a bubble -- lets remove it
             removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
         } else if (shouldBubble) {
-            if (wasAdjusted && !previouslyUserCreated) {
-                // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
-                mUserCreatedBubbles.add(entry.getKey());
-            }
             updateBubble(entry);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 93fb697..3524696 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -193,7 +193,7 @@
                         + " mActivityViewStatus=" + mActivityViewStatus
                         + " bubble=" + getBubbleKey());
             }
-            if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
+            if (mBubble != null) {
                 // Must post because this is called from a binder thread.
                 post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
                         BubbleController.DISMISS_TASK_FINISHED));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 41dbb48..2060391 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -57,16 +57,13 @@
 public class BubbleExperimentConfig {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
 
-    private static final String SHORTCUT_DUMMY_INTENT = "bubble_experiment_shortcut_intent";
-    private static PendingIntent sDummyShortcutIntent;
-
     private static final int BUBBLE_HEIGHT = 10000;
 
     private static final String ALLOW_ANY_NOTIF_TO_BUBBLE = "allow_any_notif_to_bubble";
     private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false;
 
     private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble";
-    private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = true;
+    private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false;
 
     private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble";
     private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1cabe22..0aabdff 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -374,8 +374,9 @@
                 @Override
                 public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
                     mExpandedAnimationController.dismissDraggedOutBubble(
-                            mExpandedAnimationController.getDraggedOutBubble(),
-                            BubbleStackView.this::dismissMagnetizedObject);
+                            mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
+                            mDismissTargetContainer.getHeight() /* translationYBy */,
+                            BubbleStackView.this::dismissMagnetizedObject /* after */);
                     hideDismissTarget();
                 }
             };
@@ -405,7 +406,8 @@
 
                 @Override
                 public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
-                    mStackAnimationController.implodeStack(
+                    mStackAnimationController.animateStackDismissal(
+                            mDismissTargetContainer.getHeight() /* translationYBy */,
                             () -> {
                                 resetDesaturationAndDarken();
                                 dismissMagnetizedObject();
@@ -954,6 +956,8 @@
         mVerticalPosPercentBeforeRotation =
                 (mStackAnimationController.getStackPosition().y - allowablePos.top)
                         / (allowablePos.bottom - allowablePos.top);
+        mVerticalPosPercentBeforeRotation =
+                Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation));
         addOnLayoutChangeListener(mOrientationChangedListener);
         hideFlyoutImmediate();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 501e502..c96f9a4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -139,22 +139,11 @@
             StatusBarNotification sbn = b.getEntry().getSbn();
             String packageName = sbn.getPackageName();
 
-            // Real shortcut info for this bubble
             String bubbleShortcutId =  b.getEntry().getBubbleMetadata().getShortcutId();
             if (bubbleShortcutId != null) {
-                info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c, packageName,
-                        sbn.getUser(), bubbleShortcutId);
-            } else {
-                // Check for experimental shortcut
-                String shortcutId = sbn.getNotification().getShortcutId();
-                if (BubbleExperimentConfig.useShortcutInfoToBubble(c) && shortcutId != null) {
-                    info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c,
-                            packageName,
-                            sbn.getUser(), shortcutId);
-                }
+                info.shortcutInfo = b.getEntry().getRanking().getShortcutInfo();
             }
 
-
             // App name & app icon
             PackageManager pm = c.getPackageManager();
             ApplicationInfo appInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index d974adc..0002e86 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -329,7 +329,7 @@
     }
 
     /** Plays a dismiss animation on the dragged out bubble. */
-    public void dismissDraggedOutBubble(View bubble, Runnable after) {
+    public void dismissDraggedOutBubble(View bubble, float translationYBy, Runnable after) {
         if (bubble == null) {
             return;
         }
@@ -337,6 +337,7 @@
                 .withStiffness(SpringForce.STIFFNESS_HIGH)
                 .scaleX(1.1f)
                 .scaleY(1.1f)
+                .translationY(bubble.getTranslationY() + translationYBy)
                 .alpha(0f, after)
                 .start();
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 00de8b4..5f3a2bd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -647,17 +647,18 @@
     }
 
     /**
-     * 'Implode' the stack by shrinking the bubbles via chained animations and fading them out.
+     * 'Implode' the stack by shrinking the bubbles, fading them out, and translating them down.
      */
-    public void implodeStack(Runnable after) {
-        // Pop and fade the bubbles sequentially.
-        animationForChildAtIndex(0)
-                .scaleX(0.5f)
-                .scaleY(0.5f)
-                .alpha(0f)
-                .withDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
-                .withStiffness(SpringForce.STIFFNESS_HIGH)
-                .start(after);
+    public void animateStackDismissal(float translationYBy, Runnable after) {
+        animationsForChildrenFromIndex(0, (index, animation) ->
+                animation
+                        .scaleX(0.5f)
+                        .scaleY(0.5f)
+                        .alpha(0f)
+                        .translationY(
+                                mLayout.getChildAt(index).getTranslationY() + translationYBy)
+                        .withStiffness(SpringForce.STIFFNESS_HIGH))
+                .startAll(after);
     }
 
     /**
@@ -710,8 +711,6 @@
         if (property.equals(DynamicAnimation.TRANSLATION_X)
                 || property.equals(DynamicAnimation.TRANSLATION_Y)) {
             return index + 1;
-        } else if (isStackStuckToTarget()) {
-            return index + 1; // Chain all animations in dismiss (scale, alpha, etc. are used).
         } else {
             return NONE;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 7cab847..79a5b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.UserAwareController
 import com.android.systemui.controls.management.ControlsFavoritingActivity
+import com.android.systemui.controls.ui.ControlWithState
 import com.android.systemui.controls.ui.ControlsUiController
 import java.util.function.Consumer
 
@@ -111,6 +112,13 @@
         @ControlAction.ResponseResult response: Int
     )
 
+    /**
+     * When a control should be highlighted, dimming down what's around it.
+     *
+     * @param cws focused control, or {@code null} if nothing should be highlighted.
+     */
+    fun onFocusChanged(cws: ControlWithState?)
+
     // FAVORITE MANAGEMENT
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 6d34009..5626a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.ui.ControlWithState
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
@@ -504,6 +505,10 @@
         }
     }
 
+    override fun onFocusChanged(cws: ControlWithState?) {
+        uiController.onFocusChanged(cws)
+    }
+
     override fun refreshStatus(componentName: ComponentName, control: Control) {
         if (!confirmAvailability()) {
             Log.d(TAG, "Controls not available")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index 2c1a91d..b3c6cab 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -25,6 +25,7 @@
 import android.util.Log
 import android.view.HapticFeedbackConstants
 import com.android.systemui.R
+import com.android.systemui.controls.controller.ControlsController
 
 object ControlActionCoordinator {
     const val MIN_LEVEL = 0
@@ -76,4 +77,8 @@
             it.show()
         }
     }
+
+    fun setFocusedElement(cvh: ControlViewHolder?, controlsController: ControlsController) {
+        controlsController.onFocusChanged(cvh?.cws)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 055adc6f..61a323d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -31,6 +31,7 @@
 import android.service.controls.templates.TemperatureControlTemplate
 import android.service.controls.templates.ToggleRangeTemplate
 import android.service.controls.templates.ToggleTemplate
+import android.util.MathUtils
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
@@ -66,7 +67,12 @@
         )
     }
 
+    private val toggleBackgroundIntensity: Float = layout.context.resources
+            .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)
+    private val dimmedAlpha: Float = layout.context.resources
+            .getFraction(R.fraction.controls_dimmed_alpha, 1, 1)
     private var stateAnimator: ValueAnimator? = null
+    private val baseLayer: GradientDrawable
     val icon: ImageView = layout.requireViewById(R.id.icon)
     val status: TextView = layout.requireViewById(R.id.status)
     val title: TextView = layout.requireViewById(R.id.title)
@@ -79,12 +85,18 @@
     var lastAction: ControlAction? = null
     val deviceType: Int
         get() = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType
+    var dimmed: Boolean = false
+        set(value) {
+            field = value
+            bindData(cws)
+        }
 
     init {
         val ld = layout.getBackground() as LayerDrawable
         ld.mutate()
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
         clipLayer.alpha = ALPHA_DISABLED
+        baseLayer = ld.findDrawableByLayerId(R.id.background) as GradientDrawable
         // needed for marquee to start
         status.setSelected(true)
     }
@@ -171,11 +183,13 @@
 
         val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)
 
-        val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
-        val (bg, newAlpha) = if (enabled) {
-            Pair(ri.enabledBackground, ALPHA_ENABLED)
+        val fg = context.resources.getColorStateList(ri.foreground, context.theme)
+        val bg = context.resources.getColor(R.color.control_default_background, context.theme)
+        val dimAlpha = if (dimmed) dimmedAlpha else 1f
+        var (clip, newAlpha) = if (enabled) {
+            listOf(ri.enabledBackground, ALPHA_ENABLED)
         } else {
-            Pair(R.color.control_default_background, ALPHA_DISABLED)
+            listOf(R.color.control_default_background, ALPHA_DISABLED)
         }
 
         status.setTextColor(fg)
@@ -187,14 +201,24 @@
         }
 
         (clipLayer.getDrawable() as GradientDrawable).apply {
-            val newColor = context.resources.getColor(bg, context.theme)
+            val newClipColor = context.resources.getColor(clip, context.theme)
+            val newBaseColor = if (behavior is ToggleRangeBehavior) {
+                ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity)
+            } else {
+                bg
+            }
             stateAnimator?.cancel()
             if (animated) {
-                val oldColor = color?.defaultColor ?: newColor
+                val oldColor = color?.defaultColor ?: newClipColor
+                val oldBaseColor = baseLayer.color?.defaultColor ?: newBaseColor
+                val oldAlpha = layout.alpha
                 stateAnimator = ValueAnimator.ofInt(clipLayer.alpha, newAlpha).apply {
                     addUpdateListener {
                         alpha = it.animatedValue as Int
-                        setColor(ColorUtils.blendARGB(oldColor, newColor, it.animatedFraction))
+                        setColor(ColorUtils.blendARGB(oldColor, newClipColor, it.animatedFraction))
+                        baseLayer.setColor(ColorUtils.blendARGB(oldBaseColor,
+                                newBaseColor, it.animatedFraction))
+                        layout.alpha = MathUtils.lerp(oldAlpha, dimAlpha, it.animatedFraction)
                     }
                     addListener(object : AnimatorListenerAdapter() {
                         override fun onAnimationEnd(animation: Animator?) {
@@ -207,7 +231,9 @@
                 }
             } else {
                 alpha = newAlpha
-                setColor(newColor)
+                setColor(newClipColor)
+                baseLayer.setColor(newBaseColor)
+                layout.alpha = dimAlpha
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 0f10537..61a1a98 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -36,4 +36,5 @@
         controlId: String,
         @ControlAction.ResponseResult response: Int
     )
+    fun onFocusChanged(controlWithState: ControlWithState?)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index fab6fc7..2adfb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -209,6 +209,20 @@
         }
     }
 
+    override fun onFocusChanged(focusedControl: ControlWithState?) {
+        controlViewsById.forEach { key: ControlKey, viewHolder: ControlViewHolder ->
+            val state = controlsById.get(key) ?: return@forEach
+            val shouldBeDimmed = focusedControl != null && state != focusedControl
+            if (viewHolder.dimmed == shouldBeDimmed) {
+                return@forEach
+            }
+
+            uiExecutor.execute {
+                viewHolder.dimmed = shouldBeDimmed
+            }
+        }
+    }
+
     private fun startFavoritingActivity(context: Context, si: StructureInfo) {
         startTargetedActivity(context, si, ControlsFavoritingActivity::class.java)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index d8b26e2..dafd8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -175,6 +175,7 @@
     fun beginUpdateRange() {
         status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
                 .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
+        ControlActionCoordinator.setFocusedElement(cvh, cvh.controlsController)
     }
 
     fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) {
@@ -243,6 +244,7 @@
         status.setText("$currentStatusText $currentRangeValue")
         cvh.action(FloatAction(rangeTemplate.getTemplateId(),
             findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
+        ControlActionCoordinator.setFocusedElement(null, cvh.controlsController)
     }
 
     fun findNearestStep(value: Float): Float {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 8c572fe..88f96a8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -24,6 +24,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.plugins.qs.QSFactory;
@@ -33,6 +34,7 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.settings.CurrentUserContextTracker;
 import com.android.systemui.stackdivider.DividerModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -136,4 +138,15 @@
     @Binds
     abstract KeyguardViewController bindKeyguardViewController(
             StatusBarKeyguardViewManager statusBarKeyguardViewManager);
+
+    @Singleton
+    @Provides
+    static CurrentUserContextTracker provideCurrentUserContextTracker(
+            Context context,
+            BroadcastDispatcher broadcastDispatcher) {
+        CurrentUserContextTracker tracker =
+                new CurrentUserContextTracker(context, broadcastDispatcher);
+        tracker.initialize();
+        return tracker;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 2c080b8..9bb253b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -395,6 +395,7 @@
         if (preferredComponent == null) {
             Log.i(TAG, "Controls seeding: No preferred component has been set, will not seed");
             mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, true).apply();
+            return;
         }
 
         mControlsController.seedFavoritesForComponent(
@@ -2316,4 +2317,4 @@
                 && mControlsUiController.getAvailable()
                 && !mControlsServiceInfos.isEmpty();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
index 88ab9ef4..6152490 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
@@ -49,20 +49,6 @@
     void onDrawFrame();
 
     /**
-     * Notify ambient mode is changed.
-     * @param inAmbientMode true if in ambient mode.
-     * @param duration duration of transition.
-     */
-    void updateAmbientMode(boolean inAmbientMode, long duration);
-
-    /**
-     * Notify the wallpaper offsets changed.
-     * @param xOffset offset along x axis.
-     * @param yOffset offset along y axis.
-     */
-    void updateOffsets(float xOffset, float yOffset);
-
-    /**
      * Ask renderer to report the surface size it needs.
      */
     Size reportSurfaceSize();
@@ -81,24 +67,4 @@
      */
     void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args);
 
-    /**
-     * A proxy which owns surface holder.
-     */
-    interface SurfaceProxy {
-
-        /**
-         * Ask proxy to start rendering frame to surface.
-         */
-        void requestRender();
-
-        /**
-         * Ask proxy to prepare render context.
-         */
-        void preRender();
-
-        /**
-         * Ask proxy to destroy render context.
-         */
-        void postRender();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
index 626d0cf..fa45ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -33,7 +33,6 @@
 import static android.opengl.GLES20.glVertexAttribPointer;
 
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.opengl.GLUtils;
 import android.util.Log;
 
@@ -50,14 +49,9 @@
 class ImageGLWallpaper {
     private static final String TAG = ImageGLWallpaper.class.getSimpleName();
 
-    static final String A_POSITION = "aPosition";
-    static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
-    static final String U_PER85 = "uPer85";
-    static final String U_REVEAL = "uReveal";
-    static final String U_AOD2OPACITY = "uAod2Opacity";
-    static final String U_TEXTURE = "uTexture";
-
-    private static final int HANDLE_UNDEFINED = -1;
+    private static final String A_POSITION = "aPosition";
+    private static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
+    private static final String U_TEXTURE = "uTexture";
     private static final int POSITION_COMPONENT_COUNT = 2;
     private static final int TEXTURE_COMPONENT_COUNT = 2;
     private static final int BYTES_PER_FLOAT = 4;
@@ -88,14 +82,9 @@
 
     private int mAttrPosition;
     private int mAttrTextureCoordinates;
-    private int mUniAod2Opacity;
-    private int mUniPer85;
-    private int mUniReveal;
     private int mUniTexture;
     private int mTextureId;
 
-    private float[] mCurrentTexCoordinate;
-
     ImageGLWallpaper(ImageGLProgram program) {
         mProgram = program;
 
@@ -135,31 +124,9 @@
     }
 
     private void setupUniforms() {
-        mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
-        mUniPer85 = mProgram.getUniformHandle(U_PER85);
-        mUniReveal = mProgram.getUniformHandle(U_REVEAL);
         mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
     }
 
-    int getHandle(String name) {
-        switch (name) {
-            case A_POSITION:
-                return mAttrPosition;
-            case A_TEXTURE_COORDINATES:
-                return mAttrTextureCoordinates;
-            case U_AOD2OPACITY:
-                return mUniAod2Opacity;
-            case U_PER85:
-                return mUniPer85;
-            case U_REVEAL:
-                return mUniReveal;
-            case U_TEXTURE:
-                return mUniTexture;
-            default:
-                return HANDLE_UNDEFINED;
-        }
-    }
-
     void draw() {
         glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
     }
@@ -201,87 +168,6 @@
     }
 
     /**
-     * This method adjust s(x-axis), t(y-axis) texture coordinates to get current display area
-     * of texture and will be used during transition.
-     * The adjustment happens if either the width or height of the surface is larger than
-     * corresponding size of the display area.
-     * If both width and height are larger than corresponding size of the display area,
-     * the adjustment will happen at both s, t side.
-     *
-     * @param surface The size of the surface.
-     * @param scissor The display area.
-     * @param xOffset The offset amount along s axis.
-     * @param yOffset The offset amount along t axis.
-     */
-    void adjustTextureCoordinates(Rect surface, Rect scissor, float xOffset, float yOffset) {
-        mCurrentTexCoordinate = TEXTURES.clone();
-
-        if (surface == null || scissor == null) {
-            mTextureBuffer.put(mCurrentTexCoordinate);
-            mTextureBuffer.position(0);
-            return;
-        }
-
-        int surfaceWidth = surface.width();
-        int surfaceHeight = surface.height();
-        int scissorWidth = scissor.width();
-        int scissorHeight = scissor.height();
-
-        if (surfaceWidth > scissorWidth) {
-            // Calculate the new s pos in pixels.
-            float pixelS = (float) Math.round((surfaceWidth - scissorWidth) * xOffset);
-            // Calculate the s pos in texture coordinate.
-            float coordinateS = pixelS / surfaceWidth;
-            // Calculate the percentage occupied by the scissor width in surface width.
-            float surfacePercentageW = (float) scissorWidth / surfaceWidth;
-            // Need also consider the case if surface height is smaller than scissor height.
-            if (surfaceHeight < scissorHeight) {
-                // We will narrow the surface percentage to keep aspect ratio.
-                surfacePercentageW *= (float) surfaceHeight / scissorHeight;
-            }
-            // Determine the final s pos, also limit the legal s pos to prevent from out of range.
-            float s = coordinateS + surfacePercentageW > 1f ? 1f - surfacePercentageW : coordinateS;
-            // Traverse the s pos in texture coordinates array and adjust the s pos accordingly.
-            for (int i = 0; i < mCurrentTexCoordinate.length; i += 2) {
-                // indices 2, 4 and 6 are the end of s coordinates.
-                if (i == 2 || i == 4 || i == 6) {
-                    mCurrentTexCoordinate[i] = Math.min(1f, s + surfacePercentageW);
-                } else {
-                    mCurrentTexCoordinate[i] = s;
-                }
-            }
-        }
-
-        if (surfaceHeight > scissorHeight) {
-            // Calculate the new t pos in pixels.
-            float pixelT = (float) Math.round((surfaceHeight - scissorHeight) * yOffset);
-            // Calculate the t pos in texture coordinate.
-            float coordinateT = pixelT / surfaceHeight;
-            // Calculate the percentage occupied by the scissor height in surface height.
-            float surfacePercentageH = (float) scissorHeight / surfaceHeight;
-            // Need also consider the case if surface width is smaller than scissor width.
-            if (surfaceWidth < scissorWidth) {
-                // We will narrow the surface percentage to keep aspect ratio.
-                surfacePercentageH *= (float) surfaceWidth / scissorWidth;
-            }
-            // Determine the final t pos, also limit the legal t pos to prevent from out of range.
-            float t = coordinateT + surfacePercentageH > 1f ? 1f - surfacePercentageH : coordinateT;
-            // Traverse the t pos in texture coordinates array and adjust the t pos accordingly.
-            for (int i = 1; i < mCurrentTexCoordinate.length; i += 2) {
-                // indices 1, 3 and 11 are the end of t coordinates.
-                if (i == 1 || i == 3 || i == 11) {
-                    mCurrentTexCoordinate[i] = Math.min(1f, t + surfacePercentageH);
-                } else {
-                    mCurrentTexCoordinate[i] = t;
-                }
-            }
-        }
-
-        mTextureBuffer.put(mCurrentTexCoordinate);
-        mTextureBuffer.position(0);
-    }
-
-    /**
      * Called to dump current state.
      * @param prefix prefix.
      * @param fd fd.
@@ -289,17 +175,5 @@
      * @param args args.
      */
     public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
-        StringBuilder sb = new StringBuilder();
-        sb.append('{');
-        if (mCurrentTexCoordinate != null) {
-            for (int i = 0; i < mCurrentTexCoordinate.length; i++) {
-                sb.append(mCurrentTexCoordinate[i]).append(',');
-                if (i == mCurrentTexCoordinate.length - 1) {
-                    sb.deleteCharAt(sb.length() - 1);
-                }
-            }
-        }
-        sb.append('}');
-        out.print(prefix); out.print("mTexCoordinates="); out.println(sb.toString());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
deleted file mode 100644
index 703d591..0000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import static com.android.systemui.glwallpaper.ImageWallpaperRenderer.WallpaperTexture;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Message;
-import android.util.Log;
-
-/**
- * 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_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:
-     * Luminance = .2126f * r + .7152f * g + .0722f * b.
-     */
-    private static final float[] LUMINOSITY_MATRIX = new float[] {
-            .2126f,     .0000f,     .0000f,     .0000f,     .0000f,
-            .0000f,     .7152f,     .0000f,     .0000f,     .0000f,
-            .0000f,     .0000f,     .0722f,     .0000f,     .0000f,
-            .0000f,     .0000f,     .0000f,     1.000f,     .0000f
-    };
-
-    private final Handler mHandler = new Handler(new Callback() {
-        @Override
-        public boolean handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UPDATE_THRESHOLD:
-                    mThreshold = (float) msg.obj;
-                    return true;
-                default:
-                    return false;
-            }
-        }
-    });
-
-    private float mThreshold = DEFAULT_THRESHOLD;
-
-    void start(WallpaperTexture texture) {
-        new ThresholdComputeTask(mHandler).execute(texture);
-    }
-
-    float getThreshold() {
-        return Math.min(mThreshold, MAX_THRESHOLD);
-    }
-
-    private static class ThresholdComputeTask extends AsyncTask<WallpaperTexture, Void, Float> {
-        private Handler mUpdateHandler;
-
-        ThresholdComputeTask(Handler handler) {
-            super(handler);
-            mUpdateHandler = handler;
-        }
-
-        @Override
-        protected Float doInBackground(WallpaperTexture... textures) {
-            WallpaperTexture texture = textures[0];
-            final float[] threshold = new float[] {DEFAULT_THRESHOLD};
-            if (texture == null) {
-                Log.e(TAG, "ThresholdComputeTask: WallpaperTexture not initialized");
-                return threshold[0];
-            }
-
-            texture.use(bitmap -> {
-                if (bitmap != null) {
-                    threshold[0] = new Threshold().compute(bitmap);
-                } else {
-                    Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
-                }
-            });
-            return threshold[0];
-        }
-
-        @Override
-        protected void onPostExecute(Float result) {
-            Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result);
-            mUpdateHandler.sendMessage(msg);
-        }
-    }
-
-    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 grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig(),
-                    false /* hasAlpha */, bitmap.getColorSpace());
-            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 = grayscale.getPixel(col, row);
-                    int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
-                    histogram[y]++;
-                }
-            }
-
-            return histogram;
-        }
-
-        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++) {
-                acc[i] = (float) histogram[i] / pixelCount;
-                float prev = i == 0 ? 0f : acc[i - 1];
-                float next = acc[i];
-                float idx = (float) (i + 1) / 255;
-                float sum = prev + next;
-                if (prev < 0.85f && sum >= 0.85f) {
-                    per85 = idx;
-                }
-                if (i > 0) {
-                    acc[i] += acc[i - 1];
-                }
-            }
-            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/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
deleted file mode 100644
index f815b5d..0000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.util.Log;
-
-import com.android.systemui.Interpolators;
-
-/**
- * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition.
- * The transition will happen while getting awake and quit events.
- */
-class ImageRevealHelper {
-    private static final String TAG = ImageRevealHelper.class.getSimpleName();
-    private static final float MAX_REVEAL = 0f;
-    private static final float MIN_REVEAL = 1f;
-    private static final boolean DEBUG = true;
-
-    private final ValueAnimator mAnimator;
-    private final RevealStateListener mRevealListener;
-    private float mReveal = MAX_REVEAL;
-    private boolean mAwake = false;
-
-    ImageRevealHelper(RevealStateListener listener) {
-        mRevealListener = listener;
-        mAnimator = ValueAnimator.ofFloat();
-        mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mAnimator.addUpdateListener(animator -> {
-            mReveal = (float) animator.getAnimatedValue();
-            if (mRevealListener != null) {
-                mRevealListener.onRevealStateChanged();
-            }
-        });
-        mAnimator.addListener(new AnimatorListenerAdapter() {
-            private boolean mIsCanceled;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mIsCanceled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!mIsCanceled && mRevealListener != null) {
-                    if (DEBUG) {
-                        Log.d(TAG, "transition end");
-                    }
-                    mRevealListener.onRevealEnd();
-                }
-                mIsCanceled = false;
-            }
-
-            @Override
-            public void onAnimationStart(Animator animation) {
-                if (mRevealListener != null) {
-                    if (DEBUG) {
-                        Log.d(TAG, "transition start");
-                    }
-                    mRevealListener.onRevealStart(true /* animate */);
-                }
-            }
-        });
-    }
-
-    public float getReveal() {
-        return mReveal;
-    }
-
-    void updateAwake(boolean awake, long duration) {
-        if (DEBUG) {
-            Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
-        }
-        mAnimator.cancel();
-        mAwake = awake;
-        if (duration == 0) {
-            // We are transiting from home to aod or aod to home directly,
-            // we don't need to do transition in these cases.
-            mReveal = mAwake ? MAX_REVEAL : MIN_REVEAL;
-            mRevealListener.onRevealStart(false /* animate */);
-            mRevealListener.onRevealStateChanged();
-            mRevealListener.onRevealEnd();
-        } else {
-            mAnimator.setDuration(duration);
-            mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
-            mAnimator.start();
-        }
-    }
-
-    /**
-     * A listener to trace value changes of reveal.
-     */
-    public interface RevealStateListener {
-
-        /**
-         * Called back while reveal status changes.
-         */
-        void onRevealStateChanged();
-
-        /**
-         * Called back while reveal starts.
-         */
-        void onRevealStart(boolean animate);
-
-        /**
-         * Called back while reveal ends.
-         */
-        void onRevealEnd();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index e9ddb38..1a0356c 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -19,18 +19,14 @@
 import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
 import static android.opengl.GLES20.glClear;
 import static android.opengl.GLES20.glClearColor;
-import static android.opengl.GLES20.glUniform1f;
 import static android.opengl.GLES20.glViewport;
 
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.util.Log;
-import android.util.MathUtils;
 import android.util.Size;
-import android.view.DisplayInfo;
 
 import com.android.systemui.R;
 
@@ -42,57 +38,24 @@
 /**
  * A GL renderer for image wallpaper.
  */
-public class ImageWallpaperRenderer implements GLWallpaperRenderer,
-        ImageRevealHelper.RevealStateListener {
+public class ImageWallpaperRenderer implements GLWallpaperRenderer {
     private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
-    private static final float SCALE_VIEWPORT_MIN = 1f;
-    private static final float SCALE_VIEWPORT_MAX = 1.1f;
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private final ImageGLProgram mProgram;
     private final ImageGLWallpaper mWallpaper;
-    private final ImageProcessHelper mImageProcessHelper;
-    private final ImageRevealHelper mImageRevealHelper;
-
-    private SurfaceProxy mProxy;
-    private final Rect mScissor;
     private final Rect mSurfaceSize = new Rect();
-    private final Rect mViewport = new Rect();
-    private boolean mScissorMode;
-    private float mXOffset;
-    private float mYOffset;
     private final WallpaperTexture mTexture;
 
-    public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) {
+    public ImageWallpaperRenderer(Context context) {
         final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
         if (wpm == null) {
             Log.w(TAG, "WallpaperManager not available");
         }
 
         mTexture = new WallpaperTexture(wpm);
-        DisplayInfo displayInfo = new DisplayInfo();
-        context.getDisplay().getDisplayInfo(displayInfo);
-
-        // We only do transition in portrait currently, b/137962047.
-        int orientation = context.getResources().getConfiguration().orientation;
-        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
-            mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
-        } else {
-            mScissor = new Rect(0, 0, displayInfo.logicalHeight, displayInfo.logicalWidth);
-        }
-
-        mProxy = proxy;
         mProgram = new ImageGLProgram(context);
         mWallpaper = new ImageGLWallpaper(mProgram);
-        mImageProcessHelper = new ImageProcessHelper();
-        mImageRevealHelper = new ImageRevealHelper(this);
-
-        startProcessingImage();
-    }
-
-    protected void startProcessingImage() {
-        // Compute threshold of the image, this is an async work.
-        mImageProcessHelper.start(mTexture);
     }
 
     @Override
@@ -121,103 +84,26 @@
 
     @Override
     public void onDrawFrame() {
-        float threshold = mImageProcessHelper.getThreshold();
-        float reveal = mImageRevealHelper.getReveal();
-
-        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
-        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold);
-        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
-
         glClear(GL_COLOR_BUFFER_BIT);
-        // We only need to scale viewport while doing transition.
-        if (mScissorMode) {
-            scaleViewport(reveal);
-        } else {
-            glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
-        }
+        glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
         mWallpaper.useTexture();
         mWallpaper.draw();
     }
 
     @Override
-    public void updateAmbientMode(boolean inAmbientMode, long duration) {
-        mImageRevealHelper.updateAwake(!inAmbientMode, duration);
-    }
-
-    @Override
-    public void updateOffsets(float xOffset, float yOffset) {
-        mXOffset = xOffset;
-        mYOffset = yOffset;
-        int left = (int) ((mSurfaceSize.width() - mScissor.width()) * xOffset);
-        int right = left + mScissor.width();
-        mScissor.set(left, mScissor.top, right, mScissor.bottom);
-    }
-
-    @Override
     public Size reportSurfaceSize() {
-        mTexture.use(null);
+        mTexture.use(null /* consumer */);
         mSurfaceSize.set(mTexture.getTextureDimensions());
         return new Size(mSurfaceSize.width(), mSurfaceSize.height());
     }
 
     @Override
     public void finish() {
-        mProxy = null;
-    }
-
-    private void scaleViewport(float reveal) {
-        int left = mScissor.left;
-        int top = mScissor.top;
-        int width = mScissor.width();
-        int height = mScissor.height();
-        // Interpolation between SCALE_VIEWPORT_MAX and SCALE_VIEWPORT_MIN by reveal.
-        float vpScaled = MathUtils.lerp(SCALE_VIEWPORT_MIN, SCALE_VIEWPORT_MAX, reveal);
-        // Calculate the offset amount from the lower left corner.
-        float offset = (SCALE_VIEWPORT_MIN - vpScaled) / 2;
-        // Change the viewport.
-        mViewport.set((int) (left + width * offset), (int) (top + height * offset),
-                (int) (width * vpScaled), (int) (height * vpScaled));
-        glViewport(mViewport.left, mViewport.top, mViewport.right, mViewport.bottom);
-    }
-
-    @Override
-    public void onRevealStateChanged() {
-        mProxy.requestRender();
-    }
-
-    @Override
-    public void onRevealStart(boolean animate) {
-        if (animate) {
-            mScissorMode = true;
-            // Use current display area of texture.
-            mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
-        }
-        mProxy.preRender();
-    }
-
-    @Override
-    public void onRevealEnd() {
-        if (mScissorMode) {
-            mScissorMode = false;
-            // reset texture coordinates to use full texture.
-            mWallpaper.adjustTextureCoordinates(null, null, 0, 0);
-            // We need draw full texture back before finishing render.
-            mProxy.requestRender();
-        }
-        mProxy.postRender();
     }
 
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
-        out.print(prefix); out.print("mProxy="); out.print(mProxy);
         out.print(prefix); out.print("mSurfaceSize="); out.print(mSurfaceSize);
-        out.print(prefix); out.print("mScissor="); out.print(mScissor);
-        out.print(prefix); out.print("mViewport="); out.print(mViewport);
-        out.print(prefix); out.print("mScissorMode="); out.print(mScissorMode);
-        out.print(prefix); out.print("mXOffset="); out.print(mXOffset);
-        out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
-        out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
-        out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
         out.print(prefix); out.print("mWcgContent="); out.print(isWcgContent());
         mWallpaper.dump(prefix, fd, out, args);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 62efd8c..8492fef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -82,6 +82,7 @@
     protected ComponentName mRecvComponent;
     private MediaDevice mDevice;
     private boolean mIsRegistered = false;
+    private String mKey;
 
     private final int[] mActionIds;
 
@@ -203,14 +204,15 @@
      * @param bgColor
      * @param contentIntent
      * @param appNameString
-     * @param device
+     * @param key
      */
     public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
-            int bgColor, PendingIntent contentIntent, String appNameString) {
+            int bgColor, PendingIntent contentIntent, String appNameString, String key) {
         mToken = token;
         mForegroundColor = iconColor;
         mBackgroundColor = bgColor;
         mController = new MediaController(mContext, mToken);
+        mKey = key;
 
         MediaMetadata mediaMetadata = mController.getMetadata();
 
@@ -326,6 +328,14 @@
     }
 
     /**
+     * Return the original notification's key
+     * @return The notification key
+     */
+    public String getKey()  {
+        return mKey;
+    }
+
+    /**
      * Check whether this player has an attached media session.
      * @return whether there is a controller with a current media session.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index b7658a9..51c157a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -61,6 +61,7 @@
         if (!data.enabled) {
             seekBarView.setEnabled(false)
             seekBarView.getThumb().setAlpha(0)
+            seekBarView.setProgress(0)
             elapsedTimeView.setText("")
             totalTimeView.setText("")
             return
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index dd83e42..1425100 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -77,13 +77,25 @@
         val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
         val position = playbackState?.position?.toInt()
         val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt()
-        val enabled = if (duration != null && duration <= 0) false else true
+        val enabled = if (playbackState == null ||
+                playbackState?.getState() == PlaybackState.STATE_NONE ||
+                (duration != null && duration <= 0)) false else true
         _data = Progress(enabled, seekAvailable, position, duration, color)
         if (shouldPollPlaybackPosition()) {
             checkPlaybackPosition()
         }
     }
 
+    /**
+     * Puts the seek bar into a resumption state.
+     *
+     * This should be called when the media session behind the controller has been destroyed.
+     */
+    @AnyThread
+    fun clearController() = bgExecutor.execute {
+        _data = _data.copy(enabled = false)
+    }
+
     @AnyThread
     private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({
         val currentPosition = controller?.playbackState?.position?.toInt()
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index c3779ef..ba9a30f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -339,18 +339,18 @@
 
     @Override
     public void onPipTransitionFinished(ComponentName activity, int direction) {
-        onPipTransitionFinishedOrCanceled();
+        onPipTransitionFinishedOrCanceled(direction);
     }
 
     @Override
     public void onPipTransitionCanceled(ComponentName activity, int direction) {
-        onPipTransitionFinishedOrCanceled();
+        onPipTransitionFinishedOrCanceled(direction);
     }
 
-    private void onPipTransitionFinishedOrCanceled() {
+    private void onPipTransitionFinishedOrCanceled(int direction) {
         // Re-enable touches after the animation completes
         mTouchHandler.setTouchEnabled(true);
-        mTouchHandler.onPinnedStackAnimationEnded();
+        mTouchHandler.onPinnedStackAnimationEnded(direction);
         mMenuController.onPinnedStackAnimationEnded();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 2b9b171..ec15dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -54,6 +54,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
@@ -129,9 +130,7 @@
                 }
             };
 
-    private Handler mHandler = new Handler();
-    private Messenger mToControllerMessenger;
-    private Messenger mMessenger = new Messenger(new Handler() {
+    private Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -174,7 +173,9 @@
                 }
             }
         }
-    });
+    };
+    private Messenger mToControllerMessenger;
+    private Messenger mMessenger = new Messenger(mHandler);
 
     private final Runnable mFinishRunnable = new Runnable() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d660b67..61ed40d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -30,6 +30,7 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
@@ -122,7 +123,7 @@
     private boolean mStartActivityRequested;
     private long mStartActivityRequestedTime;
     private Messenger mToActivityMessenger;
-    private Handler mHandler = new Handler() {
+    private Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -133,15 +134,15 @@
                     break;
                 }
                 case MESSAGE_EXPAND_PIP: {
-                    mListeners.forEach(l -> l.onPipExpand());
+                    mListeners.forEach(Listener::onPipExpand);
                     break;
                 }
                 case MESSAGE_DISMISS_PIP: {
-                    mListeners.forEach(l -> l.onPipDismiss());
+                    mListeners.forEach(Listener::onPipDismiss);
                     break;
                 }
                 case MESSAGE_SHOW_MENU: {
-                    mListeners.forEach(l -> l.onPipShowMenu());
+                    mListeners.forEach(Listener::onPipShowMenu);
                     break;
                 }
                 case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
@@ -259,6 +260,8 @@
         if (DEBUG) {
             Log.d(TAG, "showMenu() state=" + menuState
                     + " hasActivity=" + (mToActivityMessenger != null)
+                    + " allowMenuTimeout=" + allowMenuTimeout
+                    + " willResizeMenu=" + willResizeMenu
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index a8a5d89..00f693d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -168,6 +168,7 @@
     void synchronizePinnedStackBounds() {
         cancelAnimations();
         mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
+        mFloatingContentCoordinator.onContentMoved(this);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 0b07655..d80f18a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -56,7 +56,6 @@
 
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
     private final PipBoundsHandler mPipBoundsHandler;
-    private final PipTouchHandler mPipTouchHandler;
     private final PipMotionHelper mMotionHelper;
     private final int mDisplayId;
     private final Executor mMainExecutor;
@@ -70,10 +69,10 @@
     private final Rect mTmpBounds = new Rect();
     private final int mDelta;
 
-    private boolean mAllowGesture = false;
+    private boolean mAllowGesture;
     private boolean mIsAttached;
     private boolean mIsEnabled;
-    private boolean mEnablePipResize;
+    private boolean mEnableUserResize;
 
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
@@ -82,21 +81,20 @@
     private int mCtrlType;
 
     public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
-            PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper,
-            DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer) {
+            PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
+            PipTaskOrganizer pipTaskOrganizer) {
         final Resources res = context.getResources();
         context.getDisplay().getMetrics(mDisplayMetrics);
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
         mPipBoundsHandler = pipBoundsHandler;
-        mPipTouchHandler = pipTouchHandler;
         mMotionHelper = motionHelper;
         mPipTaskOrganizer = pipTaskOrganizer;
 
         context.getDisplay().getRealSize(mMaxSize);
         mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
 
-        mEnablePipResize = DeviceConfig.getBoolean(
+        mEnableUserResize = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 PIP_USER_RESIZE,
                 /* defaultValue = */ true);
@@ -105,7 +103,7 @@
                     @Override
                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
                         if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
-                            mEnablePipResize = properties.getBoolean(
+                            mEnableUserResize = properties.getBoolean(
                                     PIP_USER_RESIZE, /* defaultValue = */ true);
                         }
                     }
@@ -134,7 +132,7 @@
     }
 
     private void updateIsEnabled() {
-        boolean isEnabled = mIsAttached && mEnablePipResize;
+        boolean isEnabled = mIsAttached && mEnableUserResize;
         if (isEnabled == mIsEnabled) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 350ce29..f5c83c1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.pip.phone;
 
+import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
 import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
 import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
@@ -56,6 +57,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.systemui.R;
+import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
 import com.android.systemui.pip.PipTaskOrganizer;
@@ -229,7 +231,7 @@
         mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
                 mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
         mPipResizeGestureHandler =
-                new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper,
+                new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         deviceConfig, pipTaskOrganizer);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
@@ -266,6 +268,10 @@
 
         mMagnetizedPip = mMotionHelper.getMagnetizedPip();
         mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+
+        // Set the magnetic field radius equal to twice the size of the target.
+        mMagneticTarget.setMagneticFieldRadiusPx(targetSize * 2);
+
         mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener);
         mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
             @Override
@@ -339,11 +345,16 @@
         mPipResizeGestureHandler.onActivityUnpinned();
     }
 
-    public void onPinnedStackAnimationEnded() {
+    public void onPinnedStackAnimationEnded(
+            @PipAnimationController.TransitionDirection int direction) {
         // Always synchronize the motion helper bounds once PiP animations finish
         mMotionHelper.synchronizePinnedStackBounds();
         updateMovementBounds();
-        mResizedBounds.set(mMotionHelper.getBounds());
+        if (direction == TRANSITION_DIRECTION_TO_PIP) {
+            // updates mResizedBounds only if it's an entering PiP animation
+            // mResized should be otherwise updated in setMenuState.
+            mResizedBounds.set(mMotionHelper.getBounds());
+        }
 
         if (mShowPipMenuOnAnimationEnd) {
             mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -504,9 +515,6 @@
             mTargetView.setTranslationY(mTargetViewContainer.getHeight());
             mTargetViewContainer.setVisibility(View.VISIBLE);
 
-            // Set the magnetic field radius to half of PIP's width.
-            mMagneticTarget.setMagneticFieldRadiusPx(mMotionHelper.getBounds().width());
-
             // Cancel in case we were in the middle of animating it out.
             mMagneticTargetAnimator.cancel();
             mMagneticTargetAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index e636707..e4bcb09 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -99,15 +99,14 @@
      * @param bgColor background color
      * @param actionsContainer a LinearLayout containing the media action buttons
      * @param notif reference to original notification
-     * @param device current playback device
+     * @param key original notification's key
      */
     public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
-            int bgColor, View actionsContainer, Notification notif) {
+            int bgColor, View actionsContainer, Notification notif, String key) {
 
         String appName = Notification.Builder.recoverBuilder(getContext(), notif)
                 .loadHeaderAppName();
-        super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent,
-                appName);
+        super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent, appName, key);
 
         // Media controls
         LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
@@ -171,6 +170,8 @@
     public void clearControls() {
         super.clearControls();
 
+        mSeekBarViewModel.clearController();
+
         View guts = mMediaNotifView.findViewById(R.id.media_guts);
         View options = mMediaNotifView.findViewById(R.id.qs_media_controls_options);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0566b2e..40c8aad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -208,9 +208,10 @@
      * @param bgColor
      * @param actionsContainer
      * @param notif
+     * @param key
      */
     public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
-            View actionsContainer, StatusBarNotification notif) {
+            View actionsContainer, StatusBarNotification notif, String key) {
         if (!useQsMediaPlayer(mContext)) {
             // Shouldn't happen, but just in case
             Log.e(TAG, "Tried to add media session without player!");
@@ -225,13 +226,12 @@
         String packageName = notif.getPackageName();
         for (QSMediaPlayer p : mMediaPlayers) {
             if (p.getMediaSessionToken().equals(token)) {
-                Log.d(TAG, "a player for this session already exists");
+                Log.d(TAG, "Found matching player by token " + packageName);
                 player = p;
                 break;
-            }
-
-            if (packageName.equals(p.getMediaPlayerPackage())) {
-                Log.d(TAG, "found an old session for this app");
+            } else if (packageName.equals(p.getMediaPlayerPackage()) && key.equals(p.getKey())) {
+                // Also match if it's the same package and notification key
+                Log.d(TAG, "Found matching player by package " + packageName + ", " + key);
                 player = p;
                 break;
             }
@@ -267,7 +267,7 @@
 
         Log.d(TAG, "setting player session");
         player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
-                notif.getNotification());
+                notif.getNotification(), key);
 
         if (mMediaPlayers.size() > 0) {
             ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index 0ba4cb1..7946779 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -67,9 +67,10 @@
      * @param actionsToShow indices of which actions to display in the mini player
      *                      (max 3: Notification.MediaStyle.MAX_MEDIA_BUTTONS_IN_COMPACT)
      * @param contentIntent Intent to send when user taps on the view
+     * @param key original notification's key
      */
     public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
-            View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) {
+            View actionsContainer, int[] actionsToShow, PendingIntent contentIntent, String key) {
         // Only update if this is a different session and currently playing
         String oldPackage = "";
         if (getController() != null) {
@@ -84,7 +85,7 @@
             return;
         }
 
-        super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null);
+        super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null, key);
 
         LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
         int i = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
new file mode 100644
index 0000000..fa1b026
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.Assert
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Tracks a reference to the context for the current user
+ */
+@Singleton
+class CurrentUserContextTracker @Inject constructor(
+    private val sysuiContext: Context,
+    broadcastDispatcher: BroadcastDispatcher
+) {
+    private val userTracker: CurrentUserTracker
+    var currentUserContext: Context
+
+    init {
+        userTracker = object : CurrentUserTracker(broadcastDispatcher) {
+            override fun onUserSwitched(newUserId: Int) {
+                handleUserSwitched(newUserId)
+            }
+        }
+
+        currentUserContext = makeUserContext(userTracker.currentUserId)
+    }
+
+    fun initialize() {
+        userTracker.startTracking()
+    }
+
+    private fun handleUserSwitched(newUserId: Int) {
+        currentUserContext = makeUserContext(newUserId)
+    }
+
+    private fun makeUserContext(uid: Int): Context {
+        Assert.isMainThread()
+        return sysuiContext.createContextAsUser(
+                UserHandle.getUserHandleForUid(userTracker.currentUserId), 0)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 0d77159..25f1a97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -250,7 +250,8 @@
     private fun updateShadeBlur() {
         var newBlur = 0
         val state = statusBarStateController.state
-        if (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) {
+        if ((state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) &&
+                !keyguardStateController.isKeyguardFadingAway) {
             newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
         }
         shadeSpring.animateTo(newBlur)
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 d37e16b..75493f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -699,7 +699,8 @@
                             entry,
                             oldImportances.get(entry.getKey()),
                             oldAdjustments.get(entry.getKey()),
-                            NotificationUiAdjustment.extractFromNotificationEntry(entry));
+                            NotificationUiAdjustment.extractFromNotificationEntry(entry),
+                            mInflationCallback);
         }
 
         updateNotifications("updateNotificationRanking");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index f4c4924..710b137 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -51,5 +51,6 @@
             NotificationEntry entry,
             @Nullable Integer oldImportance,
             NotificationUiAdjustment oldAdjustment,
-            NotificationUiAdjustment newAdjustment);
+            NotificationUiAdjustment newAdjustment,
+            NotificationRowContentBinder.InflationCallback callback);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 73f12f8..e6a4cff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -180,13 +180,14 @@
             NotificationEntry entry,
             @Nullable Integer oldImportance,
             NotificationUiAdjustment oldAdjustment,
-            NotificationUiAdjustment newAdjustment) {
+            NotificationUiAdjustment newAdjustment,
+            NotificationRowContentBinder.InflationCallback callback) {
         if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
             if (entry.rowExists()) {
                 ExpandableNotificationRow row = entry.getRow();
                 row.reset();
                 updateRow(entry, row);
-                inflateContentViews(entry, row, null /* callback */);
+                inflateContentViews(entry, row, callback);
             } else {
                 // Once the RowInflaterTask is done, it will pick up the updated entry, so
                 // no-op here.
@@ -221,7 +222,7 @@
     private void inflateContentViews(
             NotificationEntry entry,
             ExpandableNotificationRow row,
-            NotificationRowContentBinder.InflationCallback inflationCallback) {
+            @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
         final boolean useIncreasedCollapsedHeight =
                 mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance());
         final boolean isLowPriority = entry.isAmbient();
@@ -238,7 +239,9 @@
         mRowContentBindStage.requestRebind(entry, en -> {
             row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
             row.setIsLowPriority(isLowPriority);
-            inflationCallback.onAsyncInflationFinished(en);
+            if (inflationCallback != null) {
+                inflationCallback.onAsyncInflationFinished(en);
+            }
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 565a082..355990b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.CurrentUserContextTracker;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -53,6 +54,7 @@
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -60,6 +62,7 @@
 
 import java.util.concurrent.Executor;
 
+import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import dagger.Binds;
@@ -109,7 +112,9 @@
             HighPriorityProvider highPriorityProvider,
             INotificationManager notificationManager,
             LauncherApps launcherApps,
-            ShortcutManager shortcutManager) {
+            ShortcutManager shortcutManager,
+            CurrentUserContextTracker contextTracker,
+            Provider<PriorityOnboardingDialogController.Builder> builderProvider) {
         return new NotificationGutsManager(
                 context,
                 visualStabilityManager,
@@ -119,7 +124,9 @@
                 highPriorityProvider,
                 notificationManager,
                 launcherApps,
-                shortcutManager);
+                shortcutManager,
+                contextTracker,
+                builderProvider);
     }
 
     /** Provides an instance of {@link VisualStabilityManager} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index a271993..55a5935 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -17,11 +17,13 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
 
@@ -33,6 +35,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -43,6 +46,7 @@
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.transition.ChangeBounds;
@@ -51,6 +55,7 @@
 import android.transition.TransitionSet;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
@@ -60,6 +65,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationChannelHelper;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -68,6 +74,8 @@
 import java.lang.annotation.Retention;
 import java.util.List;
 
+import javax.inject.Provider;
+
 /**
  * The guts of a conversation notification revealed when performing a long press.
  */
@@ -90,7 +98,11 @@
     private ShortcutInfo mShortcutInfo;
     private String mConversationId;
     private StatusBarNotification mSbn;
+    private Notification.BubbleMetadata mBubbleMetadata;
+    private Context mUserContext;
+    private Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
     private boolean mIsDeviceProvisioned;
+    private int mAppBubble;
 
     private TextView mPriorityDescriptionView;
     private TextView mDefaultDescriptionView;
@@ -132,17 +144,17 @@
     */
 
     private OnClickListener mOnFavoriteClick = v -> {
-        mSelectedAction = ACTION_FAVORITE;
+        setSelectedAction(ACTION_FAVORITE);
         updateToggleActions(mSelectedAction, true);
     };
 
     private OnClickListener mOnDefaultClick = v -> {
-        mSelectedAction = ACTION_DEFAULT;
+        setSelectedAction(ACTION_DEFAULT);
         updateToggleActions(mSelectedAction, true);
     };
 
     private OnClickListener mOnMuteClick = v -> {
-        mSelectedAction = ACTION_MUTE;
+        setSelectedAction(ACTION_MUTE);
         updateToggleActions(mSelectedAction, true);
     };
 
@@ -166,6 +178,23 @@
         void onClick(View v, int hoursToSnooze);
     }
 
+    @VisibleForTesting
+    void setSelectedAction(int selectedAction) {
+        if (mSelectedAction == selectedAction) {
+            return;
+        }
+
+        mSelectedAction = selectedAction;
+        onSelectedActionChanged();
+    }
+
+    private void onSelectedActionChanged() {
+        // If the user selected Priority, maybe show the priority onboarding
+        if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) {
+            showPriorityOnboarding();
+        }
+    }
+
     public void bindNotification(
             ShortcutManager shortcutManager,
             PackageManager pm,
@@ -177,6 +206,8 @@
             OnSettingsClickListener onSettingsClick,
             OnSnoozeClickListener onSnoozeClickListener,
             ConversationIconFactory conversationIconFactory,
+            Context userContext,
+            Provider<PriorityOnboardingDialogController.Builder> builderProvider,
             boolean isDeviceProvisioned) {
         mSelectedAction = -1;
         mINotificationManager = iNotificationManager;
@@ -192,6 +223,9 @@
         mIsDeviceProvisioned = isDeviceProvisioned;
         mOnSnoozeClickListener = onSnoozeClickListener;
         mIconFactory = conversationIconFactory;
+        mUserContext = userContext;
+        mBubbleMetadata = entry.getBubbleMetadata();
+        mBuilderProvider = builderProvider;
 
         mShortcutManager = shortcutManager;
         mConversationId = mNotificationChannel.getConversationId();
@@ -206,6 +240,13 @@
         mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded(
                 getContext(), mINotificationManager, entry, mNotificationChannel);
 
+        try {
+            mAppBubble = mINotificationManager.getBubblePreferenceForPackage(mPackageName, mAppUid);
+        } catch (RemoteException e) {
+            Log.e(TAG, "can't reach OS", e);
+            mAppBubble = BUBBLE_PREFERENCE_SELECTED;
+        }
+
         bindHeader();
         bindActions();
 
@@ -227,6 +268,11 @@
         snooze.setOnClickListener(mOnSnoozeClick);
         */
 
+        if (mAppBubble == BUBBLE_PREFERENCE_ALL) {
+            ((TextView) findViewById(R.id.default_summary)).setText(getResources().getString(
+                    R.string.notification_channel_summary_default_with_bubbles, mAppName));
+        }
+
         findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick);
         findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick);
         findViewById(R.id.silence).setOnClickListener(mOnMuteClick);
@@ -264,7 +310,6 @@
         // bindName();
         bindPackage();
         bindIcon(mNotificationChannel.isImportantConversation());
-
     }
 
     private void bindIcon(boolean important) {
@@ -476,6 +521,38 @@
                         mAppUid, mSelectedAction, mNotificationChannel));
     }
 
+    private boolean shouldShowPriorityOnboarding() {
+        return !Prefs.getBoolean(mUserContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, false);
+    }
+
+    private void showPriorityOnboarding() {
+        View onboardingView = LayoutInflater.from(mContext)
+                .inflate(R.layout.priority_onboarding_half_shell, null);
+
+        boolean ignoreDnd = false;
+        try {
+            ignoreDnd = (mINotificationManager
+                    .getConsolidatedNotificationPolicy().priorityConversationSenders
+                    & NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT) != 0;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not check conversation senders", e);
+        }
+
+        boolean showAsBubble = mBubbleMetadata.getAutoExpandBubble()
+                && Settings.Global.getInt(mContext.getContentResolver(),
+                        NOTIFICATION_BUBBLES, 0) == 1;
+
+        PriorityOnboardingDialogController controller = mBuilderProvider.get()
+                .setContext(mUserContext)
+                .setView(onboardingView)
+                .setIgnoresDnd(ignoreDnd)
+                .setShowsAsBubble(showAsBubble)
+                .build();
+
+        controller.init();
+        controller.show();
+    }
+
     /**
      * Closes the controls and commits the updated importance values (indirectly).
      *
@@ -560,10 +637,7 @@
                                 !mChannelToUpdate.isImportantConversation());
                         if (mChannelToUpdate.isImportantConversation()) {
                             mChannelToUpdate.setAllowBubbles(true);
-                            int currentPref =
-                                    mINotificationManager.getBubblePreferenceForPackage(
-                                            mAppPkg, mAppUid);
-                            if (currentPref == BUBBLE_PREFERENCE_NONE) {
+                            if (mAppBubble == BUBBLE_PREFERENCE_NONE) {
                                 mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
                                         BUBBLE_PREFERENCE_SELECTED);
                             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 2487d1a..624fabc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -49,6 +49,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.CurrentUserContextTracker;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -67,6 +68,8 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Provider;
+
 import dagger.Lazy;
 
 /**
@@ -111,6 +114,8 @@
     private final INotificationManager mNotificationManager;
     private final LauncherApps mLauncherApps;
     private final ShortcutManager mShortcutManager;
+    private final CurrentUserContextTracker mContextTracker;
+    private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
 
     /**
      * Injected constructor. See {@link NotificationsModule}.
@@ -121,7 +126,9 @@
             HighPriorityProvider highPriorityProvider,
             INotificationManager notificationManager,
             LauncherApps launcherApps,
-            ShortcutManager shortcutManager) {
+            ShortcutManager shortcutManager,
+            CurrentUserContextTracker contextTracker,
+            Provider<PriorityOnboardingDialogController.Builder> builderProvider) {
         mContext = context;
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarLazy = statusBarLazy;
@@ -131,6 +138,8 @@
         mNotificationManager = notificationManager;
         mLauncherApps = launcherApps;
         mShortcutManager = shortcutManager;
+        mContextTracker = contextTracker;
+        mBuilderProvider = builderProvider;
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -403,6 +412,8 @@
                 onSettingsClick,
                 onSnoozeClickListener,
                 iconFactoryLoader,
+                mContextTracker.getCurrentUserContext(),
+                mBuilderProvider,
                 mDeviceProvisionedController.isDeviceProvisioned());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
new file mode 100644
index 0000000..d1b4052
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.app.Dialog
+import android.content.Context
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.drawable.ColorDrawable
+import android.view.Gravity
+import android.view.View
+import android.view.View.GONE
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.Window
+import android.view.WindowInsets.Type.statusBars
+import android.view.WindowManager
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.Prefs
+import com.android.systemui.R
+import java.lang.IllegalStateException
+import javax.inject.Inject
+
+/**
+ * Controller to handle presenting the priority conversations onboarding dialog
+ */
+class PriorityOnboardingDialogController @Inject constructor(
+    val view: View,
+    val context: Context,
+    val ignoresDnd: Boolean,
+    val showsAsBubble: Boolean
+) {
+
+    private lateinit var dialog: Dialog
+
+    fun init() {
+        initDialog()
+    }
+
+    fun show() {
+        dialog.show()
+    }
+
+    private fun done() {
+        // Log that the user has seen the onboarding
+        Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true)
+        dialog.dismiss()
+    }
+
+    class Builder @Inject constructor() {
+        private lateinit var view: View
+        private lateinit var context: Context
+        private var ignoresDnd = false
+        private var showAsBubble = false
+
+        fun setView(v: View): Builder {
+            view = v
+            return this
+        }
+
+        fun setContext(c: Context): Builder {
+            context = c
+            return this
+        }
+
+        fun setIgnoresDnd(ignore: Boolean): Builder {
+            ignoresDnd = ignore
+            return this
+        }
+
+        fun setShowsAsBubble(bubble: Boolean): Builder {
+            showAsBubble = bubble
+            return this
+        }
+
+        fun build(): PriorityOnboardingDialogController {
+            val controller = PriorityOnboardingDialogController(
+                    view, context, ignoresDnd, showAsBubble)
+            return controller
+        }
+    }
+
+    private fun initDialog() {
+        dialog = Dialog(context)
+
+        if (dialog.window == null) {
+            throw IllegalStateException("Need a window for the onboarding dialog to show")
+        }
+
+        dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
+        // Prevent a11y readers from reading the first element in the dialog twice
+        dialog.setTitle("\u00A0")
+        dialog.apply {
+            setContentView(view)
+            setCanceledOnTouchOutside(true)
+
+            findViewById<TextView>(R.id.done_button)?.setOnClickListener {
+                done()
+            }
+
+            if (!ignoresDnd) {
+                findViewById<LinearLayout>(R.id.ignore_dnd_tip).visibility = GONE
+            }
+
+            if (!showsAsBubble) {
+                findViewById<LinearLayout>(R.id.floating_bubble_tip).visibility = GONE
+            }
+
+            window?.apply {
+                setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+                addFlags(wmFlags)
+                setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+                setWindowAnimations(com.android.internal.R.style.Animation_InputMethod)
+
+                attributes = attributes.apply {
+                    format = PixelFormat.TRANSLUCENT
+                    title = ChannelEditorDialogController::class.java.simpleName
+                    gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+                    fitInsetsTypes = attributes.fitInsetsTypes and statusBars().inv()
+                    width = MATCH_PARENT
+                    height = WRAP_CONTENT
+                }
+            }
+        }
+    }
+
+    private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+            or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+            or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+            or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 7ac0662..e20be2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
 
-import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.app.Notification;
 import android.content.Context;
@@ -133,15 +132,6 @@
         if (mAppOps != null) {
             mAppOps.setOnClickListener(listener);
         }
-        if (mCameraIcon != null) {
-            mCameraIcon.setOnClickListener(listener);
-        }
-        if (mMicIcon != null) {
-            mMicIcon.setOnClickListener(listener);
-        }
-        if (mOverlayIcon != null) {
-            mOverlayIcon.setOnClickListener(listener);
-        }
     }
 
     /**
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 874d81d..2da2724 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
@@ -193,7 +193,8 @@
                     mBackgroundColor,
                     mActions,
                     compactActions,
-                    notif.contentIntent);
+                    notif.contentIntent,
+                    sbn.getKey());
             QSPanel bigPanel = ctrl.getNotificationShadeView().findViewById(
                     com.android.systemui.R.id.quick_settings_panel);
             bigPanel.addMediaSession(token,
@@ -201,7 +202,8 @@
                     tintColor,
                     mBackgroundColor,
                     mActions,
-                    sbn);
+                    sbn,
+                    sbn.getKey());
         }
 
         boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 6e5f8a0..051bd29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -367,7 +367,7 @@
             mobileSignalController.unregisterListener();
         }
         mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
-        mContext.unregisterReceiver(this);
+        mBroadcastDispatcher.unregisterReceiver(this);
     }
 
     public int getConnectedWifiLevel() {
@@ -859,6 +859,7 @@
         pw.println("  - telephony ------");
         pw.print("  hasVoiceCallingFeature()=");
         pw.println(hasVoiceCallingFeature());
+        pw.println("  mListening=" + mListening);
 
         pw.println("  - connectivity ------");
         pw.print("  mConnectedTransports=");
diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
index ca4b67d..242f7cd 100644
--- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
@@ -187,16 +187,23 @@
                 // Tell that content to get out of the way, and save the bounds it says it's moving
                 // (or animating) to.
                 .forEach { (content, bounds) ->
-                    content.moveToBounds(
-                            content.calculateNewBoundsOnOverlap(
-                                    conflictingNewBounds,
-                                    // Pass all of the content bounds except the bounds of the
-                                    // content we're asking to move, and the conflicting new bounds
-                                    // (since those are passed separately).
-                                    otherContentBounds = allContentBounds.values
-                                            .minus(bounds)
-                                            .minus(conflictingNewBounds)))
-                    allContentBounds[content] = content.getFloatingBoundsOnScreen()
+                    val newBounds = content.calculateNewBoundsOnOverlap(
+                            conflictingNewBounds,
+                            // Pass all of the content bounds except the bounds of the
+                            // content we're asking to move, and the conflicting new bounds
+                            // (since those are passed separately).
+                            otherContentBounds = allContentBounds.values
+                                    .minus(bounds)
+                                    .minus(conflictingNewBounds))
+
+                    // If the new bounds are empty, it means there's no non-overlapping position
+                    // that is in bounds. Just leave the content where it is. This should normally
+                    // not happen, but sometimes content like PIP reports incorrect bounds
+                    // temporarily.
+                    if (!newBounds.isEmpty) {
+                        content.moveToBounds(newBounds)
+                        allContentBounds[content] = content.getFloatingBoundsOnScreen()
+                    }
                 }
 
         currentlyResolvingConflicts = false
@@ -229,8 +236,8 @@
          * @param allowedBounds The area within which we're allowed to find new bounds for the
          * content.
          * @return New bounds for the content that don't intersect the exclusion rects or the
-         * newly overlapping rect, and that is within bounds unless no possible in-bounds position
-         * exists.
+         * newly overlapping rect, and that is within bounds - or an empty Rect if no in-bounds
+         * position exists.
          */
         @JvmStatic
         fun findAreaForContentVertically(
@@ -274,7 +281,13 @@
                             !overlappingContentPushingDown && !positionAboveInBounds
 
             // Return the content rect, but offset to reflect the new position.
-            return if (usePositionBelow) newContentBoundsBelow else newContentBoundsAbove
+            val newBounds = if (usePositionBelow) newContentBoundsBelow else newContentBoundsAbove
+
+            // If the new bounds are within the allowed bounds, return them. If not, it means that
+            // there are no legal new bounds. This can happen if the new content's bounds are too
+            // large (for example, full-screen PIP). Since there is no reasonable action to take
+            // here, return an empty Rect and we will just not move the content.
+            return if (allowedBounds.contains(newBounds)) newBounds else Rect()
         }
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
index f27bdbf..e905e67 100644
--- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -27,6 +27,7 @@
 import android.view.MotionEvent
 import android.view.VelocityTracker
 import android.view.View
+import android.view.ViewConfiguration
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringForce
@@ -146,6 +147,10 @@
     private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
     private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
 
+    private var touchDown = PointF()
+    private var touchSlop = 0
+    private var movedBeyondSlop = false
+
     /** Whether touch events are presently occurring within the magnetic field area of a target. */
     val objectStuckToTarget: Boolean
         get() = targetObjectIsStuckTo != null
@@ -324,15 +329,32 @@
         // When a gesture begins, recalculate target views' positions on the screen in case they
         // have changed. Also, clear state.
         if (ev.action == MotionEvent.ACTION_DOWN) {
-            updateTargetViewLocations()
+            updateTargetViews()
 
-            // Clear the velocity tracker and assume we're not stuck to a target yet.
+            // Clear the velocity tracker and stuck target.
             velocityTracker.clear()
             targetObjectIsStuckTo = null
+
+            // Set the touch down coordinates and reset movedBeyondSlop.
+            touchDown.set(ev.rawX, ev.rawY)
+            movedBeyondSlop = false
         }
 
+        // Always pass events to the VelocityTracker.
         addMovement(ev)
 
+        // If we haven't yet moved beyond the slop distance, check if we have.
+        if (!movedBeyondSlop) {
+            val dragDistance = hypot(ev.rawX - touchDown.x, ev.rawY - touchDown.y)
+            if (dragDistance > touchSlop) {
+                // If we're beyond the slop distance, save that and continue.
+                movedBeyondSlop = true
+            } else {
+                // Otherwise, don't do anything yet.
+                return false
+            }
+        }
+
         val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target ->
             val distanceFromTargetCenter = hypot(
                     ev.rawX - target.centerOnScreen.x,
@@ -559,8 +581,14 @@
     }
 
     /** Updates the locations on screen of all of the [associatedTargets]. */
-    internal fun updateTargetViewLocations() {
+    internal fun updateTargetViews() {
         associatedTargets.forEach { it.updateLocationOnScreen() }
+
+        // Update the touch slop, since the configuration may have changed.
+        if (associatedTargets.size > 0) {
+            touchSlop =
+                    ViewConfiguration.get(associatedTargets[0].targetView.context).scaledTouchSlop
+        }
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 5227aaf..0c69ffd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui;
 
-import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.Mockito.doReturn;
@@ -32,19 +31,16 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
-import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Handler;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.util.Size;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.SurfaceHolder;
 
 import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
-import com.android.systemui.statusbar.phone.DozeParameters;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -72,8 +68,6 @@
     @Mock
     private Bitmap mWallpaperBitmap;
     @Mock
-    private DozeParameters mDozeParam;
-    @Mock
     private Handler mHandler;
 
     private CountDownLatch mEventCountdown;
@@ -100,14 +94,13 @@
         when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
         when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
         when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
-        when(mDozeParam.getDisplayNeedsBlanking()).thenReturn(false);
     }
 
     private ImageWallpaper createImageWallpaper() {
-        return new ImageWallpaper(mDozeParam) {
+        return new ImageWallpaper() {
             @Override
             public Engine onCreateEngine() {
-                return new GLEngine(mDozeParam, mHandler) {
+                return new GLEngine(mHandler) {
                     @Override
                     public Context getDisplayContext() {
                         return mMockContext;
@@ -130,75 +123,52 @@
         };
     }
 
-    private ImageWallpaperRenderer createImageWallpaperRenderer(ImageWallpaper.GLEngine engine) {
-        return new ImageWallpaperRenderer(mMockContext, engine) {
-            @Override
-            public void startProcessingImage() {
-                // No - Op
-            }
-        };
-    }
-
     @Test
     public void testBitmapWallpaper_normal() {
         // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
         // Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
-        // Finally, we assert the transition will not be stopped.
-        verifySurfaceSizeAndAssertTransition(DISPLAY_WIDTH /* bmpWidth */,
+        verifySurfaceSize(DISPLAY_WIDTH /* bmpWidth */,
                 DISPLAY_WIDTH /* bmpHeight */,
                 DISPLAY_WIDTH /* surfaceWidth */,
-                DISPLAY_WIDTH /* surfaceHeight */,
-                false /* assertion */);
+                DISPLAY_WIDTH /* surfaceHeight */);
     }
 
     @Test
     public void testBitmapWallpaper_low_resolution() {
         // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
         // Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
-        // Finally, we assert the transition will be stopped.
-        verifySurfaceSizeAndAssertTransition(LOW_BMP_WIDTH /* bmpWidth */,
+        verifySurfaceSize(LOW_BMP_WIDTH /* bmpWidth */,
                 LOW_BMP_HEIGHT /* bmpHeight */,
                 LOW_BMP_WIDTH /* surfaceWidth */,
-                LOW_BMP_HEIGHT /* surfaceHeight */,
-                false /* assertion */);
+                LOW_BMP_HEIGHT /* surfaceHeight */);
     }
 
     @Test
     public void testBitmapWallpaper_too_small() {
         // Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT.
         // Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT.
-        // Finally, we assert the transition will be stopped.
-        verifySurfaceSizeAndAssertTransition(INVALID_BMP_WIDTH /* bmpWidth */,
+        verifySurfaceSize(INVALID_BMP_WIDTH /* bmpWidth */,
                 INVALID_BMP_HEIGHT /* bmpHeight */,
                 ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
-                ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
-                false /* assertion */);
+                ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */);
     }
 
-    private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
-            int surfaceWidth, int surfaceHeight, boolean assertion) {
+    private void verifySurfaceSize(int bmpWidth, int bmpHeight,
+            int surfaceWidth, int surfaceHeight) {
         ImageWallpaper.GLEngine wallpaperEngine =
                 (ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
 
         ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
-        when(engineSpy.mIsHighEndGfx).thenReturn(true);
 
         when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth);
         when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight);
 
-        ImageWallpaperRenderer renderer = createImageWallpaperRenderer(engineSpy);
+        ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mMockContext);
         doReturn(renderer).when(engineSpy).getRendererInstance();
         engineSpy.onCreate(engineSpy.getSurfaceHolder());
 
         verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight);
         assertWithMessage("setFixedSizeAllowed should have been called.").that(
                 mEventCountdown.getCount()).isEqualTo(0);
-
-        Size frameSize = renderer.reportSurfaceSize();
-        Rect frame = new Rect(0, 0, frameSize.getWidth(), frameSize.getHeight());
-        when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
-
-        assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
-        // destroy
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
index 4b47093..e23507b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -40,6 +40,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -84,22 +85,17 @@
     }
 
     @Test
-    public void testInit_finish() {
+    public void testInit_normal() {
         mEglHelper.init(mSurfaceHolder, false /* wideColorGamut */);
         assertThat(mEglHelper.hasEglDisplay()).isTrue();
         assertThat(mEglHelper.hasEglContext()).isTrue();
         assertThat(mEglHelper.hasEglSurface()).isTrue();
         verify(mEglHelper).askCreatingEglWindowSurface(
                 any(SurfaceHolder.class), eq(null), anyInt());
-
-        mEglHelper.finish();
-        assertThat(mEglHelper.hasEglSurface()).isFalse();
-        assertThat(mEglHelper.hasEglContext()).isFalse();
-        assertThat(mEglHelper.hasEglDisplay()).isFalse();
     }
 
     @Test
-    public void testInit_finish_wide_gamut() {
+    public void testInit_wide_gamut() {
         // In EglHelper, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
         doReturn(0x3490).when(mEglHelper).getWcgCapability();
         // In EglHelper, KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace";
@@ -113,10 +109,10 @@
                 .askCreatingEglWindowSurface(any(SurfaceHolder.class), ac.capture(), anyInt());
         assertThat(ac.getValue()).isNotNull();
         assertThat(ac.getValue()).isEqualTo(expectedArgument);
-        mEglHelper.finish();
     }
 
     @Test
+    @Ignore
     public void testFinish_shouldNotCrash() {
         mEglHelper.terminateEglDisplay();
         assertThat(mEglHelper.hasEglDisplay()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
deleted file mode 100644
index c827ac7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class ImageRevealHelperTest extends SysuiTestCase {
-
-    static final int ANIMATION_DURATION = 500;
-    ImageRevealHelper mImageRevealHelper;
-    ImageRevealHelper.RevealStateListener mRevealStateListener;
-
-    @Before
-    public void setUp() throws Exception {
-        mRevealStateListener = new ImageRevealHelper.RevealStateListener() {
-            @Override
-            public void onRevealStateChanged() {
-                // no-op
-            }
-
-            @Override
-            public void onRevealStart(boolean animate) {
-                // no-op
-            }
-
-            @Override
-            public void onRevealEnd() {
-                // no-op
-            }
-        };
-        mImageRevealHelper = new ImageRevealHelper(mRevealStateListener);
-    }
-
-    @Test
-    public void testBiometricAuthUnlockAnimateImageRevealState_shouldNotBlackoutScreen() {
-        assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
-
-        mImageRevealHelper.updateAwake(true /* awake */, ANIMATION_DURATION);
-        assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
-
-        // When device unlock through Biometric, should not show reveal transition
-        mImageRevealHelper.updateAwake(false /* awake */, 0);
-        assertThat(mImageRevealHelper.getReveal()).isEqualTo(1f);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
index d61be37..24f3eb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.glwallpaper;
 
-import static com.android.systemui.glwallpaper.GLWallpaperRenderer.SurfaceProxy;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
@@ -48,30 +46,12 @@
 public class ImageWallpaperRendererTest extends SysuiTestCase {
 
     private WallpaperManager mWpmSpy;
-    private SurfaceProxy mSurfaceProxy;
 
     @Before
     public void setUp() throws Exception {
         final WallpaperManager wpm = mContext.getSystemService(WallpaperManager.class);
         mWpmSpy = spy(wpm);
         mContext.addMockSystemService(WallpaperManager.class, mWpmSpy);
-
-        mSurfaceProxy = new SurfaceProxy() {
-            @Override
-            public void requestRender() {
-                // NO-op
-            }
-
-            @Override
-            public void preRender() {
-                // No-op
-            }
-
-            @Override
-            public void postRender() {
-                // No-op
-            }
-        };
     }
 
     @Test
@@ -91,12 +71,12 @@
             doReturn(supportedWideGamuts).when(cmProxySpy).getSupportedColorSpaces();
 
             mWpmSpy.setBitmap(p3Bitmap);
-            ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+            ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext);
             rendererP3.reportSurfaceSize();
             assertThat(rendererP3.isWcgContent()).isTrue();
 
             mWpmSpy.setBitmap(srgbBitmap);
-            ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+            ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext);
             assertThat(renderer.isWcgContent()).isFalse();
         } finally {
             srgbBitmap.recycle();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index f316d04..28a3d6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -86,7 +86,29 @@
     }
 
     @Test
-    fun updateDuration() {
+    fun updateDurationWithPlayback() {
+        // GIVEN that the duration is contained within the metadata
+        val duration = 12000L
+        val metadata = MediaMetadata.Builder().run {
+            putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+            build()
+        }
+        whenever(mockController.getMetadata()).thenReturn(metadata)
+        // AND a valid playback state (ie. media session is not destroyed)
+        val state = PlaybackState.Builder().run {
+            setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+            build()
+        }
+        whenever(mockController.getPlaybackState()).thenReturn(state)
+        // WHEN the controller is updated
+        viewModel.updateController(mockController, Color.RED)
+        // THEN the duration is extracted
+        assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
+        assertThat(viewModel.progress.value!!.enabled).isTrue()
+    }
+
+    @Test
+    fun updateDurationWithoutPlayback() {
         // GIVEN that the duration is contained within the metadata
         val duration = 12000L
         val metadata = MediaMetadata.Builder().run {
@@ -98,7 +120,7 @@
         viewModel.updateController(mockController, Color.RED)
         // THEN the duration is extracted
         assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
-        assertThat(viewModel.progress.value!!.enabled).isTrue()
+        assertThat(viewModel.progress.value!!.enabled).isFalse()
     }
 
     @Test
@@ -110,6 +132,12 @@
             build()
         }
         whenever(mockController.getMetadata()).thenReturn(metadata)
+        // AND a valid playback state (ie. media session is not destroyed)
+        val state = PlaybackState.Builder().run {
+            setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+            build()
+        }
+        whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController, Color.RED)
         // THEN the seek bar is disabled
@@ -125,6 +153,12 @@
             build()
         }
         whenever(mockController.getMetadata()).thenReturn(metadata)
+        // AND a valid playback state (ie. media session is not destroyed)
+        val state = PlaybackState.Builder().run {
+            setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+            build()
+        }
+        whenever(mockController.getPlaybackState()).thenReturn(state)
         // WHEN the controller is updated
         viewModel.updateController(mockController, Color.RED)
         // THEN the seek bar is disabled
@@ -372,4 +406,30 @@
         // THEN an update task is queued
         assertThat(fakeExecutor.numPending()).isEqualTo(1)
     }
+
+    @Test
+    fun clearSeekBar() {
+        // GIVEN that the duration is contained within the metadata
+        val metadata = MediaMetadata.Builder().run {
+            putLong(MediaMetadata.METADATA_KEY_DURATION, 12000L)
+            build()
+        }
+        whenever(mockController.getMetadata()).thenReturn(metadata)
+        // AND a valid playback state (ie. media session is not destroyed)
+        val state = PlaybackState.Builder().run {
+            setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+            build()
+        }
+        whenever(mockController.getPlaybackState()).thenReturn(state)
+        // AND the controller has been updated
+        viewModel.updateController(mockController, Color.RED)
+        // WHEN the controller is cleared on the event when the session is destroyed
+        viewModel.clearController()
+        with(fakeExecutor) {
+            advanceClockToNext()
+            runAllReady()
+        }
+        // THEN the seek bar is disabled
+        assertThat(viewModel.progress.value!!.enabled).isFalse()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index b6bd5e2..61388b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -16,13 +16,12 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
-import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
-import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
@@ -36,8 +35,10 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@
 import android.app.NotificationChannelGroup;
 import android.app.PendingIntent;
 import android.app.Person;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherApps;
@@ -59,20 +61,19 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bubbles.BubbleController;
@@ -87,6 +88,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
@@ -97,6 +99,8 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
+import javax.inject.Provider;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -143,6 +147,11 @@
     private ShadeController mShadeController;
     @Mock
     private ConversationIconFactory mIconFactory;
+    @Mock
+    private Context mUserContext;
+    @Mock(answer = Answers.RETURNS_SELF)
+    private PriorityOnboardingDialogController.Builder mBuilder;
+    private Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider = () -> mBuilder;
 
     @Before
     public void setUp() throws Exception {
@@ -234,6 +243,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
         assertEquals(mIconDrawable, view.getDrawable());
@@ -253,6 +264,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
         assertTrue(textView.getText().toString().contains("App Name"));
@@ -298,6 +311,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
         assertTrue(textView.getText().toString().contains(group.getName()));
@@ -319,6 +334,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -339,6 +356,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
@@ -366,6 +385,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
@@ -389,6 +410,8 @@
                 },
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
@@ -410,6 +433,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -432,6 +457,8 @@
                 },
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 false);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -452,13 +479,17 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         View view = mNotificationInfo.findViewById(R.id.silence);
         assertThat(view.isSelected()).isTrue();
     }
 
     @Test
-    public void testBindNotification_defaultSelected_notFave_notSilent() {
+    public void testBindNotification_defaultSelected_notFave_notSilent() throws Exception {
+        when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt()))
+                .thenReturn(BUBBLE_PREFERENCE_SELECTED);
         mConversationChannel.setImportance(IMPORTANCE_HIGH);
         mConversationChannel.setImportantConversation(false);
         mConversationChannel.setAllowBubbles(true);
@@ -473,9 +504,42 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
         View view = mNotificationInfo.findViewById(R.id.default_behavior);
         assertThat(view.isSelected()).isTrue();
+        assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
+                mContext.getString(R.string.notification_channel_summary_default));
+    }
+
+    @Test
+    public void testBindNotification_default_allCanBubble() throws Exception {
+        when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt()))
+                .thenReturn(BUBBLE_PREFERENCE_ALL);
+        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+        mConversationChannel.setImportance(IMPORTANCE_HIGH);
+        mConversationChannel.setImportantConversation(false);
+        mConversationChannel.setAllowBubbles(true);
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                mIconFactory,
+                mUserContext,
+                mBuilderProvider,
+                true);
+        View view = mNotificationInfo.findViewById(R.id.default_behavior);
+        assertThat(view.isSelected()).isTrue();
+        assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
+                mContext.getString(R.string.notification_channel_summary_default_with_bubbles,
+                        "App Name"));
     }
 
     @Test
@@ -495,6 +559,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         View fave = mNotificationInfo.findViewById(R.id.priority);
@@ -533,6 +599,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -570,6 +638,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -608,6 +678,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         View fave = mNotificationInfo.findViewById(R.id.priority);
@@ -640,6 +712,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         View fave = mNotificationInfo.findViewById(R.id.priority);
@@ -670,6 +744,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -701,6 +777,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -732,6 +810,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -762,6 +842,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -791,6 +873,8 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
@@ -811,9 +895,81 @@
                 null,
                 null,
                 mIconFactory,
+                mUserContext,
+                mBuilderProvider,
                 true);
 
         verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
                 anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
     }
+
+    @Test
+    public void testSelectPriorityPresentsOnboarding_firstTime() {
+        // GIVEN pref is false
+        Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, false);
+
+        // GIVEN the priority onboarding screen is present
+        PriorityOnboardingDialogController.Builder b =
+                new PriorityOnboardingDialogController.Builder();
+        PriorityOnboardingDialogController controller =
+                mock(PriorityOnboardingDialogController.class);
+        when(b.build()).thenReturn(controller);
+
+        // GIVEN the user is changing conversation settings
+        when(mBuilderProvider.get()).thenReturn(b);
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                mIconFactory,
+                mUserContext,
+                mBuilderProvider,
+                true);
+
+        // WHEN user clicks "priority"
+        mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
+
+        // THEN the user is presented with the priority onboarding screen
+        verify(controller, atLeastOnce()).show();
+    }
+
+    @Test
+    public void testSelectPriorityDoesNotShowOnboarding_secondTime() {
+        //WHEN pref is true
+        Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true);
+
+        PriorityOnboardingDialogController.Builder b =
+                new PriorityOnboardingDialogController.Builder();
+        PriorityOnboardingDialogController controller =
+                mock(PriorityOnboardingDialogController.class);
+        when(b.build()).thenReturn(controller);
+
+        when(mBuilderProvider.get()).thenReturn(b);
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                mIconFactory,
+                mUserContext,
+                mBuilderProvider,
+                true);
+
+        // WHEN user clicks "priority"
+        mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
+
+        // THEN the user is presented with the priority onboarding screen
+        verify(controller, never()).show();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index ed46423..5813740 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -66,6 +66,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.settings.CurrentUserContextTracker;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -83,11 +84,14 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import javax.inject.Provider;
+
 /**
  * Tests for {@link NotificationGutsManager}.
  */
@@ -120,6 +124,10 @@
     @Mock private LauncherApps mLauncherApps;
     @Mock private ShortcutManager mShortcutManager;
     @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+    @Mock private CurrentUserContextTracker mContextTracker;
+    @Mock(answer = Answers.RETURNS_SELF)
+    private PriorityOnboardingDialogController.Builder mBuilder;
+    private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder;
 
     @Before
     public void setUp() {
@@ -136,7 +144,7 @@
 
         mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager,
                 () -> mStatusBar, mHandler, mAccessibilityManager, mHighPriorityProvider,
-                mINotificationManager, mLauncherApps, mShortcutManager);
+                mINotificationManager, mLauncherApps, mShortcutManager, mContextTracker, mProvider);
         mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
                 mCheckSaveListener, mOnSettingsClickListener);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
index f6b7b74..251ca9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
@@ -186,8 +186,8 @@
 
     @Test
     fun testMotionEventConsumption_downInMagneticField() {
-        // We should consume DOWN events if they occur in the field.
-        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+        // We should not consume DOWN events even if they occur in the field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
                 x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN)))
     }
 
@@ -342,10 +342,14 @@
         // Trigger the magnet animation, and block the test until it ends.
         PhysicsAnimatorTestUtils.setAllAnimationsBlock(true)
         magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = targetCenterX,
-                y = targetCenterY,
+                x = targetCenterX - 250,
+                y = targetCenterY - 250,
                 action = MotionEvent.ACTION_DOWN))
 
+        magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX,
+                y = targetCenterY))
+
         // The object's (top-left) position should now position it centered over the target.
         assertEquals(targetCenterX - objectSize / 2, objectX)
         assertEquals(targetCenterY - objectSize / 2, objectY)
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index 1dc8227..2b2fe45 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -34,11 +34,14 @@
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
+    <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
+
     <application
         android:process="com.android.networkstack.process"
         android:extractNativeLibs="false"
diff --git a/packages/Tethering/res/values-mcc204-mnc04/strings.xml b/packages/Tethering/res/values-mcc204-mnc04/strings.xml
deleted file mode 100644
index 9dadd49..0000000
--- a/packages/Tethering/res/values-mcc204-mnc04/strings.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
-    <string name="no_upstream_notification_title">Tethering has no internet</string>
-    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
-    <string name="no_upstream_notification_message">Devices can\u2019t connect</string>
-    <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
-    <string name="no_upstream_notification_disable_button">Turn off tethering</string>
-
-    <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
-    <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string>
-    <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
-    <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
-    <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
-    <string name="upstream_roaming_notification_continue_button">Continue</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml
new file mode 100644
index 0000000..8c627d5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+         "0" for disable this feature. -->
+    <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml
new file mode 100644
index 0000000..8c627d5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+         "0" for disable this feature. -->
+    <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 430fdc4..52aa5bb 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -202,4 +202,10 @@
     <string name="tethering_notification_title">@string/tethered_notification_title</string>
     <!-- String for tether enable notification message. -->
     <string name="tethering_notification_message">@string/tethered_notification_message</string>
+
+    <!-- No upstream notification is shown when there is a downstream but no upstream that is able
+         to do the tethering. -->
+    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+         "-1" for disable this feature. -->
+    <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>
 </resources>
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 4c7b2d4..049a9f6 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -38,8 +38,6 @@
 import android.net.util.SharedLog;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.ResultReceiver;
@@ -75,11 +73,6 @@
 
     private final ComponentName mSilentProvisioningService;
     private static final int MS_PER_HOUR = 60 * 60 * 1000;
-    private static final int EVENT_START_PROVISIONING = 0;
-    private static final int EVENT_STOP_PROVISIONING = 1;
-    private static final int EVENT_UPSTREAM_CHANGED = 2;
-    private static final int EVENT_MAYBE_RUN_PROVISIONING = 3;
-    private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
 
     // The ArraySet contains enabled downstream types, ex:
     // {@link TetheringManager.TETHERING_WIFI}
@@ -90,7 +83,7 @@
     private final int mPermissionChangeMessageCode;
     private final SharedLog mLog;
     private final SparseIntArray mEntitlementCacheValue;
-    private final EntitlementHandler mHandler;
+    private final Handler mHandler;
     private final StateMachine mTetherMasterSM;
     // Key: TetheringManager.TETHERING_*(downstream).
     // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
@@ -112,10 +105,7 @@
         mEntitlementCacheValue = new SparseIntArray();
         mTetherMasterSM = tetherMasterSM;
         mPermissionChangeMessageCode = permissionChangeMessageCode;
-        final Handler masterHandler = tetherMasterSM.getHandler();
-        // Create entitlement's own handler which is associated with TetherMaster thread
-        // let all entitlement processes run in the same thread.
-        mHandler = new EntitlementHandler(masterHandler.getLooper());
+        mHandler = tetherMasterSM.getHandler();
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
                 null, mHandler);
         mSilentProvisioningService = ComponentName.unflattenFromString(
@@ -172,14 +162,9 @@
      *        provisioning app UI if there is one.
      */
     public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING,
-                downstreamType, encodeBool(showProvisioningUi)));
-    }
+        if (!isValidDownstreamType(downstreamType)) return;
 
-    private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) {
-        if (!isValidDownstreamType(type)) return;
-
-        if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type);
+        if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType);
 
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
         if (isTetherProvisioningRequired(config)) {
@@ -192,9 +177,9 @@
             // till upstream change to cellular.
             if (mUsingCellularAsUpstream) {
                 if (showProvisioningUi) {
-                    runUiTetherProvisioning(type, config.activeDataSubId);
+                    runUiTetherProvisioning(downstreamType, config.activeDataSubId);
                 } else {
-                    runSilentTetherProvisioning(type, config.activeDataSubId);
+                    runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
                 }
                 mNeedReRunProvisioningUi = false;
             } else {
@@ -211,10 +196,6 @@
      * @param type tethering type from TetheringManager.TETHERING_{@code *}
      */
     public void stopProvisioningIfNeeded(int type) {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0));
-    }
-
-    private void handleStopProvisioningIfNeeded(int type) {
         if (!isValidDownstreamType(type)) return;
 
         mCurrentTethers.remove(type);
@@ -230,11 +211,6 @@
      * @param isCellular whether tethering upstream is cellular.
      */
     public void notifyUpstream(boolean isCellular) {
-        mHandler.sendMessage(mHandler.obtainMessage(
-                EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0));
-    }
-
-    private void handleNotifyUpstream(boolean isCellular) {
         if (DBG) {
             mLog.i("notifyUpstream: " + isCellular
                     + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
@@ -244,16 +220,17 @@
 
         if (mUsingCellularAsUpstream) {
             final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-            handleMaybeRunProvisioning(config);
+            maybeRunProvisioning(config);
         }
     }
 
     /** Run provisioning if needed */
     public void maybeRunProvisioning() {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING));
+        final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
+        maybeRunProvisioning(config);
     }
 
-    private void handleMaybeRunProvisioning(final TetheringConfiguration config) {
+    private void maybeRunProvisioning(final TetheringConfiguration config) {
         if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) {
             return;
         }
@@ -319,7 +296,7 @@
         }
 
         if (mUsingCellularAsUpstream) {
-            handleMaybeRunProvisioning(config);
+            maybeRunProvisioning(config);
         }
     }
 
@@ -494,46 +471,6 @@
         }
     };
 
-    private class EntitlementHandler extends Handler {
-        EntitlementHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case EVENT_START_PROVISIONING:
-                    handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2));
-                    break;
-                case EVENT_STOP_PROVISIONING:
-                    handleStopProvisioningIfNeeded(msg.arg1);
-                    break;
-                case EVENT_UPSTREAM_CHANGED:
-                    handleNotifyUpstream(toBool(msg.arg1));
-                    break;
-                case EVENT_MAYBE_RUN_PROVISIONING:
-                    final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-                    handleMaybeRunProvisioning(config);
-                    break;
-                case EVENT_GET_ENTITLEMENT_VALUE:
-                    handleRequestLatestTetheringEntitlementValue(msg.arg1,
-                            (ResultReceiver) msg.obj, toBool(msg.arg2));
-                    break;
-                default:
-                    mLog.log("Unknown event: " + msg.what);
-                    break;
-            }
-        }
-    }
-
-    private static boolean toBool(int encodedBoolean) {
-        return encodedBoolean != 0;
-    }
-
-    private static int encodeBool(boolean b) {
-        return b ? 1 : 0;
-    }
-
     private static boolean isValidDownstreamType(int type) {
         switch (type) {
             case TETHERING_BLUETOOTH:
@@ -644,13 +581,6 @@
     /** Get the last value of the tethering entitlement check. */
     public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
             boolean showEntitlementUi) {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
-                downstream, encodeBool(showEntitlementUi), receiver));
-
-    }
-
-    private void handleRequestLatestTetheringEntitlementValue(int downstream,
-            ResultReceiver receiver, boolean showEntitlementUi) {
         if (!isValidDownstreamType(downstream)) {
             receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
             return;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index f3cead9..da8bf54 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -257,7 +257,7 @@
         mContext = mDeps.getContext();
         mNetd = mDeps.getINetd(mContext);
         mLooper = mDeps.getTetheringLooper();
-        mNotificationUpdater = mDeps.getNotificationUpdater(mContext);
+        mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
 
         mPublicSync = new Object();
 
@@ -337,6 +337,11 @@
         filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
         mContext.registerReceiver(mStateReceiver, filter, null, mHandler);
 
+        final IntentFilter noUpstreamFilter = new IntentFilter();
+        noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING);
+        mContext.registerReceiver(
+                mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);
+
         final WifiManager wifiManager = getWifiManager();
         if (wifiManager != null) {
             wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
@@ -855,6 +860,8 @@
             } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
                 mLog.log("OBSERVED data saver changed");
                 handleDataSaverChanged();
+            } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {
+                untetherAll();
             }
         }
 
@@ -922,8 +929,10 @@
                     case WifiManager.WIFI_AP_STATE_ENABLED:
                         enableWifiIpServingLocked(ifname, ipmode);
                         break;
-                    case WifiManager.WIFI_AP_STATE_DISABLED:
                     case WifiManager.WIFI_AP_STATE_DISABLING:
+                        // We can see this state on the way to disabled.
+                        break;
+                    case WifiManager.WIFI_AP_STATE_DISABLED:
                     case WifiManager.WIFI_AP_STATE_FAILED:
                     default:
                         disableWifiIpServingLocked(ifname, curState);
@@ -1944,10 +1953,12 @@
     /** Get the latest value of the tethering entitlement check. */
     void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
             boolean showEntitlementUi) {
-        if (receiver != null) {
+        if (receiver == null) return;
+
+        mHandler.post(() -> {
             mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver,
                     showEntitlementUi);
-        }
+        });
     }
 
     /** Register tethering event callback */
@@ -2011,6 +2022,7 @@
         } finally {
             mTetheringEventCallbacks.finishBroadcast();
         }
+        mNotificationUpdater.onUpstreamNetworkChanged(network);
     }
 
     private void reportConfigurationChanged(TetheringConfigurationParcel config) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 893c582..9b54b5f 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -106,8 +106,9 @@
     /**
      * Get a reference to the TetheringNotificationUpdater to be used by tethering.
      */
-    public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) {
-        return new TetheringNotificationUpdater(ctx);
+    public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx,
+            @NonNull final Looper looper) {
+        return new TetheringNotificationUpdater(ctx, looper);
     }
 
     /**
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index 4287056..ff83fd1 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -19,18 +19,25 @@
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.text.TextUtils.isEmpty;
 
 import android.app.Notification;
+import android.app.Notification.Action;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.net.Network;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -39,9 +46,13 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * A class to display tethering-related notifications.
  *
@@ -58,12 +69,22 @@
     private static final String WIFI_DOWNSTREAM = "WIFI";
     private static final String USB_DOWNSTREAM = "USB";
     private static final String BLUETOOTH_DOWNSTREAM = "BT";
+    @VisibleForTesting
+    static final String ACTION_DISABLE_TETHERING =
+            "com.android.server.connectivity.tethering.DISABLE_TETHERING";
     private static final boolean NOTIFY_DONE = true;
     private static final boolean NO_NOTIFY = false;
-    // Id to update and cancel tethering notification. Must be unique within the tethering app.
-    private static final int ENABLE_NOTIFICATION_ID = 1000;
+    @VisibleForTesting
+    static final int EVENT_SHOW_NO_UPSTREAM = 1;
+    // Id to update and cancel enable notification. Must be unique within the tethering app.
+    @VisibleForTesting
+    static final int ENABLE_NOTIFICATION_ID = 1000;
     // Id to update and cancel restricted notification. Must be unique within the tethering app.
-    private static final int RESTRICTED_NOTIFICATION_ID = 1001;
+    @VisibleForTesting
+    static final int RESTRICTED_NOTIFICATION_ID = 1001;
+    // Id to update and cancel no upstream notification. Must be unique within the tethering app.
+    @VisibleForTesting
+    static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
     @VisibleForTesting
     static final int NO_ICON_ID = 0;
     @VisibleForTesting
@@ -71,14 +92,16 @@
     private final Context mContext;
     private final NotificationManager mNotificationManager;
     private final NotificationChannel mChannel;
+    private final Handler mHandler;
 
     // WARNING : the constructor is called on a different thread. Thread safety therefore
-    // relies on this value being initialized to 0, and not any other value. If you need
+    // relies on these values being initialized to 0 or false, and not any other value. If you need
     // to change this, you will need to change the thread where the constructor is invoked,
     // or to introduce synchronization.
     // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
     // This value has to be made 1 2 and 4, and OR'd with the others.
     private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+    private boolean mNoUpstream = false;
 
     // WARNING : this value is not able to being initialized to 0 and must have volatile because
     // telephony service is not guaranteed that is up before tethering service starts. If telephony
@@ -87,10 +110,30 @@
     // INVALID_SUBSCRIPTION_ID.
     private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-    @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID})
+    @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID})
     @interface NotificationId {}
 
-    public TetheringNotificationUpdater(@NonNull final Context context) {
+    private static final class MccMncOverrideInfo {
+        public final List<String> visitedMccMncs;
+        public final int homeMcc;
+        public final int homeMnc;
+        MccMncOverrideInfo(List<String> visitedMccMncs, int mcc, int mnc) {
+            this.visitedMccMncs = visitedMccMncs;
+            this.homeMcc = mcc;
+            this.homeMnc = mnc;
+        }
+    }
+
+    private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>();
+
+    static {
+        // VZW
+        sCarrierIdToMccMnc.put(
+                1839, new MccMncOverrideInfo(Arrays.asList(new String[] {"20404"}), 311, 480));
+    }
+
+    public TetheringNotificationUpdater(@NonNull final Context context,
+            @NonNull final Looper looper) {
         mContext = context;
         mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
                 .getSystemService(Context.NOTIFICATION_SERVICE);
@@ -99,6 +142,22 @@
                 context.getResources().getString(R.string.notification_channel_tethering_status),
                 NotificationManager.IMPORTANCE_LOW);
         mNotificationManager.createNotificationChannel(mChannel);
+        mHandler = new NotificationHandler(looper);
+    }
+
+    private class NotificationHandler extends Handler {
+        NotificationHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case EVENT_SHOW_NO_UPSTREAM:
+                    notifyTetheringNoUpstream();
+                    break;
+            }
+        }
     }
 
     /** Called when downstream has changed */
@@ -106,6 +165,7 @@
         if (mDownstreamTypesMask == downstreamTypesMask) return;
         mDownstreamTypesMask = downstreamTypesMask;
         updateEnableNotification();
+        updateNoUpstreamNotification();
     }
 
     /** Called when active data subscription id changed */
@@ -113,21 +173,62 @@
         if (mActiveDataSubId == subId) return;
         mActiveDataSubId = subId;
         updateEnableNotification();
+        updateNoUpstreamNotification();
     }
 
+    /** Called when upstream network changed */
+    public void onUpstreamNetworkChanged(@Nullable final Network network) {
+        final boolean isNoUpstream = (network == null);
+        if (mNoUpstream == isNoUpstream) return;
+        mNoUpstream = isNoUpstream;
+        updateNoUpstreamNotification();
+    }
+
+    @NonNull
     @VisibleForTesting
-    Resources getResourcesForSubId(@NonNull final Context c, final int subId) {
-        return SubscriptionManager.getResourcesForSubId(c, subId);
+    final Handler getHandler() {
+        return mHandler;
+    }
+
+    @NonNull
+    @VisibleForTesting
+    Resources getResourcesForSubId(@NonNull final Context context, final int subId) {
+        final Resources res = SubscriptionManager.getResourcesForSubId(context, subId);
+        final TelephonyManager tm =
+                ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+                        .createForSubscriptionId(mActiveDataSubId);
+        final int carrierId = tm.getSimCarrierId();
+        final String mccmnc = tm.getSimOperator();
+        final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId);
+        if (overrideInfo != null && overrideInfo.visitedMccMncs.contains(mccmnc)) {
+            // Re-configure MCC/MNC value to specific carrier to get right resources.
+            final Configuration config = res.getConfiguration();
+            config.mcc = overrideInfo.homeMcc;
+            config.mnc = overrideInfo.homeMnc;
+            return context.createConfigurationContext(config).getResources();
+        }
+        return res;
     }
 
     private void updateEnableNotification() {
-        final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
+        final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
 
         if (tetheringInactive || setupNotification() == NO_NOTIFY) {
             clearNotification(ENABLE_NOTIFICATION_ID);
         }
     }
 
+    private void updateNoUpstreamNotification() {
+        final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
+
+        if (tetheringInactive
+                || !mNoUpstream
+                || setupNoUpstreamNotification() == NO_NOTIFY) {
+            clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
+            mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
+        }
+    }
+
     @VisibleForTesting
     void tetheringRestrictionLifted() {
         clearNotification(RESTRICTED_NOTIFICATION_ID);
@@ -142,9 +243,38 @@
         final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
         final String title = res.getString(R.string.disable_tether_notification_title);
         final String message = res.getString(R.string.disable_tether_notification_message);
+        if (isEmpty(title) || isEmpty(message)) return;
+
+        final PendingIntent pi = PendingIntent.getActivity(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                new Intent(Settings.ACTION_TETHER_SETTINGS),
+                Intent.FLAG_ACTIVITY_NEW_TASK,
+                null /* options */);
 
         showNotification(R.drawable.stat_sys_tether_general, title, message,
-                RESTRICTED_NOTIFICATION_ID);
+                RESTRICTED_NOTIFICATION_ID, pi, new Action[0]);
+    }
+
+    private void notifyTetheringNoUpstream() {
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final String title = res.getString(R.string.no_upstream_notification_title);
+        final String message = res.getString(R.string.no_upstream_notification_message);
+        final String disableButton =
+                res.getString(R.string.no_upstream_notification_disable_button);
+        if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return;
+
+        final Intent intent = new Intent(ACTION_DISABLE_TETHERING);
+        intent.setPackage(mContext.getPackageName());
+        final PendingIntent pi = PendingIntent.getBroadcast(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                intent,
+                0 /* flags */);
+        final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build();
+
+        showNotification(R.drawable.stat_sys_tether_general, title, message,
+                NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action);
     }
 
     /**
@@ -179,12 +309,13 @@
      *
      * @return {@link android.util.SparseArray} with downstream types and icon id info.
      */
+    @NonNull
     @VisibleForTesting
     SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) {
         final String[] array = res.getStringArray(id);
         final SparseArray<Integer> icons = new SparseArray<>();
         for (String config : array) {
-            if (TextUtils.isEmpty(config)) continue;
+            if (isEmpty(config)) continue;
 
             final String[] elements = config.split(";");
             if (elements.length != 2) {
@@ -204,6 +335,18 @@
         return icons;
     }
 
+    private boolean setupNoUpstreamNotification() {
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final int delayToShowUpstreamNotification =
+                res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul);
+
+        if (delayToShowUpstreamNotification < 0) return NO_NOTIFY;
+
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM),
+                delayToShowUpstreamNotification);
+        return NOTIFY_DONE;
+    }
+
     private boolean setupNotification() {
         final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
         final SparseArray<Integer> downstreamIcons =
@@ -214,17 +357,22 @@
 
         final String title = res.getString(R.string.tethering_notification_title);
         final String message = res.getString(R.string.tethering_notification_message);
+        if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
 
-        showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID);
+        final PendingIntent pi = PendingIntent.getActivity(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                new Intent(Settings.ACTION_TETHER_SETTINGS),
+                Intent.FLAG_ACTIVITY_NEW_TASK,
+                null /* options */);
+
+        showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID, pi, new Action[0]);
         return NOTIFY_DONE;
     }
 
     private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
-            @NonNull final String message, @NotificationId final int id) {
-        final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
-        final PendingIntent pi = PendingIntent.getActivity(
-                mContext.createContextAsUser(UserHandle.CURRENT, 0),
-                0 /* requestCode */, intent, 0 /* flags */, null /* options */);
+            @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi,
+            @NonNull final Action... actions) {
         final Notification notification =
                 new Notification.Builder(mContext, mChannel.getId())
                         .setSmallIcon(iconId)
@@ -236,6 +384,7 @@
                         .setVisibility(Notification.VISIBILITY_PUBLIC)
                         .setCategory(Notification.CATEGORY_STATUS)
                         .setContentIntent(pi)
+                        .setActions(actions)
                         .build();
 
         mNotificationManager.notify(null /* tag */, id, notification);
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
index 55640db..31eaabf 100644
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -16,6 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.networkstack.tethering.tests.unit">
 
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
 
     <application android:debuggable="true">
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 7bff74b..5f88588 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -23,14 +23,26 @@
 import android.net.ConnectivityManager.TETHERING_BLUETOOTH
 import android.net.ConnectivityManager.TETHERING_USB
 import android.net.ConnectivityManager.TETHERING_WIFI
+import android.net.Network
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
 import android.os.UserHandle
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
-import androidx.test.platform.app.InstrumentationRegistry
+import android.telephony.TelephonyManager
 import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
 import com.android.internal.util.test.BroadcastInterceptingContext
 import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
+import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
+import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
+import com.android.testutils.waitForIdle
+import org.junit.After
 import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,8 +55,8 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.times
-import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
 
 const val TEST_SUBID = 1
@@ -55,10 +67,13 @@
 const val WIFI_MASK = 1 shl TETHERING_WIFI
 const val USB_MASK = 1 shl TETHERING_USB
 const val BT_MASK = 1 shl TETHERING_BLUETOOTH
-const val TITTLE = "Tethering active"
+const val TITLE = "Tethering active"
 const val MESSAGE = "Tap here to set up."
-const val TEST_TITTLE = "Hotspot active"
+const val TEST_TITLE = "Hotspot active"
 const val TEST_MESSAGE = "Tap to set up hotspot."
+const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access"
+const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet."
+const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot"
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -67,12 +82,15 @@
     // should crash if they are used before being initialized.
     @Mock private lateinit var mockContext: Context
     @Mock private lateinit var notificationManager: NotificationManager
+    @Mock private lateinit var telephonyManager: TelephonyManager
     @Mock private lateinit var defaultResources: Resources
     @Mock private lateinit var testResources: Resources
 
-    // lateinit for this class under test, as it should be reset to a different instance for every
-    // tests but should always be initialized before use (or the test should crash).
+    // lateinit for these classes under test, as they should be reset to a different instance for
+    // every test but should always be initialized before use (or the test should crash).
+    private lateinit var context: TestContext
     private lateinit var notificationUpdater: TetheringNotificationUpdater
+    private lateinit var fakeTetheringThread: HandlerThread
 
     private val ENABLE_ICON_CONFIGS = arrayOf(
             "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth",
@@ -82,11 +100,19 @@
     private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
         override fun createContextAsUser(user: UserHandle, flags: Int) =
                 if (user == UserHandle.ALL) mockContext else this
+        override fun getSystemService(name: String) =
+                if (name == Context.TELEPHONY_SERVICE) telephonyManager
+                else super.getSystemService(name)
     }
 
-    private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) {
+    private inner class WrappedNotificationUpdater(c: Context, looper: Looper)
+        : TetheringNotificationUpdater(c, looper) {
         override fun getResourcesForSubId(context: Context, subId: Int) =
-                if (subId == TEST_SUBID) testResources else defaultResources
+                when (subId) {
+                    TEST_SUBID -> testResources
+                    INVALID_SUBSCRIPTION_ID -> defaultResources
+                    else -> super.getResourcesForSubId(context, subId)
+                }
     }
 
     private fun setupResources() {
@@ -94,12 +120,20 @@
                 .getStringArray(R.array.tethering_notification_icons)
         doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources)
                 .getStringArray(R.array.tethering_notification_icons)
-        doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
+        doReturn(5).`when`(testResources)
+                .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+        doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
         doReturn(MESSAGE).`when`(defaultResources)
                 .getString(R.string.tethering_notification_message)
-        doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title)
+        doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title)
         doReturn(TEST_MESSAGE).`when`(testResources)
                 .getString(R.string.tethering_notification_message)
+        doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources)
+                .getString(R.string.no_upstream_notification_title)
+        doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources)
+                .getString(R.string.no_upstream_notification_message)
+        doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources)
+                .getString(R.string.no_upstream_notification_disable_button)
         doReturn(USB_ICON_ID).`when`(defaultResources)
                 .getIdentifier(eq("android.test:drawable/usb"), any(), any())
         doReturn(BT_ICON_ID).`when`(defaultResources)
@@ -113,35 +147,61 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        val context = TestContext(InstrumentationRegistry.getInstrumentation().context)
+        context = TestContext(InstrumentationRegistry.getInstrumentation().context)
         doReturn(notificationManager).`when`(mockContext)
                 .getSystemService(Context.NOTIFICATION_SERVICE)
-        notificationUpdater = WrappedNotificationUpdater(context)
+        fakeTetheringThread = HandlerThread(this::class.simpleName)
+        fakeTetheringThread.start()
+        notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper)
         setupResources()
     }
 
+    @After
+    fun tearDown() {
+        fakeTetheringThread.quitSafely()
+    }
+
     private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
     private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
 
-    private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") {
-        verify(notificationManager, never()).cancel(any(), anyInt())
+    private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) {
+        verify(notificationManager, never()).cancel(any(), eq(id))
 
         val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
         verify(notificationManager, times(1))
-                .notify(any(), anyInt(), notificationCaptor.capture())
+                .notify(any(), eq(id), notificationCaptor.capture())
 
         val notification = notificationCaptor.getValue()
         assertEquals(iconId, notification.smallIcon.resId)
         assertEquals(title, notification.title())
         assertEquals(text, notification.text())
+    }
 
+    private fun verifyNotificationCancelled(id: Int) =
+        verify(notificationManager, times(1)).cancel(any(), eq(id))
+
+    private val tetheringActiveNotifications =
+            listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID)
+
+    private fun verifyCancelAllTetheringActiveNotifications() {
+        tetheringActiveNotifications.forEach {
+            verifyNotificationCancelled(it)
+        }
         reset(notificationManager)
     }
 
-    private fun verifyNoNotification() {
-        verify(notificationManager, times(1)).cancel(any(), anyInt())
-        verify(notificationManager, never()).notify(any(), anyInt(), any())
-
+    private fun verifyOnlyTetheringActiveNotification(
+        notifyId: Int,
+        iconId: Int,
+        title: String,
+        text: String
+    ) {
+        tetheringActiveNotifications.forEach {
+            when (it) {
+                notifyId -> verifyNotification(iconId, title, text, notifyId)
+                else -> verifyNotificationCancelled(it)
+            }
+        }
         reset(notificationManager)
     }
 
@@ -149,7 +209,7 @@
     fun testNotificationWithDownstreamChanged() {
         // Wifi downstream. No notification.
         notificationUpdater.onDownstreamChanged(WIFI_MASK)
-        verifyNoNotification()
+        verifyCancelAllTetheringActiveNotifications()
 
         // Same downstream changed. Nothing happened.
         notificationUpdater.onDownstreamChanged(WIFI_MASK)
@@ -157,22 +217,23 @@
 
         // Wifi and usb downstreams. Show enable notification
         notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK)
-        verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE)
+        verifyOnlyTetheringActiveNotification(
+                ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE)
 
         // Usb downstream. Still show enable notification.
         notificationUpdater.onDownstreamChanged(USB_MASK)
-        verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+        verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
 
         // No downstream. No notification.
         notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
-        verifyNoNotification()
+        verifyCancelAllTetheringActiveNotifications()
     }
 
     @Test
     fun testNotificationWithActiveDataSubscriptionIdChanged() {
         // Usb downstream. Showed enable notification with default resource.
         notificationUpdater.onDownstreamChanged(USB_MASK)
-        verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+        verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
 
         // Same subId changed. Nothing happened.
         notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
@@ -180,15 +241,16 @@
 
         // Set test sub id. Clear notification with test resource.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
-        verifyNoNotification()
+        verifyCancelAllTetheringActiveNotifications()
 
         // Wifi downstream. Show enable notification with test resource.
         notificationUpdater.onDownstreamChanged(WIFI_MASK)
-        verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE)
+        verifyOnlyTetheringActiveNotification(
+                ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
 
         // No downstream. No notification.
         notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
-        verifyNoNotification()
+        verifyCancelAllTetheringActiveNotifications()
     }
 
     private fun assertIconNumbers(number: Int, configs: Array<String?>) {
@@ -227,10 +289,8 @@
 
     @Test
     fun testSetupRestrictedNotification() {
-        val title = InstrumentationRegistry.getInstrumentation().context.resources
-                .getString(R.string.disable_tether_notification_title)
-        val message = InstrumentationRegistry.getInstrumentation().context.resources
-                .getString(R.string.disable_tether_notification_message)
+        val title = context.resources.getString(R.string.disable_tether_notification_title)
+        val message = context.resources.getString(R.string.disable_tether_notification_message)
         val disallowTitle = "Tether function is disallowed"
         val disallowMessage = "Please contact your admin"
         doReturn(title).`when`(defaultResources)
@@ -244,18 +304,127 @@
 
         // User restrictions on. Show restricted notification.
         notificationUpdater.notifyTetheringDisabledByRestriction()
-        verifyNotification(R.drawable.stat_sys_tether_general, title, message)
+        verifyNotification(R.drawable.stat_sys_tether_general, title, message,
+                RESTRICTED_NOTIFICATION_ID)
+        reset(notificationManager)
 
         // User restrictions off. Clear notification.
         notificationUpdater.tetheringRestrictionLifted()
-        verifyNoNotification()
+        verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID)
+        reset(notificationManager)
 
         // Set test sub id. No notification.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
-        verifyNoNotification()
+        verifyCancelAllTetheringActiveNotifications()
 
         // User restrictions on again. Show restricted notification with test resource.
         notificationUpdater.notifyTetheringDisabledByRestriction()
-        verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage)
+        verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage,
+                RESTRICTED_NOTIFICATION_ID)
+        reset(notificationManager)
+    }
+
+    val MAX_BACKOFF_MS = 200L
+    /**
+     * Waits for all messages, including delayed ones, to be processed.
+     *
+     * This will wait until the handler has no more messages to be processed including
+     * delayed ones, or the timeout has expired. It uses an exponential backoff strategy
+     * to wait longer and longer to consume less CPU, with the max granularity being
+     * MAX_BACKOFF_MS.
+     *
+     * @return true if all messages have been processed including delayed ones, false if timeout
+     *
+     * TODO: Move this method to com.android.testutils.HandlerUtils.kt.
+     */
+    private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) {
+        fun hasMatchingMessages() =
+                if (what == null) hasMessagesOrCallbacks() else hasMessages(what)
+        val expiry = System.currentTimeMillis() + timeoutMs
+        var delay = 5L
+        while (System.currentTimeMillis() < expiry && hasMatchingMessages()) {
+            // None of Handler, Looper, Message and MessageQueue expose any way to retrieve
+            // the time when the next (let alone the last) message will be processed, so
+            // short of examining the internals with reflection sleep() is the only solution.
+            Thread.sleep(delay)
+            delay = (delay * 2)
+                    .coerceAtMost(expiry - System.currentTimeMillis())
+                    .coerceAtMost(MAX_BACKOFF_MS)
+        }
+
+        val timeout = expiry - System.currentTimeMillis()
+        if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms")
+        waitForIdle(timeout)
+    }
+
+    @Test
+    fun testNotificationWithUpstreamNetworkChanged() {
+        // Set test sub id. No notification.
+        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+        verifyCancelAllTetheringActiveNotifications()
+
+        // Wifi downstream. Show enable notification with test resource.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        verifyOnlyTetheringActiveNotification(
+                ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+
+        // There is no upstream. Show no upstream notification.
+        notificationUpdater.onUpstreamNetworkChanged(null)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+        verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
+                TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
+        reset(notificationManager)
+
+        // Same upstream network changed. Nothing happened.
+        notificationUpdater.onUpstreamNetworkChanged(null)
+        verifyZeroInteractions(notificationManager)
+
+        // Upstream come back. Clear no upstream notification.
+        notificationUpdater.onUpstreamNetworkChanged(Network(1000))
+        verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID)
+        reset(notificationManager)
+
+        // No upstream again. Show no upstream notification.
+        notificationUpdater.onUpstreamNetworkChanged(null)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+        verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
+                TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
+        reset(notificationManager)
+
+        // No downstream. No notification.
+        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+        verifyCancelAllTetheringActiveNotifications()
+
+        // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream
+        // again. Show enable notification only.
+        doReturn(-1).`when`(testResources)
+                .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+        verifyOnlyTetheringActiveNotification(
+                ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+    }
+
+    @Test
+    fun testGetResourcesForSubId() {
+        doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt())
+        doReturn(1234).`when`(telephonyManager).getSimCarrierId()
+        doReturn("000000").`when`(telephonyManager).getSimOperator()
+
+        val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId.
+        val config = context.resources.configuration
+        var res = notificationUpdater.getResourcesForSubId(context, subId)
+        assertEquals(config.mcc, res.configuration.mcc)
+        assertEquals(config.mnc, res.configuration.mnc)
+
+        doReturn(1839).`when`(telephonyManager).getSimCarrierId()
+        res = notificationUpdater.getResourcesForSubId(context, subId)
+        assertEquals(config.mcc, res.configuration.mcc)
+        assertEquals(config.mnc, res.configuration.mnc)
+
+        doReturn("20404").`when`(telephonyManager).getSimOperator()
+        res = notificationUpdater.getResourcesForSubId(context, subId)
+        assertEquals(311, res.configuration.mcc)
+        assertEquals(480, res.configuration.mnc)
     }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index d4be3a2..feb99e6 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -383,7 +383,7 @@
         }
 
         @Override
-        public TetheringNotificationUpdater getNotificationUpdater(Context ctx) {
+        public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) {
             return mNotificationUpdater;
         }
     }
@@ -1691,6 +1691,18 @@
         assertEquals(clientAddrParceled, params.clientAddr);
     }
 
+    @Test
+    public void testUpstreamNetworkChanged() {
+        final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
+                mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
+        final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+        stateMachine.chooseUpstreamType(true);
+
+        verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
+        verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network));
+    }
+
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
 }
diff --git a/read-snapshot.txt b/read-snapshot.txt
deleted file mode 100644
index e69de29..0000000
--- a/read-snapshot.txt
+++ /dev/null
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7230b00..d252f9e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2441,7 +2441,11 @@
      *    accessibility button.
      * 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
      *    version <= Q: turns on / off the accessibility service.
-     * 3) For services targeting sdk version > Q:
+     * 3) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
+     *    version > Q and request accessibility button: turn on the accessibility service if it's
+     *    not in the enabled state.
+     *    (It'll happen when a service is disabled and assigned to shortcut then upgraded.)
+     * 4) For services targeting sdk version > Q:
      *    a) Turns on / off the accessibility service, if service does not request accessibility
      *       button.
      *    b) Callbacks to accessibility service if service is bounded and requests accessibility
@@ -2475,6 +2479,13 @@
                 }
                 return true;
             }
+            if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY && targetSdk > Build.VERSION_CODES.Q
+                    && requestA11yButton) {
+                if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
+                    enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
+                    return true;
+                }
+            }
             // Callbacks to a11y service if it's bounded and requests a11y button.
             if (serviceConnection == null
                     || !userState.mBoundServices.contains(serviceConnection)
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3d68618..9d1ad42 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2653,6 +2653,11 @@
         } else if (viewState.id.equals(this.mCurrentViewId)
                 && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
             requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText);
+        } else if (viewState.id.equals(this.mCurrentViewId)
+                && (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
+            if (!TextUtils.isEmpty(filterText)) {
+                mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
+            }
         }
 
         viewState.setState(ViewState.STATE_CHANGED);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 31bccea..8ecda8f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2830,7 +2830,6 @@
 
     private int checkOperationImpl(int code, int uid, String packageName,
                 boolean raw) {
-        verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a87fb8b..b4f7cdb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -552,7 +552,8 @@
         }
         if (state == Display.STATE_OFF) {
             brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
-        } else if (brightnessState < PowerManager.BRIGHTNESS_MIN || Float.isNaN(brightnessState)) {
+        } else if (brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT
+                && brightnessState < PowerManager.BRIGHTNESS_MIN) {
             brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         } else if (brightnessState > PowerManager.BRIGHTNESS_MAX) {
             brightnessState = PowerManager.BRIGHTNESS_MAX;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e6cb371..b949d6b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -784,6 +784,7 @@
         private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
 
         private static final class Entry {
+            final int mSequenceNumber = sSequenceNumber.getAndIncrement();
             final ClientState mClientState;
             @SoftInputModeFlags
             final int mFocusedWindowSoftInputMode;
@@ -831,7 +832,7 @@
                     continue;
                 }
                 pw.print(prefix);
-                pw.println("SoftInputShowHideHistory #" + sSequenceNumber.getAndIncrement() + ":");
+                pw.println("SoftInputShowHideHistory #" + entry.mSequenceNumber + ":");
 
                 pw.print(prefix);
                 pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index 195b059..095cd14 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -253,7 +253,7 @@
                 int op = CallerIdentity.asAppOp(identity.permissionLevel);
                 if (op >= 0) {
                     if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity.uid,
-                            identity.packageName, identity.featureId, null)
+                            identity.packageName, identity.featureId, identity.listenerId)
                             != AppOpsManager.MODE_ALLOWED) {
                         continue;
                     }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6e2feeb..1345e37 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -261,8 +261,10 @@
                     .build();
             builder.addSelectedRoute(mSelectedRouteId);
 
-            for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
-                builder.addTransferableRoute(route.getId());
+            if (mBtRouteProvider != null) {
+                for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
+                    builder.addTransferableRoute(route.getId());
+                }
             }
 
             RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index aed2927..e98326b 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -30,6 +30,7 @@
 import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
 import android.service.notification.IConditionProvider;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -54,7 +55,6 @@
     private final ArraySet<String> mSystemConditionProviderNames;
     private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
             = new ArraySet<>();
-
     private Callback mCallback;
 
     public ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm) {
@@ -195,6 +195,21 @@
     }
 
     @Override
+    protected void loadDefaultsFromConfig() {
+        String defaultDndAccess = mContext.getResources().getString(
+                R.string.config_defaultDndAccessPackages);
+        if (defaultDndAccess != null) {
+            String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+            for (int i = 0; i < dnds.length; i++) {
+                if (TextUtils.isEmpty(dnds[i])) {
+                    continue;
+                }
+                addDefaultComponentOrPackage(dnds[i]);
+            }
+        }
+    }
+
+    @Override
     protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
         if (removed == null) return;
         for (int i = mRecords.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 45df368..5d3dc5f 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -21,6 +21,7 @@
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
 import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_SYSTEM;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -96,6 +97,8 @@
 
     private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
     protected static final String ENABLED_SERVICES_SEPARATOR = ":";
+    private static final String DB_VERSION_1 = "1";
+
 
     /**
      * List of components and apps that can have running {@link ManagedServices}.
@@ -107,7 +110,7 @@
     static final String ATT_VERSION = "version";
     static final String ATT_DEFAULTS = "defaults";
 
-    static final int DB_VERSION = 1;
+    static final int DB_VERSION = 2;
 
     static final int APPROVAL_BY_PACKAGE = 0;
     static final int APPROVAL_BY_COMPONENT = 1;
@@ -187,17 +190,22 @@
     protected void addDefaultComponentOrPackage(String packageOrComponent) {
         if (!TextUtils.isEmpty(packageOrComponent)) {
             synchronized (mDefaultsLock) {
-                ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
-                if (cn == null) {
+                if (mApprovalLevel == APPROVAL_BY_PACKAGE) {
                     mDefaultPackages.add(packageOrComponent);
-                } else {
+                    return;
+                }
+                ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
+                if (cn != null  && mApprovalLevel == APPROVAL_BY_COMPONENT) {
                     mDefaultPackages.add(cn.getPackageName());
                     mDefaultComponents.add(cn);
+                    return;
                 }
             }
         }
     }
 
+    protected abstract void loadDefaultsFromConfig();
+
     boolean isDefaultComponentOrPackage(String packageOrComponent) {
         synchronized (mDefaultsLock) {
             ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
@@ -504,19 +512,19 @@
 
     void readDefaults(XmlPullParser parser) {
         String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS);
-        if (defaultComponents == null) {
-            return;
-        }
-        String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR);
-        synchronized (mDefaultsLock) {
-            for (int i = 0; i < components.length; i++) {
-                if (!TextUtils.isEmpty(components[i])) {
-                    ComponentName cn = ComponentName.unflattenFromString(components[i]);
-                    if (cn != null) {
-                        mDefaultPackages.add(cn.getPackageName());
-                        mDefaultComponents.add(cn);
-                    } else {
-                        mDefaultPackages.add(components[i]);
+
+        if (!TextUtils.isEmpty(defaultComponents)) {
+            String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR);
+            synchronized (mDefaultsLock) {
+                for (int i = 0; i < components.length; i++) {
+                    if (!TextUtils.isEmpty(components[i])) {
+                        ComponentName cn = ComponentName.unflattenFromString(components[i]);
+                        if (cn != null) {
+                            mDefaultPackages.add(cn.getPackageName());
+                            mDefaultComponents.add(cn);
+                        } else {
+                            mDefaultPackages.add(components[i]);
+                        }
                     }
                 }
             }
@@ -531,9 +539,11 @@
             throws XmlPullParserException, IOException {
         // read grants
         int type;
+        String version = "";
         readDefaults(parser);
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
             String tag = parser.getName();
+            version = XmlUtils.readStringAttribute(parser, ATT_VERSION);
             if (type == XmlPullParser.END_TAG
                     && getConfig().xmlTag.equals(tag)) {
                 break;
@@ -561,9 +571,38 @@
                 }
             }
         }
+        boolean isVersionOne = TextUtils.isEmpty(version) || DB_VERSION_1.equals(version);
+        if (isVersionOne) {
+            upgradeToVersionTwo();
+        }
         rebindServices(false, USER_ALL);
     }
 
+    private void upgradeToVersionTwo() {
+        // check if any defaults are loaded
+        int defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+        if (defaultsSize == 0) {
+            // load defaults from current allowed
+            if (this.mApprovalLevel == APPROVAL_BY_COMPONENT) {
+                List<ComponentName> approvedComponents = getAllowedComponents(USER_SYSTEM);
+                for (int i = 0; i < approvedComponents.size(); i++) {
+                    addDefaultComponentOrPackage(approvedComponents.get(i).flattenToString());
+                }
+            }
+            if (this.mApprovalLevel == APPROVAL_BY_PACKAGE) {
+                List<String> approvedPkgs = getAllowedPackages(USER_SYSTEM);
+                for (int i = 0; i < approvedPkgs.size(); i++) {
+                    addDefaultComponentOrPackage(approvedPkgs.get(i));
+                }
+            }
+        }
+        // if no defaults are loaded, then load from config
+        defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+        if (defaultsSize == 0) {
+            loadDefaultsFromConfig();
+        }
+    }
+
     /**
      * Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
      */
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 54efe54..9b02b48 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -108,6 +108,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -156,6 +157,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -220,6 +222,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseArrayMap;
 import android.util.StatsEvent;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
@@ -291,6 +294,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Objects;
@@ -528,13 +532,15 @@
     private NotificationRecordLogger mNotificationRecordLogger;
     private InstanceIdSequence mNotificationInstanceIdSequence;
 
-    private static class Archive {
+    static class Archive {
+        final SparseArray<Boolean> mEnabled;
         final int mBufferSize;
-        final ArrayDeque<Pair<StatusBarNotification, Integer>> mBuffer;
+        final LinkedList<Pair<StatusBarNotification, Integer>> mBuffer;
 
         public Archive(int size) {
             mBufferSize = size;
-            mBuffer = new ArrayDeque<>(mBufferSize);
+            mBuffer = new LinkedList<>();
+            mEnabled = new SparseArray<>();
         }
 
         public String toString() {
@@ -547,7 +553,10 @@
             return sb.toString();
         }
 
-        public void record(StatusBarNotification nr, int reason) {
+        public void record(StatusBarNotification sbn, int reason) {
+            if (!mEnabled.get(sbn.getNormalizedUserId(), false)) {
+                return;
+            }
             if (mBuffer.size() == mBufferSize) {
                 mBuffer.removeFirst();
             }
@@ -555,7 +564,7 @@
             // We don't want to store the heavy bits of the notification in the archive,
             // but other clients in the system process might be using the object, so we
             // store a (lightened) copy.
-            mBuffer.addLast(new Pair<>(nr.cloneLight(), reason));
+            mBuffer.addLast(new Pair<>(sbn.cloneLight(), reason));
         }
 
         public Iterator<Pair<StatusBarNotification, Integer>> descendingIterator() {
@@ -577,60 +586,25 @@
             return  a.toArray(new StatusBarNotification[a.size()]);
         }
 
+        public void updateHistoryEnabled(@UserIdInt int userId, boolean enabled) {
+            mEnabled.put(userId, enabled);
+
+            if (!enabled) {
+                for (int i = mBuffer.size() - 1; i >= 0; i--) {
+                    if (userId == mBuffer.get(i).first.getNormalizedUserId()) {
+                        mBuffer.remove(i);
+                    }
+                }
+            }
+        }
     }
 
     void loadDefaultApprovedServices(int userId) {
-        String defaultListenerAccess = getContext().getResources().getString(
-                com.android.internal.R.string.config_defaultListenerAccessPackages);
-        if (defaultListenerAccess != null) {
-            String[] listeners =
-                    defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
-            for (int i = 0; i < listeners.length; i++) {
-                if (TextUtils.isEmpty(listeners[i])) {
-                    continue;
-                }
-                ArraySet<ComponentName> approvedListeners =
-                        mListeners.queryPackageForServices(listeners[i],
-                                MATCH_DIRECT_BOOT_AWARE
-                                        | MATCH_DIRECT_BOOT_UNAWARE, userId);
-                for (int k = 0; k < approvedListeners.size(); k++) {
-                    ComponentName cn = approvedListeners.valueAt(k);
-                    mListeners.addDefaultComponentOrPackage(cn.flattenToString());
-                }
-            }
-        }
+        mListeners.loadDefaultsFromConfig();
 
-        String defaultDndAccess = getContext().getResources().getString(
-                com.android.internal.R.string.config_defaultDndAccessPackages);
-        if (defaultDndAccess != null) {
-            String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
-            for (int i = 0; i < dnds.length; i++) {
-                if (TextUtils.isEmpty(dnds[i])) {
-                    continue;
-                }
-                mConditionProviders.addDefaultComponentOrPackage(dnds[i]);
-            }
-        }
+        mConditionProviders.loadDefaultsFromConfig();
 
-
-        ArraySet<String> assistants = new ArraySet<>();
-        String deviceAssistant = DeviceConfig.getProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
-        if (deviceAssistant != null) {
-            assistants.addAll(Arrays.asList(deviceAssistant.split(
-                    ManagedServices.ENABLED_SERVICES_SEPARATOR)));
-        }
-        assistants.addAll(Arrays.asList(getContext().getResources().getString(
-                com.android.internal.R.string.config_defaultAssistantAccessComponent)
-                .split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
-        for (int i = 0; i < assistants.size(); i++) {
-            String cnString = assistants.valueAt(i);
-            if (TextUtils.isEmpty(cnString)) {
-                continue;
-            }
-            mAssistants.addDefaultComponentOrPackage(cnString);
-        }
+        mAssistants.loadDefaultsFromConfig();
     }
 
     protected void allowDefaultApprovedServices(int userId) {
@@ -653,11 +627,14 @@
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
         if (overrideDefaultAssistantString != null) {
-            ComponentName overrideDefaultAssistant =
-                    ComponentName.unflattenFromString(overrideDefaultAssistantString);
-            if (allowAssistant(userId, overrideDefaultAssistant)) return;
+            ArraySet<ComponentName> approved = mAssistants.queryPackageForServices(
+                    overrideDefaultAssistantString,
+                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                    userId);
+            for (int i = 0; i < approved.size(); i++) {
+                if (allowAssistant(userId, approved.valueAt(i))) return;
+            }
         }
-
         ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents();
         // We should have only one default assistant by default
         // allowAssistant should execute once in practice
@@ -1638,6 +1615,9 @@
                 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
         private final Uri NOTIFICATION_RATE_LIMIT_URI
                 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
+        private final Uri NOTIFICATION_HISTORY_ENABLED
+                = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
+
 
         SettingsObserver(Handler handler) {
             super(handler);
@@ -1653,10 +1633,12 @@
                     false, this, UserHandle.USER_ALL);
             resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI,
                     false, this, UserHandle.USER_ALL);
+            resolver.registerContentObserver(NOTIFICATION_HISTORY_ENABLED,
+                    false, this, UserHandle.USER_ALL);
             update(null);
         }
 
-        @Override public void onChange(boolean selfChange, Uri uri) {
+        @Override public void onChange(boolean selfChange, Uri uri, int userId) {
             update(uri);
         }
 
@@ -1681,6 +1663,14 @@
             if (uri == null || NOTIFICATION_BUBBLES_URI.equals(uri)) {
                 mPreferencesHelper.updateBubblesEnabled();
             }
+            if (uri == null || NOTIFICATION_HISTORY_ENABLED.equals(uri)) {
+                final IntArray userIds = mUserProfiles.getCurrentProfileIds();
+
+                for (int i = 0; i < userIds.size(); i++) {
+                    mArchive.updateHistoryEnabled(userIds.get(i), Settings.Secure.getInt(resolver,
+                            Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) == 1);
+                }
+            }
         }
     }
 
@@ -1959,7 +1949,8 @@
                 mPackageManagerClient,
                 mRankingHandler,
                 mZenModeHelper,
-                new NotificationChannelLoggerImpl());
+                new NotificationChannelLoggerImpl(),
+                mAppOps);
         mRankingHelper = new RankingHelper(getContext(),
                 mRankingHandler,
                 mPreferencesHelper,
@@ -2300,7 +2291,8 @@
             mRoleObserver.init();
             LauncherApps launcherApps =
                     (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
-            mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener);
+            mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener, getLocalService(
+                    ShortcutServiceInternal.class));
             BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class);
             if (bubbsExtractor != null) {
                 bubbsExtractor.setShortcutHelper(mShortcutHelper);
@@ -8568,6 +8560,26 @@
         private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>();
         private Set<String> mAllowedAdjustments = new ArraySet<>();
 
+        @Override
+        protected void loadDefaultsFromConfig() {
+            ArraySet<String> assistants = new ArraySet<>();
+            assistants.addAll(Arrays.asList(mContext.getResources().getString(
+                    com.android.internal.R.string.config_defaultAssistantAccessComponent)
+                    .split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
+            for (int i = 0; i < assistants.size(); i++) {
+                String cnString = assistants.valueAt(i);
+                if (TextUtils.isEmpty(cnString)) {
+                    continue;
+                }
+                ArraySet<ComponentName> approved = queryPackageForServices(cnString,
+                        MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
+                for (int k = 0; k < approved.size(); k++) {
+                    ComponentName cn = approved.valueAt(k);
+                    addDefaultComponentOrPackage(cn.flattenToString());
+                }
+            }
+        }
+
         public NotificationAssistants(Context context, Object lock, UserProfiles up,
                 IPackageManager pm) {
             super(context, lock, up, pm);
@@ -9005,7 +9017,29 @@
 
         public NotificationListeners(IPackageManager pm) {
             super(getContext(), mNotificationLock, mUserProfiles, pm);
+        }
 
+        @Override
+        protected void loadDefaultsFromConfig() {
+            String defaultListenerAccess = mContext.getResources().getString(
+                    R.string.config_defaultListenerAccessPackages);
+            if (defaultListenerAccess != null) {
+                String[] listeners =
+                        defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+                for (int i = 0; i < listeners.length; i++) {
+                    if (TextUtils.isEmpty(listeners[i])) {
+                        continue;
+                    }
+                    ArraySet<ComponentName> approvedListeners =
+                            this.queryPackageForServices(listeners[i],
+                                    MATCH_DIRECT_BOOT_AWARE
+                                            | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
+                    for (int k = 0; k < approvedListeners.size(); k++) {
+                        ComponentName cn = approvedListeners.valueAt(k);
+                        addDefaultComponentOrPackage(cn.flattenToString());
+                    }
+                }
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 192df41..2bbbffc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -590,6 +590,8 @@
             pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
         }
         pw.println(prefix + "mAdjustments=" + mAdjustments);
+        pw.println(prefix + "shortcut=" + notification.getShortcutId()
+                + " found valid? " + (mShortcutInfo != null));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b3d373f..d432fc8 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,7 +16,9 @@
 
 package com.android.server.notification;
 
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -30,6 +32,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -79,7 +82,9 @@
 
 public class PreferencesHelper implements RankingConfig {
     private static final String TAG = "NotificationPrefHelper";
-    private static final int XML_VERSION = 1;
+    private static final int XML_VERSION = 2;
+    /** What version to check to do the upgrade for bubbles. */
+    private static final int XML_VERSION_BUBBLES_UPGRADE = 1;
     private static final int UNKNOWN_UID = UserHandle.USER_NULL;
     private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
 
@@ -151,6 +156,7 @@
     private final RankingHandler mRankingHandler;
     private final ZenModeHelper mZenModeHelper;
     private final NotificationChannelLogger mNotificationChannelLogger;
+    private final AppOpsManager mAppOps;
 
     private SparseBooleanArray mBadgingEnabled;
     private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE;
@@ -167,12 +173,14 @@
     }
 
     public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
-            ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger) {
+            ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger,
+            AppOpsManager appOpsManager) {
         mContext = context;
         mZenModeHelper = zenHelper;
         mRankingHandler = rankingHandler;
         mPm = pm;
         mNotificationChannelLogger = notificationChannelLogger;
+        mAppOps = appOpsManager;
 
         // STOPSHIP (b/142218092) this should be removed before ship
         if (!wasBadgingForcedTrue(context)) {
@@ -195,6 +203,15 @@
         if (type != XmlPullParser.START_TAG) return;
         String tag = parser.getName();
         if (!TAG_RANKING.equals(tag)) return;
+
+        boolean upgradeForBubbles = false;
+        if (parser.getAttributeCount() > 0) {
+            String attribute = parser.getAttributeName(0);
+            if (ATT_VERSION.equals(attribute)) {
+                int xmlVersion = Integer.parseInt(parser.getAttributeValue(0));
+                upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
+            }
+        }
         synchronized (mPackagePreferences) {
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 tag = parser.getName();
@@ -220,6 +237,16 @@
                                 }
                             }
                             boolean skipWarningLogged = false;
+                            boolean hasSAWPermission = false;
+                            if (upgradeForBubbles) {
+                                hasSAWPermission = mAppOps.noteOpNoThrow(
+                                        OP_SYSTEM_ALERT_WINDOW, uid, name, null,
+                                        "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
+                            }
+                            int bubblePref = hasSAWPermission
+                                    ? BUBBLE_PREFERENCE_ALL
+                                    : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE,
+                                            DEFAULT_BUBBLE_PREFERENCE);
 
                             PackagePreferences r = getOrCreatePackagePreferencesLocked(
                                     name, userId, uid,
@@ -231,8 +258,7 @@
                                             parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
                                     XmlUtils.readBooleanAttribute(
                                             parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
-                                    XmlUtils.readIntAttribute(
-                                            parser, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE));
+                                    bubblePref);
                             r.importance = XmlUtils.readIntAttribute(
                                     parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                             r.priority = XmlUtils.readIntAttribute(
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index f1ce3a7..1d48438 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -21,11 +21,15 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 
 import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -38,6 +42,7 @@
  * Helper for querying shortcuts.
  */
 class ShortcutHelper {
+    private static final String TAG = "ShortcutHelper";
 
     /**
      * Listener to call when a shortcut we're tracking has been removed.
@@ -48,6 +53,8 @@
 
     private LauncherApps mLauncherAppsService;
     private ShortcutListener mShortcutListener;
+    private ShortcutServiceInternal mShortcutServiceInternal;
+    private IntentFilter mSharingFilter;
 
     // Key: packageName Value: <shortcutId, notifId>
     private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
@@ -111,9 +118,17 @@
         }
     };
 
-    ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener) {
+    ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener,
+            ShortcutServiceInternal shortcutServiceInternal) {
         mLauncherAppsService = launcherApps;
         mShortcutListener = listener;
+        mSharingFilter = new IntentFilter();
+        try {
+            mSharingFilter.addDataType("*/*");
+        } catch (IntentFilter.MalformedMimeTypeException e) {
+            Slog.e(TAG, "Bad mime type", e);
+        }
+        mShortcutServiceInternal = shortcutServiceInternal;
     }
 
     @VisibleForTesting
@@ -121,6 +136,11 @@
         mLauncherAppsService = launcherApps;
     }
 
+    @VisibleForTesting
+    void setShortcutServiceInternal(ShortcutServiceInternal shortcutServiceInternal) {
+        mShortcutServiceInternal = shortcutServiceInternal;
+    }
+
     /**
      * Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}.
      */
@@ -141,7 +161,14 @@
             ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
                     ? shortcuts.get(0)
                     : null;
-            return info != null && info.isLongLived() ? info : null;
+            if (info == null || !info.isLongLived() || !info.isEnabled()) {
+                return null;
+            }
+            if (mShortcutServiceInternal.isSharingShortcut(user.getIdentifier(),
+                    "android", packageName, shortcutId, user.getIdentifier(), mSharingFilter)) {
+                return info;
+            }
+            return null;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9fb468e..7cee286 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -71,6 +71,8 @@
     public static final int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11;
     /** Indicates that dexopt should generate an app image */
     public static final int DEXOPT_GENERATE_APP_IMAGE = 1 << 12;
+    /** Indicates that dexopt may be run with different performance / priority tuned for restore */
+    public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove
 
     public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
     public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 1951e74..4b8a242 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -22,6 +22,7 @@
 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
 import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
 import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_FOR_RESTORE;
 import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
 import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
@@ -706,6 +707,7 @@
                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
                 | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
                 | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
+                | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0)
                 | hiddenApiFlag;
         return adjustDexoptFlags(dexFlags);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 59ac603..d2481b7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -66,6 +66,8 @@
 import static android.content.pm.PackageManager.INSTALL_INTERNAL;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -94,6 +96,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -1820,10 +1823,12 @@
                             state.setVerifierResponse(Binder.getCallingUid(),
                                     PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
                             broadcastPackageVerified(verificationId, originUri,
-                                    PackageManager.VERIFICATION_ALLOW, user);
+                                    PackageManager.VERIFICATION_ALLOW, null, args.mDataLoaderType,
+                                    user);
                         } else {
                             broadcastPackageVerified(verificationId, originUri,
-                                    PackageManager.VERIFICATION_REJECT, user);
+                                    PackageManager.VERIFICATION_REJECT, null, args.mDataLoaderType,
+                                    user);
                             params.setReturnCode(
                                     PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
                             state.setVerifierResponse(Binder.getCallingUid(),
@@ -1899,7 +1904,7 @@
 
                         if (state.isInstallAllowed()) {
                             broadcastPackageVerified(verificationId, originUri,
-                                    response.code, args.getUser());
+                                    response.code, null, args.mDataLoaderType, args.getUser());
                         } else {
                             params.setReturnCode(
                                     PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
@@ -3576,7 +3581,8 @@
             // Prepare a supplier of package parser for the staging manager to parse apex file
             // during the staging installation.
             final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2(
-                    mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback);
+                    mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */,
+                    mPackageParserCallback);
             mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
@@ -13575,12 +13581,17 @@
     }
 
     private void broadcastPackageVerified(int verificationId, Uri packageUri,
-            int verificationCode, UserHandle user) {
+            int verificationCode, @Nullable String rootHashString, int dataLoaderType,
+            UserHandle user) {
         final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
         intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
         intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
+        if (rootHashString != null) {
+            intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+        }
+        intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
 
         mContext.sendBroadcastAsUser(intent, user,
                 android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
@@ -14952,8 +14963,17 @@
             verificationState.setRequiredVerifierUid(requiredUid);
             final int installerUid =
                     verificationInfo == null ? -1 : verificationInfo.installerUid;
-            if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(),
-                      installFlags, installerUid)) {
+            final boolean isVerificationEnabled = isVerificationEnabled(
+                    pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
+            final boolean isV4Signed =
+                    (mArgs.signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
+            final boolean isIncrementalInstall =
+                    (mArgs.mDataLoaderType == DataLoaderType.INCREMENTAL);
+            // NOTE: We purposefully skip verification for only incremental installs when there's
+            // a v4 signature block. Otherwise, proceed with verification as usual.
+            if (!origin.existing
+                    && isVerificationEnabled
+                    && (!isIncrementalInstall || !isV4Signed)) {
                 final Intent verification = new Intent(
                         Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
                 verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -16569,7 +16589,29 @@
             }
             executePostCommitSteps(commitRequest);
         } finally {
-            if (!success) {
+            if (success) {
+                for (InstallRequest request : requests) {
+                    final InstallArgs args = request.args;
+                    if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
+                        continue;
+                    }
+                    if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
+                        continue;
+                    }
+                    // For incremental installs, we bypass the verifier prior to install. Now
+                    // that we know the package is valid, send a notice to the verifier with
+                    // the root hash of the base.apk.
+                    final String baseCodePath = request.installResult.pkg.getBaseCodePath();
+                    final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
+                    final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
+                    final int verificationId = mPendingVerificationToken++;
+                    final String rootHashString = PackageManagerServiceUtils
+                            .buildVerificationRootHashString(baseCodePath, splitCodePaths);
+                    broadcastPackageVerified(verificationId, originUri,
+                            PackageManager.VERIFICATION_ALLOW, rootHashString,
+                            args.mDataLoaderType, args.getUser());
+                }
+            } else {
                 for (ScanResult result : preparedScans.values()) {
                     if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
                             false)) {
@@ -16670,10 +16712,15 @@
                 // method because `pkg` may not be in `mPackages` yet.
                 //
                 // Also, don't fail application installs if the dexopt step fails.
+                int flags = DexoptOptions.DEXOPT_BOOT_COMPLETE
+                        | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+                if (reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_RESTORE
+                        || reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_SETUP) {
+                    flags |= DexoptOptions.DEXOPT_FOR_RESTORE;
+                }
                 DexoptOptions dexoptOptions = new DexoptOptions(packageName,
                         REASON_INSTALL,
-                        DexoptOptions.DEXOPT_BOOT_COMPLETE
-                                | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
+                        flags);
                 ScanResult result = reconciledPkg.scanResult;
 
                 // This mirrors logic from commitReconciledScanResultLocked, where the library files
@@ -16911,7 +16958,6 @@
             if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
                 parsedPackage.setSigningDetails(args.signingDetails);
             } else {
-                // TODO(b/136132412): skip for Incremental installation
                 parsedPackage.setSigningDetails(
                         ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */));
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 91afd84..5c175a6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -32,7 +32,6 @@
 import android.app.AppGlobals;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -40,7 +39,6 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.os.Build;
 import android.os.Debug;
 import android.os.Environment;
@@ -50,6 +48,9 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.V4Signature;
+import android.os.incremental.V4Signature.HashingInfo;
 import android.service.pm.PackageServiceDumpProto;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -62,6 +63,7 @@
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.HexDump;
 import com.android.server.EventLogTags;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.PackageDexUsage;
@@ -94,8 +96,6 @@
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.function.Predicate;
 import java.util.zip.GZIPInputStream;
 
@@ -943,4 +943,71 @@
             Os.chmod(currentDir.getAbsolutePath(), mode);
         }
     }
+
+    /**
+     * Returns a string that's compatible with the verification root hash extra.
+     * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH
+     */
+    @NonNull
+    public static String buildVerificationRootHashString(@NonNull String baseFilename,
+            @Nullable String[] splitFilenameArray) {
+        final StringBuilder sb = new StringBuilder();
+        final String baseFilePath =
+                baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1);
+        sb.append(baseFilePath).append(":");
+        final byte[] baseRootHash = getRootHash(baseFilename);
+        if (baseRootHash == null) {
+            sb.append("0");
+        } else {
+            sb.append(HexDump.toHexString(baseRootHash));
+        }
+        if (splitFilenameArray == null || splitFilenameArray.length == 0) {
+            return sb.toString();
+        }
+
+        for (int i = splitFilenameArray.length - 1; i >= 0; i--) {
+            final String splitFilename = splitFilenameArray[i];
+            final String splitFilePath =
+                    splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1);
+            final byte[] splitRootHash = getRootHash(splitFilename);
+            sb.append(";").append(splitFilePath).append(":");
+            if (splitRootHash == null) {
+                sb.append("0");
+            } else {
+                sb.append(HexDump.toHexString(splitRootHash));
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns the root has for the given file.
+     * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated.
+     * <p>NOTE: This currently only works on files stored on the incremental file system. The
+     * eventual goal is that this hash [among others] can be retrieved for any file.
+     */
+    @Nullable
+    private static byte[] getRootHash(String filename) {
+        try {
+            final byte[] baseFileSignature =
+                    IncrementalManager.unsafeGetFileSignature(filename);
+            if (baseFileSignature == null) {
+                throw new IOException("File signature not present");
+            }
+            final V4Signature signature =
+                    V4Signature.readFrom(baseFileSignature);
+            if (signature.hashingInfo == null) {
+                throw new IOException("Hashing info not present");
+            }
+            final HashingInfo hashInfo =
+                    HashingInfo.fromByteArray(signature.hashingInfo);
+            if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) {
+                throw new IOException("Root has not present");
+            }
+            return hashInfo.rawRootHash;
+        } catch (IOException ignore) {
+            Slog.e(TAG, "ERROR: could not load root hash from incremental install");
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c0502b8..0b6024a 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -211,24 +211,16 @@
                     UserManager.DISALLOW_ADD_USER,
                     UserManager.DISALLOW_BLUETOOTH,
                     UserManager.DISALLOW_BLUETOOTH_SHARING,
-                    UserManager.DISALLOW_CONFIG_BLUETOOTH,
                     UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
-                    UserManager.DISALLOW_CONFIG_LOCATION,
                     UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
                     UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
                     UserManager.DISALLOW_CONFIG_TETHERING,
-                    UserManager.DISALLOW_CONFIG_WIFI,
-                    UserManager.DISALLOW_CONTENT_CAPTURE,
-                    UserManager.DISALLOW_CONTENT_SUGGESTIONS,
                     UserManager.DISALLOW_DATA_ROAMING,
-                    UserManager.DISALLOW_DEBUGGING_FEATURES,
                     UserManager.DISALLOW_SAFE_BOOT,
-                    UserManager.DISALLOW_SHARE_LOCATION,
                     UserManager.DISALLOW_SMS,
                     UserManager.DISALLOW_USB_FILE_TRANSFER,
                     UserManager.DISALLOW_AIRPLANE_MODE,
                     UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
-                    UserManager.DISALLOW_OUTGOING_CALLS,
                     UserManager.DISALLOW_UNMUTE_MICROPHONE
     );
 
@@ -237,7 +229,16 @@
      * set on the parent profile instance to apply them on the personal profile.
      */
     private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS =
-            Sets.newArraySet();
+            Sets.newArraySet(
+                    UserManager.DISALLOW_CONFIG_BLUETOOTH,
+                    UserManager.DISALLOW_CONFIG_LOCATION,
+                    UserManager.DISALLOW_CONFIG_WIFI,
+                    UserManager.DISALLOW_CONTENT_CAPTURE,
+                    UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+                    UserManager.DISALLOW_DEBUGGING_FEATURES,
+                    UserManager.DISALLOW_SHARE_LOCATION,
+                    UserManager.DISALLOW_OUTGOING_CALLS
+    );
 
     /**
      * User restrictions that default to {@code true} for managed profile owners.
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index b453c89..68f3886 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -61,6 +61,10 @@
     // should get the dex metdata file if present.
     public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
 
+    // When set, indicates that dexopt is being invoked from the install flow during device restore
+    // or device setup and should be scheduled appropriately.
+    public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove
+
     // The name of package to optimize.
     private final String mPackageName;
 
@@ -99,7 +103,8 @@
                 DEXOPT_DOWNGRADE |
                 DEXOPT_AS_SHARED_LIBRARY |
                 DEXOPT_IDLE_BACKGROUND_JOB |
-                DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+                DEXOPT_INSTALL_WITH_DEX_METADATA_FILE |
+                DEXOPT_FOR_RESTORE;
         if ((flags & (~validityMask)) != 0) {
             throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
         }
@@ -155,6 +160,10 @@
         return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
     }
 
+    public boolean isDexoptInstallForRestore() {
+        return (mFlags & DEXOPT_FOR_RESTORE) != 0;
+    }
+
     public String getSplitName() {
         return mSplitName;
     }
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 1c45680..4bbe373 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -81,9 +81,6 @@
         mAlarmManager = context.getSystemService(AlarmManager.class);
         mPermissionControllerManager = context.getSystemService(PermissionControllerManager.class);
         mHandler = context.getMainThreadHandler();
-
-        // Listen for tracked uid being uninstalled
-        context.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
     }
 
     /**
@@ -171,6 +168,14 @@
     }
 
     /**
+     * Register to listen for Uids being uninstalled. This must be done outside of the
+     * PermissionManagerService lock.
+     */
+    void registerUninstallListener() {
+        mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
+    }
+
+    /**
      * A class which watches a package for inactivity and notifies the permission controller when
      * the package becomes inactive
      */
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index ccc7492..bacc7ac 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,6 +26,7 @@
 import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -1656,7 +1657,8 @@
         final int userSettableMask = FLAG_PERMISSION_USER_SET
                 | FLAG_PERMISSION_USER_FIXED
                 | FLAG_PERMISSION_REVOKED_COMPAT
-                | FLAG_PERMISSION_REVIEW_REQUIRED;
+                | FLAG_PERMISSION_REVIEW_REQUIRED
+                | FLAG_PERMISSION_ONE_TIME;
 
         final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
                 | FLAG_PERMISSION_POLICY_FIXED;
@@ -3210,16 +3212,19 @@
     }
 
     private OneTimePermissionUserManager getOneTimePermissionUserManager(@UserIdInt int userId) {
+        OneTimePermissionUserManager oneTimePermissionUserManager;
         synchronized (mLock) {
-            OneTimePermissionUserManager oneTimePermissionUserManager =
+            oneTimePermissionUserManager =
                     mOneTimePermissionUserManagers.get(userId);
-            if (oneTimePermissionUserManager == null) {
-                oneTimePermissionUserManager = new OneTimePermissionUserManager(
-                        mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0));
-                mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager);
+            if (oneTimePermissionUserManager != null) {
+                return oneTimePermissionUserManager;
             }
-            return oneTimePermissionUserManager;
+            oneTimePermissionUserManager = new OneTimePermissionUserManager(
+                    mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0));
+            mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager);
         }
+        oneTimePermissionUserManager.registerUninstallListener();
+        return oneTimePermissionUserManager;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index 67677c6..e1e6195 100644
--- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -41,10 +41,11 @@
     public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
         long[] output = new long[STATUS_KEYS.length];
         output[0] = -1;
+        output[3] = -1;
+        output[4] = -1;
         Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output);
-        if (output[0] == -1 || (output[3] == 0 && output[4] == 0)) {
-            // Could not open file or anon rss / swap are 0 indicating the process is in a zombie
-            // state.
+        if (output[0] == -1 || output[3] == -1 || output[4] == -1) {
+            // Could not open or parse file.
             return null;
         }
         final MemorySnapshot snapshot = new MemorySnapshot();
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 288c22a..1afec9c 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2059,32 +2059,35 @@
         synchronized (mProcessStatsLock) {
             final long token = Binder.clearCallingIdentity();
             try {
+                // force procstats to flush & combine old files into one store
                 long lastHighWaterMark = readProcStatsHighWaterMark(section);
                 List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
-                long highWaterMark = processStatsService.getCommittedStats(
-                        lastHighWaterMark, section, true, statsFiles);
-                if (statsFiles.size() != 1) {
-                    return StatsManager.PULL_SKIP;
-                }
-                unpackStreamedData(atomTag, pulledData, statsFiles);
+
+                ProcessStats procStats = new ProcessStats(false);
+                long highWaterMark = processStatsService.getCommittedStatsMerged(
+                        lastHighWaterMark, section, true, statsFiles, procStats);
+
+                // aggregate the data together for westworld consumption
+                ProtoOutputStream proto = new ProtoOutputStream();
+                procStats.dumpAggregatedProtoForStatsd(proto);
+
+                StatsEvent e = StatsEvent.newBuilder()
+                        .setAtomId(atomTag)
+                        .writeByteArray(proto.getBytes())
+                        .build();
+                pulledData.add(e);
+
                 new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark)
                         .delete();
                 new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + highWaterMark)
                         .createNewFile();
-            } catch (IOException e) {
-                Slog.e(TAG, "Getting procstats failed: ", e);
-                return StatsManager.PULL_SKIP;
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Getting procstats failed: ", e);
-                return StatsManager.PULL_SKIP;
-            } catch (SecurityException e) {
+            } catch (RemoteException | IOException e) {
                 Slog.e(TAG, "Getting procstats failed: ", e);
                 return StatsManager.PULL_SKIP;
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
-
         return StatsManager.PULL_SUCCESS;
     }
 
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
index 06c2354..f59d431 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
@@ -27,6 +27,7 @@
 import android.content.pm.ServiceInfo;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.text.TextUtils.SimpleStringSplitter;
 import android.util.Log;
 import android.util.Slog;
 
@@ -34,6 +35,8 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Watches for emote provider services to be installed.
@@ -51,8 +54,8 @@
     private final PackageManager mPackageManager;
     private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>();
     private final int mUserId;
-    private final String mUnbundledServicePackage;
     private final Object mLock;
+    private final Set<String> mUnbundledServicePackages = new HashSet<>();
 
     private boolean mRunning;
 
@@ -61,9 +64,19 @@
         mHandler = new Handler(true);
         mUserId = UserHandle.myUserId();
         mPackageManager = context.getPackageManager();
-        mUnbundledServicePackage = context.getString(
-                com.android.internal.R.string.config_tvRemoteServicePackage);
         mLock = lock;
+
+        // Unbundled package names supports a comma-separated list
+        SimpleStringSplitter splitter = new SimpleStringSplitter(',');
+        splitter.setString(context.getString(
+                com.android.internal.R.string.config_tvRemoteServicePackage));
+
+        splitter.forEach(packageName -> {
+            packageName = packageName.trim();
+            if (!packageName.isEmpty()) {
+                mUnbundledServicePackages.add(packageName);
+            }
+        });
     }
 
     public void start() {
@@ -157,7 +170,7 @@
         }
 
         // Check if package name is white-listed here.
-        if (!serviceInfo.packageName.equals(mUnbundledServicePackage)) {
+        if (!mUnbundledServicePackages.contains(serviceInfo.packageName)) {
             Slog.w(TAG, "Ignoring atv remote provider service because the package has not "
                     + "been set and/or whitelisted: "
                     + serviceInfo.packageName + "/" + serviceInfo.name);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e79b804..521ffa5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1374,8 +1374,8 @@
             final Rect spaceToFill = transformedBounds != null
                     ? transformedBounds
                     : inMultiWindowMode()
-                            ? task.getDisplayedBounds()
-                            : getRootTask().getParent().getDisplayedBounds();
+                            ? task.getBounds()
+                            : getRootTask().getParent().getBounds();
             mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
         } else if (mLetterbox != null) {
             mLetterbox.hide();
@@ -6663,17 +6663,6 @@
         return super.getBounds();
     }
 
-    @Override
-    Rect getDisplayedBounds() {
-        if (task != null) {
-            final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
-            if (!overrideDisplayedBounds.isEmpty()) {
-                return overrideDisplayedBounds;
-            }
-        }
-        return getBounds();
-    }
-
     @VisibleForTesting
     @Override
     Rect getAnimationBounds(int appStackClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8bf46bc..5968eede 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -91,7 +91,6 @@
 import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
 import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
-import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
 import static com.android.server.wm.TaskProto.DISPLAY_ID;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
 import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
@@ -660,8 +659,7 @@
                 setBounds(newBounds);
             } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
                 // For pinned stack, resize is now part of the {@link WindowContainerTransaction}
-                resize(new Rect(newBounds), null /* configBounds */,
-                        PRESERVE_WINDOWS, true /* deferResume */);
+                resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */);
             }
         }
         if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -835,8 +833,7 @@
             }
 
             if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
-                resize(mTmpRect2, null /*configBounds*/,
-                        false /*preserveWindows*/, true /*deferResume*/);
+                resize(mTmpRect2, false /*preserveWindows*/, true /*deferResume*/);
             }
         } finally {
             mAtmService.continueWindowLayout();
@@ -894,9 +891,6 @@
                 setTaskBounds(mDeferredBounds);
                 setBounds(mDeferredBounds);
             }
-            if (mUpdateDisplayedBoundsDeferredCalled) {
-                setTaskDisplayedBounds(mDeferredDisplayedBounds);
-            }
         }
     }
 
@@ -2966,8 +2960,7 @@
 
     // TODO: Can only be called from special methods in ActivityStackSupervisor.
     // Need to consolidate those calls points into this resize method so anyone can call directly.
-    void resize(Rect displayedBounds, Rect configBounds, boolean preserveWindows,
-            boolean deferResume) {
+    void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
         if (!updateBoundsAllowed(displayedBounds)) {
             return;
         }
@@ -2979,7 +2972,7 @@
             // Update override configurations of all tasks in the stack.
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class),
-                    displayedBounds, configBounds);
+                    displayedBounds);
             forAllTasks(c, true /* traverseTopToBottom */);
             c.recycle();
 
@@ -3000,17 +2993,10 @@
         }
     }
 
-    private static void processTaskResizeBounds(
-            Task task, Rect displayedBounds, Rect configBounds) {
+    private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
         if (!task.isResizeable()) return;
 
-        if (configBounds != null && !configBounds.isEmpty()) {
-            task.setOverrideDisplayedBounds(displayedBounds);
-            task.setBounds(configBounds);
-        } else {
-            task.setOverrideDisplayedBounds(null);
-            task.setBounds(displayedBounds);
-        }
+        task.setBounds(displayedBounds);
     }
 
     /**
@@ -3032,22 +3018,6 @@
         task.setBounds(task.isResizeable() ? bounds : null);
     }
 
-    /** Helper to setDisplayedBounds on all child tasks */
-    private void setTaskDisplayedBounds(Rect bounds) {
-        if (!updateDisplayedBoundsAllowed(bounds)) {
-            return;
-        }
-
-        final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds,
-                PooledLambda.__(Task.class), bounds);
-        forAllLeafTasks(c, true /* traverseTopToBottom */);
-        c.recycle();
-    }
-
-    private static void setTaskDisplayedBounds(Task task, Rect bounds) {
-        task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds);
-    }
-
     /**
      * Returns the top-most activity that occludes the given one, or @{code null} if none.
      */
@@ -3569,8 +3539,8 @@
     }
 
     @Override
-    void getRelativeDisplayedPosition(Point outPos) {
-        super.getRelativeDisplayedPosition(outPos);
+    void getRelativePosition(Point outPos) {
+        super.getRelativePosition(outPos);
         final int outset = getStackOutset();
         outPos.x -= outset;
         outPos.y -= outset;
@@ -3581,7 +3551,7 @@
             return;
         }
 
-        final Rect stackBounds = getDisplayedBounds();
+        final Rect stackBounds = getBounds();
         int width = stackBounds.width();
         int height = stackBounds.height();
 
@@ -3776,7 +3746,6 @@
         proto.write(FILLS_PARENT, matchParentBounds());
         getRawBounds().dumpDebug(proto, BOUNDS);
 
-        getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
         if (mLastNonFullscreenBounds != null) {
             mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f924bd4..3bccced 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1357,7 +1357,7 @@
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
             if (stack.shouldResizeStackWithLaunchBounds()) {
-                stack.resize(bounds, null /* configBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
+                stack.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
             } else {
                 // WM resizeTask must be done after the task is moved to the correct stack,
                 // because Task's setBounds() also updates dim layer's bounds, but that has
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7a04894..d92f43b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2786,6 +2786,11 @@
         }
 
         final int prevMode = task.getWindowingMode();
+        if (prevMode == windowingMode) {
+            // The task is already in split-screen and with correct windowing mode.
+            return true;
+        }
+
         moveTaskToSplitScreenPrimaryTask(task, toTop);
         return prevMode != task.getWindowingMode();
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2a676e1..a47cdc6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -26,6 +26,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -141,6 +142,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -3370,34 +3372,18 @@
         }
     }
 
+    private boolean isImeControlledByApp() {
+        return mInputMethodTarget != null && !WindowConfiguration.isSplitScreenWindowingMode(
+                mInputMethodTarget.getWindowingMode());
+    }
+
     boolean isImeAttachedToApp() {
-        return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
+        return isImeControlledByApp()
+                && mInputMethodTarget.mActivityRecord != null
                 && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                 // An activity with override bounds should be letterboxed inside its parent bounds,
                 // so it doesn't fill the screen.
-                && mInputMethodTarget.mActivityRecord.matchParentBounds());
-    }
-
-    /**
-     * Get IME target that should host IME when this display that is reparented to another
-     * WindowState.
-     * IME is never displayed in a child display.
-     * Use {@link WindowState#getImeControlTarget()} when IME target window
-     * which originally called
-     * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} is known.
-     *
-     * @return {@link WindowState} of host that controls IME.
-     *         {@code null} when {@param dc} is not a virtual display.
-     * @see DisplayContent#reparent
-     */
-    @Nullable
-    WindowState getImeControlTarget() {
-        WindowState imeTarget = mInputMethodTarget;
-        if (imeTarget != null) {
-            return imeTarget.getImeControlTarget();
-        }
-
-        return getInsetsStateController().getImeSourceProvider().getControlTarget().getWindow();
+                && mInputMethodTarget.mActivityRecord.matchParentBounds();
     }
 
     /**
@@ -3407,7 +3393,6 @@
      *
      * @param target current IME target.
      * @return {@link WindowState} that can host IME.
-     * @see DisplayContent#getImeControlTarget()
      */
     WindowState getImeHostOrFallback(WindowState target) {
         if (target != null && target.getDisplayContent().canShowIme()) {
@@ -3440,7 +3425,7 @@
 
         mInputMethodTarget = target;
         mInputMethodTargetWaitingAnim = targetWaitingAnim;
-        assignWindowLayers(false /* setLayoutNeeded */);
+        assignWindowLayers(true /* setLayoutNeeded */);
         updateImeParent();
         updateImeControlTarget();
     }
@@ -3448,8 +3433,6 @@
     /**
      * The IME input target is the window which receives input from IME. It is also a candidate
      * which controls the visibility and animation of the input method window.
-     *
-     * @param target the window that receives input from IME.
      */
     void setInputMethodInputTarget(WindowState target) {
         if (mInputMethodInputTarget != target) {
@@ -3459,12 +3442,7 @@
     }
 
     private void updateImeControlTarget() {
-        if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
-            mInputMethodControlTarget = mRemoteInsetsControlTarget;
-        } else {
-            // Otherwise, we just use the ime input target
-            mInputMethodControlTarget = mInputMethodInputTarget;
-        }
+        mInputMethodControlTarget = computeImeControlTarget();
         mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
     }
 
@@ -3477,6 +3455,19 @@
     }
 
     /**
+     * Computes the window where we hand IME control to.
+     */
+    @VisibleForTesting
+    InsetsControlTarget computeImeControlTarget() {
+        if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) {
+            return mRemoteInsetsControlTarget;
+        } else {
+            // Otherwise, we just use the ime target as received from IME.
+            return mInputMethodInputTarget;
+        }
+    }
+
+    /**
      * Computes the window the IME should be attached to.
      */
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ba14d48..4ac319d 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -171,7 +171,7 @@
 
         if (aboveIme) {
             state = new InsetsState(state);
-            state.removeSource(ITYPE_IME);
+            state.setSourceVisible(ITYPE_IME, false);
         }
 
         return state;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 0a9878d..51053b2 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -165,17 +165,6 @@
         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
 
-        // TODO(multi-display) currently only support recents animation in default display.
-        final DisplayContent dc =
-                mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent;
-        if (!mWindowManager.canStartRecentsAnimation()) {
-            notifyAnimationCancelBeforeStart(recentsAnimationRunner);
-            ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                    "Can't start recents animation, nextAppTransition=%s",
-                        dc.mAppTransition.getAppTransition());
-            return;
-        }
-
         // If the activity is associated with the recents stack, then try and get that first
         ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
                 mTargetActivityType);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 54210ae..2ce10a7 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -898,11 +898,11 @@
         TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
             mTask = task;
             mIsRecentTaskInvisible = isRecentTaskInvisible;
-            mBounds.set(mTask.getDisplayedBounds());
+            mBounds.set(mTask.getBounds());
 
             mLocalBounds.set(mBounds);
             Point tmpPos = new Point();
-            mTask.getRelativeDisplayedPosition(tmpPos);
+            mTask.getRelativePosition(tmpPos);
             mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
         }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ad1a205..1ffa01c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -52,7 +52,6 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.EMPTY;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -353,10 +352,6 @@
     final Rect mPreparedFrozenBounds = new Rect();
     final Configuration mPreparedFrozenMergedConfig = new Configuration();
 
-    // If non-empty, bounds used to display the task during animations/interactions.
-    // TODO(b/119687367): This member is temporary.
-    private final Rect mOverrideDisplayedBounds = new Rect();
-
     // Id of the previous display the stack was on.
     int mPrevDisplayId = INVALID_DISPLAY;
 
@@ -2795,29 +2790,6 @@
         }
     }
 
-    /**
-     * Displayed bounds are used to set where the task is drawn at any given time. This is
-     * separate from its actual bounds so that the app doesn't see any meaningful configuration
-     * changes during transitionary periods.
-     */
-    void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
-        if (overrideDisplayedBounds != null) {
-            adjustForMinimalTaskDimensions(overrideDisplayedBounds, mOverrideDisplayedBounds);
-            mOverrideDisplayedBounds.set(overrideDisplayedBounds);
-        } else {
-            mOverrideDisplayedBounds.setEmpty();
-        }
-        updateSurfacePosition();
-    }
-
-    /**
-     * Gets the bounds that override where the task is displayed. See
-     * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
-     */
-    Rect getOverrideDisplayedBounds() {
-        return mOverrideDisplayedBounds;
-    }
-
     boolean isResizeable(boolean checkSupportsPip) {
         return (mAtmService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
                 || (checkSupportsPip && mSupportsPictureInPicture));
@@ -2851,49 +2823,6 @@
         mPreparedFrozenMergedConfig.setTo(getConfiguration());
     }
 
-    /**
-     * Align the task to the adjusted bounds.
-     *
-     * @param adjustedBounds Adjusted bounds to which the task should be aligned.
-     * @param tempInsetBounds Insets bounds for the task.
-     * @param alignBottom True if the task's bottom should be aligned to the adjusted
-     *                    bounds's bottom; false if the task's top should be aligned
-     *                    the adjusted bounds's top.
-     */
-    void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
-        if (!isResizeable() || EMPTY.equals(getRequestedOverrideConfiguration())) {
-            return;
-        }
-
-        getBounds(mTmpRect2);
-        if (alignBottom) {
-            int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
-            mTmpRect2.offset(0, offsetY);
-        } else {
-            mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
-        }
-        if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
-            setOverrideDisplayedBounds(null);
-            setBounds(mTmpRect2);
-        } else {
-            setOverrideDisplayedBounds(mTmpRect2);
-            setBounds(tempInsetBounds);
-        }
-    }
-
-    /**
-     * Gets the current overridden displayed bounds. These will be empty if the task is not
-     * currently overriding where it is displayed.
-     */
-    @Override
-    public Rect getDisplayedBounds() {
-        if (mOverrideDisplayedBounds.isEmpty()) {
-            return super.getDisplayedBounds();
-        } else {
-            return mOverrideDisplayedBounds;
-        }
-    }
-
     @Override
     void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
             Rect outSurfaceInsets) {
@@ -3431,7 +3360,6 @@
         pw.println(prefix + "taskId=" + mTaskId);
         pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
         pw.println(doublePrefix + "appTokens=" + mChildren);
-        pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
 
         final String triplePrefix = doublePrefix + "  ";
         final String quadruplePrefix = triplePrefix + "  ";
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 77530fb..899ab24 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2062,7 +2062,7 @@
     // TODO: Remove this and use #getBounds() instead once we set an app transition animation
     // on TaskStack.
     Rect getAnimationBounds(int appStackClipMode) {
-        return getDisplayedBounds();
+        return getBounds();
     }
 
     /**
@@ -2124,7 +2124,7 @@
         // Separate position and size for use in animators.
         mTmpRect.set(getAnimationBounds(appStackClipMode));
         if (sHierarchicalAnimations) {
-            getRelativeDisplayedPosition(mTmpPoint);
+            getRelativePosition(mTmpPoint);
         } else {
             mTmpPoint.set(mTmpRect.left, mTmpRect.top);
         }
@@ -2399,7 +2399,7 @@
             return;
         }
 
-        getRelativeDisplayedPosition(mTmpPos);
+        getRelativePosition(mTmpPos);
         if (mTmpPos.equals(mLastSurfacePosition)) {
             return;
         }
@@ -2414,16 +2414,6 @@
     }
 
     /**
-     * Displayed bounds specify where to display this container at. It differs from bounds during
-     * certain operations (like animation or interactive dragging).
-     *
-     * @return the bounds to display this container at.
-     */
-    Rect getDisplayedBounds() {
-        return getBounds();
-    }
-
-    /**
      * The {@code outFrame} retrieved by this method specifies where the animation will finish
      * the entrance animation, as the next frame will display the window at these coordinates. In
      * case of exit animation, this is where the animation will start, as the frame before the
@@ -2443,7 +2433,7 @@
         outSurfaceInsets.setEmpty();
     }
 
-    void getRelativeDisplayedPosition(Point outPos) {
+    void getRelativePosition(Point outPos) {
         // In addition to updateSurfacePosition, we keep other code that sets
         // position from fighting with the organizer
         if (isOrganized()) {
@@ -2451,11 +2441,11 @@
             return;
         }
 
-        final Rect dispBounds = getDisplayedBounds();
+        final Rect dispBounds = getBounds();
         outPos.set(dispBounds.left, dispBounds.top);
         final WindowContainer parent = getParent();
         if (parent != null) {
-            final Rect parentBounds = parent.getDisplayedBounds();
+            final Rect parentBounds = parent.getBounds();
             outPos.offset(-parentBounds.left, -parentBounds.top);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a488af7..f55a1b3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2445,8 +2445,17 @@
             if (controls != null) {
                 final int length = Math.min(controls.length, outControls.length);
                 for (int i = 0; i < length; i++) {
-                    outControls[i] = win.isClientLocal()
-                            ? new InsetsSourceControl(controls[i]) : controls[i];
+                    final InsetsSourceControl control = controls[i];
+
+                    // Check if we are sending invalid leashes.
+                    final SurfaceControl leash = control != null ? control.getLeash() : null;
+                    if (leash != null && !leash.isValid()) {
+                        Slog.wtf(TAG, leash + " is not valid before sending to " + win,
+                                leash.getReleaseStack());
+                    }
+
+                    outControls[i] = win.isClientLocal() && control != null
+                            ? new InsetsSourceControl(control) : control;
                 }
             }
         }
@@ -2788,18 +2797,6 @@
         return mRecentsAnimationController;
     }
 
-    /**
-     * @return Whether the next recents animation can continue to start. Called from
-     *         {@link RecentsAnimation#startRecentsActivity}.
-     */
-    boolean canStartRecentsAnimation() {
-        // TODO(multi-display): currently only default display support recent activity
-        if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
-            return false;
-        }
-        return true;
-    }
-
     void cancelRecentsAnimation(
             @RecentsAnimationController.ReorderMode int reorderMode, String reason) {
         if (mRecentsAnimationController != null) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d9c0219..8b27667 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -320,7 +320,7 @@
             final ActivityStack stack = (ActivityStack) container;
             if (stack.inPinnedWindowingMode()) {
                 stack.resize(config.windowConfiguration.getBounds(),
-                        null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
+                        PRESERVE_WINDOWS, true /* deferResume */);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5f2e14f..00c84ec 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -999,18 +999,6 @@
         frame.inset(left, top, right, bottom);
     }
 
-    @Override
-    public Rect getDisplayedBounds() {
-        final Task task = getTask();
-        if (task != null) {
-            Rect bounds = task.getOverrideDisplayedBounds();
-            if (!bounds.isEmpty()) {
-                return bounds;
-            }
-        }
-        return super.getDisplayedBounds();
-    }
-
     void computeFrame(DisplayFrames displayFrames) {
         getLayoutingWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
         computeFrameLw();
@@ -1065,7 +1053,7 @@
             layoutXDiff = 0;
             layoutYDiff = 0;
         } else {
-            windowFrames.mContainingFrame.set(getDisplayedBounds());
+            windowFrames.mContainingFrame.set(getBounds());
             if (mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) {
 
                 // If the bounds are frozen, we still want to translate the window freely and only
@@ -1223,7 +1211,7 @@
             parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
             parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
         } else if (parent != null) {
-            final Rect parentBounds = parent.getDisplayedBounds();
+            final Rect parentBounds = parent.getBounds();
             parentLeft = parentBounds.left;
             parentTop = parentBounds.top;
         }
@@ -1469,7 +1457,7 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
-        if (dc != null && mDisplayContent != null
+        if (dc != null && mDisplayContent != null && dc != mDisplayContent
                 && mDisplayContent.mInputMethodInputTarget == this) {
             dc.setInputMethodInputTarget(mDisplayContent.mInputMethodInputTarget);
             mDisplayContent.mInputMethodInputTarget = null;
@@ -5290,7 +5278,7 @@
             outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
                     -parent.mWindowFrames.mFrame.top + mTmpPoint.y);
         } else if (parentWindowContainer != null) {
-            final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
+            final Rect parentBounds = parentWindowContainer.getBounds();
             outPoint.offset(-parentBounds.left, -parentBounds.top);
         }
 
@@ -5342,7 +5330,9 @@
             // this promotion.
             final WindowState imeTarget = getDisplayContent().mInputMethodTarget;
             boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
-                    && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0;
+                    && imeTarget.mToken == mToken
+                    && getParent() != null
+                    && imeTarget.compareTo(this) <= 0;
             return inTokenWithAndAboveImeTarget;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d4470f8..9957707 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1030,7 +1030,7 @@
                         mTmpPos.x = 0;
                         mTmpPos.y = 0;
                         if (stack != null) {
-                            stack.getRelativeDisplayedPosition(mTmpPos);
+                            stack.getRelativePosition(mTmpPos);
                         }
 
                         xOffset = -mTmpPos.x;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4faed65..09d1d3a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1986,29 +1986,32 @@
             Sets.newSet(
                     UserManager.DISALLOW_CONFIG_DATE_TIME,
                     UserManager.DISALLOW_ADD_USER,
-                    UserManager.DISALLOW_BLUETOOTH,
                     UserManager.DISALLOW_BLUETOOTH_SHARING,
-                    UserManager.DISALLOW_CONFIG_BLUETOOTH,
                     UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
-                    UserManager.DISALLOW_CONFIG_LOCATION,
                     UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
                     UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
                     UserManager.DISALLOW_CONFIG_TETHERING,
-                    UserManager.DISALLOW_CONFIG_WIFI,
-                    UserManager.DISALLOW_CONTENT_CAPTURE,
-                    UserManager.DISALLOW_CONTENT_SUGGESTIONS,
                     UserManager.DISALLOW_DATA_ROAMING,
-                    UserManager.DISALLOW_DEBUGGING_FEATURES,
                     UserManager.DISALLOW_SAFE_BOOT,
-                    UserManager.DISALLOW_SHARE_LOCATION,
                     UserManager.DISALLOW_SMS,
                     UserManager.DISALLOW_USB_FILE_TRANSFER,
                     UserManager.DISALLOW_AIRPLANE_MODE,
                     UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
-                    UserManager.DISALLOW_OUTGOING_CALLS,
                     UserManager.DISALLOW_UNMUTE_MICROPHONE
             );
 
+    private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS =
+            Sets.newSet(
+                    UserManager.DISALLOW_CONFIG_BLUETOOTH,
+                    UserManager.DISALLOW_CONFIG_LOCATION,
+                    UserManager.DISALLOW_CONFIG_WIFI,
+                    UserManager.DISALLOW_CONTENT_CAPTURE,
+                    UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+                    UserManager.DISALLOW_DEBUGGING_FEATURES,
+                    UserManager.DISALLOW_SHARE_LOCATION,
+                    UserManager.DISALLOW_OUTGOING_CALLS
+            );
+
     public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
         final int MANAGED_PROFILE_ADMIN_UID =
                 UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
@@ -2021,7 +2024,10 @@
                 .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
 
         for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS) {
-            addAndRemoveUserRestrictionOnParentDpm(restriction);
+            addAndRemoveGlobalUserRestrictionOnParentDpm(restriction);
+        }
+        for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS) {
+            addAndRemoveLocalUserRestrictionOnParentDpm(restriction);
         }
 
         parentDpm.setCameraDisabled(admin1, true);
@@ -2047,7 +2053,7 @@
         reset(getServices().userManagerInternal);
     }
 
-    private void addAndRemoveUserRestrictionOnParentDpm(String restriction) {
+    private void addAndRemoveGlobalUserRestrictionOnParentDpm(String restriction) {
         parentDpm.addUserRestriction(admin1, restriction);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
@@ -2063,6 +2069,22 @@
         );
     }
 
+    private void addAndRemoveLocalUserRestrictionOnParentDpm(String restriction) {
+        parentDpm.addUserRestriction(admin1, restriction);
+        verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+                eq(DpmMockContext.CALLER_USER_HANDLE),
+                MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM, restriction),
+                eq(false));
+        parentDpm.clearUserRestriction(admin1, restriction);
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(),
+                dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                        .getParentActiveAdmin()
+                        .getEffectiveRestrictions()
+        );
+    }
+
     public void testNoDefaultEnabledUserRestrictions() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 62589eb..22020ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -54,6 +54,7 @@
         assertFalse(opt.isForce());
         assertFalse(opt.isDexoptIdleBackgroundJob());
         assertFalse(opt.isDexoptInstallWithDexMetadata());
+        assertFalse(opt.isDexoptInstallForRestore());
     }
 
     @Test
@@ -67,7 +68,8 @@
                 DexoptOptions.DEXOPT_DOWNGRADE  |
                 DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
                 DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
-                DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+                DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE |
+                DexoptOptions.DEXOPT_FOR_RESTORE;
 
         DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
         assertEquals(mPackageName, opt.getPackageName());
@@ -82,6 +84,7 @@
         assertTrue(opt.isDexoptAsSharedLibrary());
         assertTrue(opt.isDexoptIdleBackgroundJob());
         assertTrue(opt.isDexoptInstallWithDexMetadata());
+        assertTrue(opt.isDexoptInstallForRestore());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index af89761..939b7a0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -35,6 +35,7 @@
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.platform.test.annotations.Presubmit;
+import android.util.SparseIntArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -542,7 +543,12 @@
 
     @Test
     public void testUsesSdk() throws Exception {
-        parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+        ParsedPackage pkg =
+                parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+        SparseIntArray minExtVers = pkg.getMinExtensionVersions();
+        assertEquals(1, minExtVers.size());
+        assertEquals(0, minExtVers.get(10000, -1));
+
         try {
             parsePackage("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5, x -> x);
             fail("Expected parsing exception due to incompatible extension SDK version");
diff --git a/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java b/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java
index 0a2bb62..55e526f 100644
--- a/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java
@@ -84,6 +84,22 @@
     }
 
     @Test
+    public void acceptsValidCsvPackageName() {
+        // Test intentionally includes empty spacing for a more complex test
+        when(mMockResources.getString(com.android.internal.R.string.config_tvRemoteServicePackage))
+            .thenReturn(",,foo,  " + TV_REMOTE_SERVICE_PACKAGE_NAME + ",bar, baz,,");
+        assertTrue(mTvRemoteProviderWatcher.verifyServiceTrusted(createTvServiceInfo()));
+    }
+
+    @Test
+    public void rejectsInvalidCsvPackageName() {
+        // Checks include empty strings to validate that processing as well
+        when(mMockResources.getString(com.android.internal.R.string.config_tvRemoteServicePackage))
+            .thenReturn(",,foo,,  ,bar,   baz,,");
+        assertFalse(mTvRemoteProviderWatcher.verifyServiceTrusted(createTvServiceInfo()));
+    }
+
+    @Test
     public void tvServiceIsTrusted() {
         assertTrue(mTvRemoteProviderWatcher.verifyServiceTrusted(createTvServiceInfo()));
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
new file mode 100644
index 0000000..c69ef8d
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static android.os.UserHandle.USER_CURRENT;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ArchiveTest extends UiServiceTestCase {
+    private static final int SIZE = 5;
+
+    private NotificationManagerService.Archive mArchive;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mArchive = new NotificationManagerService.Archive(SIZE);
+        mArchive.updateHistoryEnabled(USER_SYSTEM, true);
+        mArchive.updateHistoryEnabled(USER_CURRENT, true);
+    }
+
+    private StatusBarNotification getNotification(String pkg, int id, UserHandle user) {
+        Notification n = new Notification.Builder(getContext(), "test")
+                .setContentTitle("A")
+                .setWhen(1205)
+                .build();
+        return  new StatusBarNotification(
+                pkg, pkg, id, null, 0, 0, n, user, null, System.currentTimeMillis());
+    }
+
+
+    @Test
+    public void testRecordAndRead() {
+        List<String> expected = new ArrayList<>();
+        for (int i = 0; i < SIZE; i++) {
+            StatusBarNotification sbn = getNotification("pkg" + i, i,
+                    UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT));
+            expected.add(sbn.getKey());
+            mArchive.record(sbn, REASON_CANCEL);
+        }
+
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        assertThat(actual).hasSize(expected.size());
+        for (StatusBarNotification sbn : actual) {
+            assertThat(expected).contains(sbn.getKey());
+        }
+    }
+
+    @Test
+    public void testRecordAndRead_overLimit() {
+        List<String> expected = new ArrayList<>();
+        for (int i = 0; i < (SIZE * 2); i++) {
+            StatusBarNotification sbn = getNotification("pkg" + i, i, UserHandle.of(USER_SYSTEM));
+            mArchive.record(sbn, REASON_CANCEL);
+            if (i >= SIZE) {
+                expected.add(sbn.getKey());
+            }
+        }
+
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray((SIZE * 2), true));
+        assertThat(actual).hasSize(expected.size());
+        for (StatusBarNotification sbn : actual) {
+            assertThat(expected).contains(sbn.getKey());
+        }
+    }
+
+    @Test
+    public void testDoesNotRecordIfHistoryDisabled() {
+        mArchive.updateHistoryEnabled(USER_CURRENT, false);
+        List<String> expected = new ArrayList<>();
+        for (int i = 0; i < SIZE; i++) {
+            StatusBarNotification sbn = getNotification("pkg" + i, i,
+                    UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT));
+            mArchive.record(sbn, REASON_CANCEL);
+            if (i % 2 ==0) {
+                expected.add(sbn.getKey());
+            }
+        }
+
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        assertThat(actual).hasSize(expected.size());
+        for (StatusBarNotification sbn : actual) {
+            assertThat(expected).contains(sbn.getKey());
+        }
+    }
+
+    @Test
+    public void testRemovesEntriesWhenHistoryDisabled() {
+        mArchive.updateHistoryEnabled(USER_CURRENT, true);
+        List<String> expected = new ArrayList<>();
+        for (int i = 0; i < SIZE; i++) {
+            StatusBarNotification sbn = getNotification("pkg" + i, i,
+                    UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT));
+            mArchive.record(sbn, REASON_CANCEL);
+            if (i % 2 ==0) {
+                expected.add(sbn.getKey());
+            }
+        }
+        mArchive.updateHistoryEnabled(USER_CURRENT, false);
+
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        assertThat(actual).hasSize(expected.size());
+        for (StatusBarNotification sbn : actual) {
+            assertThat(expected).contains(sbn.getKey());
+        }
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 7b7470c..28ff9a5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -77,6 +77,7 @@
 import java.io.ByteArrayOutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -96,6 +97,10 @@
 
     UserInfo mZero = new UserInfo(0, "zero", 0);
     UserInfo mTen = new UserInfo(10, "ten", 0);
+    private String mDefaultsString;
+    private String mVersionString;
+    private final Set<ComponentName> mDefaults = new ArraySet();
+    private ManagedServices mService;
 
     private static final String SETTING = "setting";
     private static final String SECONDARY_SETTING = "secondary_setting";
@@ -106,8 +111,8 @@
     private ArrayMap<Integer, String> mExpectedSecondaryComponentNames;
 
     // type : user : list of approved
-    private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary = new ArrayMap<>();
-    private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary = new ArrayMap<>();
+    private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary;
+    private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary;
 
     @Before
     public void setUp() throws Exception {
@@ -132,6 +137,9 @@
         profileIds.add(12);
         when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
 
+        mVersionString = "2";
+        mExpectedPrimary = new ArrayMap<>();
+        mExpectedSecondary = new ArrayMap<>();
         mExpectedPrimaryPackages = new ArrayMap<>();
         mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
         mExpectedPrimaryPackages.put(10, "this.is.another.package");
@@ -155,6 +163,8 @@
                 "this.is.another.package:component:package");
         mExpectedSecondary.put(APPROVAL_BY_PACKAGE, mExpectedSecondaryPackages);
         mExpectedSecondary.put(APPROVAL_BY_COMPONENT, mExpectedSecondaryComponentNames);
+        mService = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
     }
 
     @Test
@@ -1178,9 +1188,99 @@
         }
     }
 
+    @Test
+    public void loadDefaults_noVersionNoDefaults() throws Exception {
+        resetComponentsAndPackages();
+        loadXml(mService);
+        assertEquals(mService.getDefaultComponents().size(), 0);
+    }
+
+    @Test
+    public void loadDefaults_noVersionNoDefaultsOneActive() throws Exception {
+        resetComponentsAndPackages();
+        mService.addDefaultComponentOrPackage("package/class");
+        loadXml(mService);
+        assertEquals(1, mService.getDefaultComponents().size());
+        assertTrue(mService.getDefaultComponents()
+                .contains(ComponentName.unflattenFromString("package/class")));
+    }
+
+    @Test
+    public void loadDefaults_noVersionWithDefaults() throws Exception {
+        resetComponentsAndPackages();
+        mDefaults.add(new ComponentName("default", "class"));
+        loadXml(mService);
+        assertEquals(mService.getDefaultComponents(), mDefaults);
+    }
+
+    @Test
+    public void loadDefaults_versionOneWithDefaultsWithActive() throws Exception {
+        resetComponentsAndPackages();
+        mDefaults.add(new ComponentName("default", "class"));
+        mExpectedPrimaryComponentNames.put(0, "package/class");
+        mVersionString = "1";
+        loadXml(mService);
+        assertEquals(mService.getDefaultComponents(),
+                new ArraySet(Arrays.asList(new ComponentName("package", "class"))));
+    }
+
+    @Test
+    public void loadDefaults_versionTwoWithDefaultsWithActive() throws Exception {
+        resetComponentsAndPackages();
+        mDefaults.add(new ComponentName("default", "class"));
+        mDefaultsString = "default/class";
+        mExpectedPrimaryComponentNames.put(0, "package/class");
+        mVersionString = "2";
+        loadXml(mService);
+        assertEquals(1, mService.getDefaultComponents().size());
+        mDefaults.forEach(pkg -> {
+            assertTrue(mService.getDefaultComponents().contains(pkg));
+        });
+    }
+
+    @Test
+    public void loadDefaults_versionOneWithXMLDefaultsWithActive() throws Exception {
+        resetComponentsAndPackages();
+        mDefaults.add(new ComponentName("default", "class"));
+        mDefaultsString = "xml/class";
+        mExpectedPrimaryComponentNames.put(0, "package/class");
+        mVersionString = "1";
+        loadXml(mService);
+        assertEquals(mService.getDefaultComponents(),
+                new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
+    }
+
+    @Test
+    public void loadDefaults_versionTwoWithXMLDefaultsWithActive() throws Exception {
+        resetComponentsAndPackages();
+        mDefaults.add(new ComponentName("default", "class"));
+        mDefaultsString = "xml/class";
+        mExpectedPrimaryComponentNames.put(0, "package/class");
+        mVersionString = "2";
+        loadXml(mService);
+        assertEquals(mService.getDefaultComponents(),
+                new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
+    }
+
+    private void resetComponentsAndPackages() {
+        ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1);
+        ArrayMap<Integer, String> emptyPkgs = new ArrayMap(0);
+        empty.append(mService.mApprovalLevel, emptyPkgs);
+        mExpectedPrimary = empty;
+        mExpectedPrimaryComponentNames = emptyPkgs;
+        mExpectedPrimaryPackages = emptyPkgs;
+        mExpectedSecondary = empty;
+        mExpectedSecondaryComponentNames = emptyPkgs;
+        mExpectedSecondaryPackages = emptyPkgs;
+    }
+
     private void loadXml(ManagedServices service) throws Exception {
         final StringBuffer xml = new StringBuffer();
-        xml.append("<" + service.getConfig().xmlTag + ">\n");
+        String xmlTag = service.getConfig().xmlTag;
+        xml.append("<" + xmlTag
+                + (mDefaultsString != null ? " defaults=\"" + mDefaultsString + "\" " : "")
+                + (mVersionString != null ? " version=\"" + mVersionString + "\" " : "")
+                + ">\n");
         for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) {
             xml.append(getXmlEntry(
                     mExpectedPrimary.get(service.mApprovalLevel).get(userId), userId, true));
@@ -1197,7 +1297,7 @@
                 + ManagedServices.ATT_USER_ID + "=\"98\" "
                 + ManagedServices.ATT_IS_PRIMARY + "=\"false\" "
                 + ManagedServices.ATT_APPROVED_LIST + "=\"98\" />\n");
-        xml.append("</" + service.getConfig().xmlTag + ">");
+        xml.append("</" + xmlTag + ">");
 
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(new BufferedInputStream(
@@ -1224,6 +1324,7 @@
 
     private void addExpectedServices(final ManagedServices service, final List<String> packages,
             int userId) {
+        ManagedServices.Config config = service.getConfig();
         when(mPm.queryIntentServicesAsUser(any(), anyInt(), eq(userId))).
                 thenAnswer(new Answer<List<ResolveInfo>>() {
                     @Override
@@ -1233,7 +1334,7 @@
                         Intent invocationIntent = (Intent) args[0];
                         if (invocationIntent != null) {
                             if (invocationIntent.getAction().equals(
-                                    service.getConfig().serviceInterface)
+                                    config.serviceInterface)
                                     && packages.contains(invocationIntent.getPackage())) {
                                 List<ResolveInfo> dummyServices = new ArrayList<>();
                                 for (int i = 1; i <= 3; i ++) {
@@ -1431,6 +1532,11 @@
         }
 
         @Override
+        protected void loadDefaultsFromConfig() {
+            mDefaultComponents.addAll(mDefaults);
+        }
+
+        @Override
         protected String getRequiredPermission() {
             return null;
         }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 88186cd..ab4dc47 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -72,13 +72,13 @@
 
     Object mLock = new Object();
 
+
     UserInfo mZero = new UserInfo(0, "zero", 0);
     UserInfo mTen = new UserInfo(10, "ten", 0);
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-
         getContext().setMockPackageManager(mPm);
         getContext().addMockSystemService(Context.USER_SERVICE, mUm);
         mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));
@@ -122,7 +122,7 @@
 
     @Test
     public void testXmlUpgradeExistingApprovedComponents() throws Exception {
-        String xml = "<enabled_assistants>"
+        String xml = "<enabled_assistants version=\"2\" defaults=\"b\\b\">"
                 + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
                 + "</enabled_assistants>";
 
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 3cd0e92..ecdd9e5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -113,6 +113,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Color;
@@ -234,6 +235,8 @@
     @Mock
     private LauncherApps mLauncherApps;
     @Mock
+    private ShortcutServiceInternal mShortcutServiceInternal;
+    @Mock
     ActivityManager mActivityManager;
     @Mock
     Resources mResources;
@@ -466,6 +469,7 @@
 
         mShortcutHelper = mService.getShortcutHelper();
         mShortcutHelper.setLauncherApps(mLauncherApps);
+        mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
 
         // Set the testable bubble extractor
         RankingHelper rankingHelper = mService.getRankingHelper();
@@ -782,33 +786,16 @@
         userInfos.add(new UserInfo(0, "", 0));
         final ArraySet<ComponentName> validAssistants = new ArraySet<>();
         validAssistants.add(ComponentName.unflattenFromString(testComponent));
-        final String originalComponent = DeviceConfig.getProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE
-        );
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
-                testComponent,
-                false
-        );
         when(mActivityManager.isLowRamDevice()).thenReturn(false);
         when(mAssistants.queryPackageForServices(isNull(), anyInt(), anyInt()))
                 .thenReturn(validAssistants);
-        when(mAssistants.getDefaultComponents()).thenReturn(new ArraySet<>());
+        when(mAssistants.getDefaultComponents()).thenReturn(validAssistants);
         when(mUm.getEnabledProfiles(anyInt())).thenReturn(userInfos);
 
         mService.setDefaultAssistantForUser(userId);
 
         verify(mAssistants).setPackageOrComponentEnabled(
                 eq(testComponent), eq(userId), eq(true), eq(true));
-
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
-                originalComponent,
-                false
-        );
     }
 
     @Test
@@ -6088,8 +6075,11 @@
         List<ShortcutInfo> shortcutInfos = new ArrayList<>();
         ShortcutInfo info = mock(ShortcutInfo.class);
         when(info.isLongLived()).thenReturn(true);
+        when(info.isEnabled()).thenReturn(true);
         shortcutInfos.add(info);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
 
         // Test: Send the bubble notification
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
@@ -6148,8 +6138,11 @@
         List<ShortcutInfo> shortcutInfos = new ArrayList<>();
         ShortcutInfo info = mock(ShortcutInfo.class);
         when(info.isLongLived()).thenReturn(true);
+        when(info.isEnabled()).thenReturn(true);
         shortcutInfos.add(info);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
 
         // Test: Send the bubble notification
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
@@ -6289,7 +6282,7 @@
 
         mService.loadDefaultApprovedServices(USER_SYSTEM);
 
-        verify(mConditionProviders, times(1)).addDefaultComponentOrPackage("test");
+        verify(mConditionProviders, times(1)).loadDefaultsFromConfig();
     }
 
     // TODO: add tests for the rest of the non-empty cases
@@ -6492,7 +6485,10 @@
         ShortcutInfo si = mock(ShortcutInfo.class);
         when(si.getShortLabel()).thenReturn("Hello");
         when(si.isLongLived()).thenReturn(true);
+        when(si.isEnabled()).thenReturn(true);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
 
         List<ConversationChannelWrapper> conversations =
                 mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 427237c..ac51750 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.notification;
 
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
@@ -50,6 +53,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -132,6 +136,7 @@
     @Spy IContentProvider mTestIContentProvider = new MockIContentProvider();
     @Mock Context mContext;
     @Mock ZenModeHelper mMockZenModeHelper;
+    @Mock AppOpsManager mAppOpsManager;
 
     private NotificationManager.Policy mTestNotificationPolicy;
 
@@ -187,7 +192,10 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(),
+                anyString(), eq(null), anyString())).thenReturn(MODE_DEFAULT);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         resetZenModeHelper();
 
         mAudioAttributes = new AudioAttributes.Builder()
@@ -1464,7 +1472,8 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         assertFalse(mHelper.areChannelsBypassingDnd());
         verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
         resetZenModeHelper();
@@ -1475,7 +1484,8 @@
         // start notification policy off with mAreChannelsBypassingDnd = false
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         assertFalse(mHelper.areChannelsBypassingDnd());
         verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
         resetZenModeHelper();
@@ -2241,7 +2251,8 @@
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
                 + "</package>\n"
                 + "</ranking>\n";
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadByteArrayXml(preQXml.getBytes(), true, UserHandle.USER_SYSTEM);
 
         assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -2253,7 +2264,8 @@
         mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -2349,7 +2361,8 @@
         mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -2360,7 +2373,8 @@
         mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -2372,7 +2386,8 @@
         mHelper.revokeNotificationDelegate(PKG_O, UID_O);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -2384,7 +2399,8 @@
         mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         // appears disabled
@@ -2402,7 +2418,8 @@
         mHelper.revokeNotificationDelegate(PKG_O, UID_O);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         // appears disabled
@@ -2417,17 +2434,74 @@
 
     @Test
     public void testBubblePreference_defaults() throws Exception {
-        assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
+        assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
-        assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
+        assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
         assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
     }
 
     @Test
+    public void testBubblePreference_upgradeWithSAWPermission() throws Exception {
+        when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
+                anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n"
+                + "<channel id=\"someId\" name=\"hi\""
+                + " importance=\"3\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O));
+        assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testBubblePreference_upgradeWithSAWThenUserOverride() throws Exception {
+        when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
+                anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n"
+                + "<channel id=\"someId\" name=\"hi\""
+                + " importance=\"3\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O));
+        assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+
+        mHelper.setBubblesAllowed(PKG_O, UID_O, BUBBLE_PREFERENCE_SELECTED);
+        assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
+                mHelper.getAppLockedFields(PKG_O, UID_O));
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
+        loadStreamXml(baos, false, UserHandle.USER_ALL);
+
+        assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
+                mHelper.getAppLockedFields(PKG_O, UID_O));
+    }
+
+    @Test
     public void testBubblePreference_xml() throws Exception {
         mHelper.setBubblesAllowed(PKG_O, UID_O, BUBBLE_PREFERENCE_NONE);
         assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -2435,7 +2509,8 @@
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -2949,7 +3024,8 @@
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
 
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
 
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -2969,7 +3045,8 @@
     public void testPlaceholderConversationId_shortcutRequired() throws Exception {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
 
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -2989,7 +3066,8 @@
     public void testNormalConversationId_shortcutRequired() throws Exception {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
 
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -3009,7 +3087,8 @@
     public void testNoConversationId_shortcutRequired() throws Exception {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager);
 
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index 50fb9b4..f7304bd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -16,7 +16,11 @@
 
 package com.android.server.notification;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -25,6 +29,7 @@
 import android.app.Notification;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -58,6 +63,8 @@
     @Mock
     ShortcutHelper.ShortcutListener mShortcutListener;
     @Mock
+    ShortcutServiceInternal mShortcutServiceInternal;
+    @Mock
     NotificationRecord mNr;
     @Mock
     Notification mNotif;
@@ -72,7 +79,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mShortcutHelper = new ShortcutHelper(mLauncherApps, mShortcutListener);
+        mShortcutHelper = new ShortcutHelper(
+                mLauncherApps, mShortcutListener, mShortcutServiceInternal);
         when(mNr.getKey()).thenReturn(KEY);
         when(mNr.getSbn()).thenReturn(mSbn);
         when(mSbn.getPackageName()).thenReturn(PKG);
@@ -138,4 +146,80 @@
         callback.onShortcutsChanged(PKG, shortcutInfos, mock(UserHandle.class));
         verify(mShortcutListener).onShortcutRemoved(mNr.getKey());
     }
+
+    @Test
+    public void testGetValidShortcutInfo_noMatchingShortcut() {
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
+
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+    }
+
+    @Test
+    public void testGetValidShortcutInfo_nullShortcut() {
+        ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        shortcuts.add(null);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
+
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+    }
+
+    @Test
+    public void testGetValidShortcutInfo_notLongLived() {
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.isLongLived()).thenReturn(false);
+        when(si.isEnabled()).thenReturn(true);
+        ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        shortcuts.add(si);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
+
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+    }
+
+    @Test
+    public void testGetValidShortcutInfo_notSharingShortcut() {
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.isLongLived()).thenReturn(true);
+        when(si.isEnabled()).thenReturn(true);
+        ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        shortcuts.add(si);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(false);
+
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+    }
+
+    @Test
+    public void testGetValidShortcutInfo_notEnabled() {
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.isLongLived()).thenReturn(true);
+        when(si.isEnabled()).thenReturn(false);
+        ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        shortcuts.add(si);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
+
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
+    }
+
+    @Test
+    public void testGetValidShortcutInfo_isValid() {
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.isLongLived()).thenReturn(true);
+        when(si.isEnabled()).thenReturn(true);
+        ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        shortcuts.add(si);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
+        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any())).thenReturn(true);
+
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4532400..17dd26e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -89,13 +89,13 @@
     public void testOnPictureInPictureRequested() throws RemoteException {
         final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
         final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
-        ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
-        doNothing().when(lifecycleManager).scheduleTransaction(any());
+        final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
+        doReturn(mockLifecycleManager).when(mService).getLifecycleManager();
         doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
 
         mService.requestPictureInPictureMode(activity.token);
 
-        verify(lifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
+        verify(mockLifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
         final ClientTransaction transaction = mClientTransactionCaptor.getValue();
         // Check that only an enter pip request item callback was scheduled.
         assertEquals(1, transaction.getCallbacks().size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3fd81b4..daff149 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -65,6 +65,7 @@
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
@@ -84,10 +85,13 @@
 import android.util.DisplayMetrics;
 import android.view.DisplayCutout;
 import android.view.Gravity;
+import android.view.IDisplayWindowInsetsController;
 import android.view.IDisplayWindowRotationCallback;
 import android.view.IDisplayWindowRotationController;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWindowManager;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceControl.Transaction;
@@ -810,25 +814,19 @@
 
     @Test
     public void testComputeImeParent_app() throws Exception {
-        try (final InsetsModeSession session =
-                     new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
-            final DisplayContent dc = createNewDisplay();
-            dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
-            assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
-                    dc.computeImeParent());
-        }
+        final DisplayContent dc = createNewDisplay();
+        dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+        assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
+                dc.computeImeParent());
     }
 
     @Test
     public void testComputeImeParent_app_notFullscreen() throws Exception {
-        try (final InsetsModeSession session =
-                     new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
-            final DisplayContent dc = createNewDisplay();
-            dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
-            dc.mInputMethodTarget.setWindowingMode(
-                    WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-            assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
-        }
+        final DisplayContent dc = createNewDisplay();
+        dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
+        dc.mInputMethodTarget.setWindowingMode(
+                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
     }
 
     @Test
@@ -843,12 +841,61 @@
 
     @Test
     public void testComputeImeParent_noApp() throws Exception {
-        try (final InsetsModeSession session =
-                     new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
-            final DisplayContent dc = createNewDisplay();
-            dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
-            assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
-        }
+        final DisplayContent dc = createNewDisplay();
+        dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+        assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
+    }
+
+    @Test
+    public void testComputeImeControlTarget() throws Exception {
+        final DisplayContent dc = createNewDisplay();
+        dc.setRemoteInsetsController(createDisplayWindowInsetsController());
+        dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+        dc.mInputMethodTarget = dc.mInputMethodInputTarget;
+        assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
+    }
+
+    @Test
+    public void testComputeImeControlTarget_splitscreen() throws Exception {
+        final DisplayContent dc = createNewDisplay();
+        dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+        dc.mInputMethodInputTarget.setWindowingMode(
+                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        dc.mInputMethodTarget = dc.mInputMethodInputTarget;
+        dc.setRemoteInsetsController(createDisplayWindowInsetsController());
+        assertNotEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
+    }
+
+    @Test
+    public void testComputeImeControlTarget_notMatchParentBounds() throws Exception {
+        spyOn(mAppWindow.mActivityRecord);
+        doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
+        mDisplayContent.mInputMethodInputTarget = mAppWindow;
+        mDisplayContent.mInputMethodTarget = mDisplayContent.mInputMethodInputTarget;
+        mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
+        assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
+    }
+
+    private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
+        return new IDisplayWindowInsetsController.Stub() {
+
+            @Override
+            public void insetsChanged(InsetsState insetsState) throws RemoteException {
+            }
+
+            @Override
+            public void insetsControlChanged(InsetsState insetsState,
+                    InsetsSourceControl[] insetsSourceControls) throws RemoteException {
+            }
+
+            @Override
+            public void showInsets(int i, boolean b) throws RemoteException {
+            }
+
+            @Override
+            public void hideInsets(int i, boolean b) throws RemoteException {
+            }
+        };
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 61b74b0..9f28f45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -29,9 +29,11 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.spy;
@@ -44,7 +46,6 @@
 import android.view.InsetsState;
 import android.view.test.InsetsModeSession;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.AfterClass;
@@ -153,22 +154,24 @@
 
     @Test
     public void testStripForDispatch_belowIme() {
+        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+        app.mBehindIme = true;
 
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
-        assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+        getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
     }
 
     @Test
     public void testStripForDispatch_aboveIme() {
-        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        app.mBehindIme = false;
 
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+        getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+        assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
     }
 
     @Test
@@ -188,11 +191,11 @@
         // Adding FLAG_NOT_FOCUSABLE makes app above IME.
         app.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
         mDisplayContent.computeImeTarget(true);
-        mDisplayContent.setLayoutNeeded();
         mDisplayContent.applySurfaceChangesTransaction();
 
-        // app won't get IME insets while above IME.
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+        // app won't get visible IME insets while above IME even when IME is visible.
+        getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+        assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
 
         // Reset invocation counter.
         clearInvocations(app);
@@ -200,49 +203,49 @@
         // Removing FLAG_NOT_FOCUSABLE makes app below IME.
         app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
         mDisplayContent.computeImeTarget(true);
-        mDisplayContent.setLayoutNeeded();
         mDisplayContent.applySurfaceChangesTransaction();
 
         // Make sure app got notified.
         verify(app, atLeast(1)).notifyInsetsChanged();
 
-        // app will get IME insets while below IME.
-        assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+        // app will get visible IME insets while below IME when IME is visible.
+        getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
     }
 
     @Test
     public void testStripForDispatch_childWindow_altFocusable() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
 
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
         child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
 
-        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+        mDisplayContent.computeImeTarget(true);
+        mDisplayContent.setLayoutNeeded();
+        mDisplayContent.applySurfaceChangesTransaction();
 
-        // IME cannot be the IME target.
-        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
-
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
-        assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+        getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+        assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
     }
 
     @Test
     public void testStripForDispatch_childWindow_splitScreen() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
 
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
         child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
         child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
 
-        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+        mDisplayContent.computeImeTarget(true);
+        mDisplayContent.setLayoutNeeded();
+        mDisplayContent.applySurfaceChangesTransaction();
 
-        // IME cannot be the IME target.
-        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
-
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
-        assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+        getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+        assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 1f6ba7a..44ca2cd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -79,7 +79,6 @@
         mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
         doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
                 anyInt(), any(), any(), anyInt(), any(), any());
-        doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
 
         final RecentTasks recentTasks = mService.getRecentTasks();
         spyOn(recentTasks);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index ec77be8..473c1c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -145,9 +145,5 @@
         Rect bounds = new Rect(10, 10, 100, 200);
         task.setBounds(bounds);
         assertEquals(new Point(bounds.left, bounds.top), task.getLastSurfacePosition());
-
-        Rect dispBounds = new Rect(20, 30, 110, 220);
-        task.setOverrideDisplayedBounds(dispBounds);
-        assertEquals(new Point(dispBounds.left, dispBounds.top), task.getLastSurfacePosition());
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 7be05a3..eb2aa41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -310,27 +310,6 @@
         assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
                 resolvedTaskBounds.right - contentInsetRight,
                 resolvedTaskBounds.bottom - contentInsetBottom));
-
-        pf.set(0, 0, logicalWidth, logicalHeight);
-        // If we set displayed bounds, the insets will be computed with the main task bounds
-        // but the frame will be positioned according to the displayed bounds.
-        final int insetLeft = logicalWidth / 5;
-        final int insetTop = logicalHeight / 5;
-        final int insetRight = insetLeft + (resolvedTaskBounds.right - resolvedTaskBounds.left);
-        final int insetBottom = insetTop + (resolvedTaskBounds.bottom - resolvedTaskBounds.top);
-        task.setOverrideDisplayedBounds(resolvedTaskBounds);
-        task.setBounds(insetLeft, insetTop, insetRight, insetBottom);
-        windowFrames.setFrames(pf, pf, cf, cf, pf, cf);
-        w.computeFrameLw();
-        assertEquals(resolvedTaskBounds, w.getFrameLw());
-        assertEquals(0, w.getRelativeFrameLw().left);
-        assertEquals(0, w.getRelativeFrameLw().top);
-        contentInsetRight = insetRight - cfRight;
-        contentInsetBottom = insetBottom - cfBottom;
-        assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
-        assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
-                resolvedTaskBounds.right - contentInsetRight,
-                resolvedTaskBounds.bottom - contentInsetBottom));
     }
 
     @Test
@@ -460,33 +439,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 130388666)
-    public void testDisplayCutout_tempDisplayedBounds() {
-        // Regular fullscreen task and window
-        WindowState w = createWindow();
-        final Task task = w.getTask();
-        task.setBounds(new Rect(0, 0, 1000, 2000));
-        task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
-        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
-
-        final Rect pf = new Rect(0, -500, 1000, 1500);
-        // Create a display cutout of size 50x50, aligned top-center
-        final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
-                pf.width(), pf.height());
-
-        final WindowFrames windowFrames = w.getWindowFrames();
-        windowFrames.setFrames(pf, pf, pf, pf, pf, pf);
-        windowFrames.setDisplayCutout(cutout);
-        w.computeFrameLw();
-
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
-    }
-
-    @Test
     public void testFreeformContentInsets() {
         removeGlobalMinSizeRestriction();
         // fullscreen task doesn't use bounds for computeFrame
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 71b35b6..65fb2c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -657,4 +657,16 @@
         win0.mActivityRecord.getStack().setFocusable(false);
         assertTrue(win0.cantReceiveTouchInput());
     }
+
+    @Test
+    public void testNeedsRelativeLayeringToIme_notAttached() {
+        WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken,
+                "SameTokenWindow");
+        mDisplayContent.mInputMethodTarget = mAppWindow;
+        sameTokenWindow.mActivityRecord.getStack().setWindowingMode(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        assertTrue(sameTokenWindow.needsRelativeLayeringToIme());
+        sameTokenWindow.removeImmediately();
+        assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
+    }
 }
diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
index c35dd3b..5352be6 100644
--- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
+++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
@@ -397,7 +397,7 @@
     public LogcatTimestamp() throws Exception{
       long currentTimeMillis = System.currentTimeMillis();
       epochTime = String.format(
-          "%d.%d", currentTimeMillis/1000, currentTimeMillis%1000);
+          "%d.%03d", currentTimeMillis/1000, currentTimeMillis%1000);
       Log.i(TAG, "Current logcat timestamp is " + epochTime);
     }
 
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a116c07..242c2e9 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -80,7 +80,6 @@
     private final int mMtu;
     private final int mMtuV4;
     private final int mMtuV6;
-    private final int mVersion;
 
     /**
      * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -126,9 +125,7 @@
                 ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
         mPcscfAddresses = (pcscfAddresses == null)
                 ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
-        mMtu = mtu;
-        mMtuV4 = mMtuV6 = 0;
-        mVersion = 0;
+        mMtu = mMtuV4 = mMtuV6 = mtu;
     }
 
     /** @hide */
@@ -136,7 +133,7 @@
             @LinkStatus int linkStatus, @ProtocolType int protocolType,
             @Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
             @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
-            @Nullable List<InetAddress> pcscfAddresses, int mtuV4, int mtuV6, int version) {
+            @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) {
         mCause = cause;
         mSuggestedRetryTime = suggestedRetryTime;
         mId = id;
@@ -151,10 +148,9 @@
                 ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
         mPcscfAddresses = (pcscfAddresses == null)
                 ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
-        mMtu = 0;
+        mMtu = mtu;
         mMtuV4 = mtuV4;
         mMtuV6 = mtuV6;
-        mVersion = version;
     }
 
     /** @hide */
@@ -177,7 +173,6 @@
         mMtu = source.readInt();
         mMtuV4 = source.readInt();
         mMtuV6 = source.readInt();
-        mVersion = source.readInt();
     }
 
     /**
@@ -247,7 +242,7 @@
      */
     @Deprecated
     public int getMtu() {
-        return mVersion < 5 ? mMtu : 0;
+        return mMtu;
     }
 
     /**
@@ -256,7 +251,7 @@
      * Zero or negative values means network has either not sent a value or sent an invalid value.
      */
     public int getMtuV4() {
-        return mVersion < 5 ? 0 : mMtuV4;
+        return mMtuV4;
     }
 
     /**
@@ -264,7 +259,7 @@
      * Zero or negative values means network has either not sent a value or sent an invalid value.
      */
     public int getMtuV6() {
-        return mVersion < 5 ? 0 : mMtuV6;
+        return mMtuV6;
     }
 
     @NonNull
@@ -282,10 +277,9 @@
            .append(" dnses=").append(mDnsAddresses)
            .append(" gateways=").append(mGatewayAddresses)
            .append(" pcscf=").append(mPcscfAddresses)
-           .append(" mtu=").append(mMtu)
-           .append(" mtuV4=").append(mMtuV4)
-           .append(" mtuV6=").append(mMtuV6)
-           .append(" version=").append(mVersion)
+           .append(" mtu=").append(getMtu())
+           .append(" mtuV4=").append(getMtuV4())
+           .append(" mtuV6=").append(getMtuV6())
            .append("}");
         return sb.toString();
     }
@@ -315,15 +309,14 @@
                 && mPcscfAddresses.containsAll(other.mPcscfAddresses)
                 && mMtu == other.mMtu
                 && mMtuV4 == other.mMtuV4
-                && mMtuV6 == other.mMtuV6
-                && mVersion == other.mVersion;
+                && mMtuV6 == other.mMtuV6;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
                 mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
-                mMtu, mMtuV4, mMtuV6, mVersion);
+                mMtu, mMtuV4, mMtuV6);
     }
 
     @Override
@@ -346,7 +339,6 @@
         dest.writeInt(mMtu);
         dest.writeInt(mMtuV4);
         dest.writeInt(mMtuV6);
-        dest.writeInt(mVersion);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -403,8 +395,6 @@
 
         private int mMtuV6;
 
-        private int mVersion;
-
         /**
          * Default constructor for Builder.
          */
@@ -563,29 +553,14 @@
         }
 
         /**
-         * Set the IRadio version for this DataCallResponse
-         * @hide
-         */
-        public @NonNull Builder setVersion(int version) {
-            mVersion = version;
-            return this;
-        }
-
-        /**
          * Build the DataCallResponse.
          *
          * @return the DataCallResponse object.
          */
         public @NonNull DataCallResponse build() {
-            if (mVersion >= 5) {
-                return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
-                        mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
-                        mPcscfAddresses, mMtuV4, mMtuV6, mVersion);
-            } else {
-                return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
-                        mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
-                        mPcscfAddresses, mMtu);
-            }
+            return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
+                    mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
+                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6);
         }
     }
 }
diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp
index f90f26f..75db551 100644
--- a/tests/AppLaunch/Android.bp
+++ b/tests/AppLaunch/Android.bp
@@ -8,6 +8,8 @@
         "android.test.base",
         "android.test.runner",
     ],
-    static_libs: ["androidx.test.rules"],
+    static_libs: [
+        "androidx.test.rules",
+        "ub-uiautomator"],
     test_suites: ["device-tests"],
 }
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2d2f4db..7d750b7 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
  */
 package com.android.tests.applaunch;
 
+import static org.junit.Assert.assertNotNull;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.ActivityManager;
@@ -29,7 +31,9 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
 import android.test.InstrumentationTestRunner;
 import android.util.Log;
@@ -46,6 +50,7 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
+import java.nio.file.Paths;
 import java.time.format.DateTimeFormatter;
 import java.time.ZonedDateTime;
 import java.time.ZoneOffset;
@@ -67,6 +72,7 @@
  * in the following format:
  * -e apps <app name>^<result key>|<app name>^<result key>
  */
+@Deprecated
 public class AppLaunch extends InstrumentationTestCase {
 
     private static final int JOIN_TIMEOUT = 10000;
@@ -94,6 +100,9 @@
     private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
     private static final String KEY_COMPILER_FILTERS = "compiler_filters";
     private static final String KEY_FORCE_STOP_APP = "force_stop_app";
+    private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording";
+    private static final int MAX_RECORDING_PARTS = 5;
+    private static final long VIDEO_TAIL_BUFFER = 500;
 
     private static final String SIMPLEPERF_APP_CMD =
             "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -144,14 +153,17 @@
 
     private Map<String, Intent> mNameToIntent;
     private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
+    private RecordingThread mCurrentThread;
     private Map<String, String> mNameToResultKey;
     private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
     private IActivityManager mAm;
+    private File launchSubDir = null;
     private String mSimplePerfCmd = null;
     private String mLaunchOrder = null;
     private boolean mDropCache = false;
     private int mLaunchIterations = 10;
     private boolean mForceStopApp = true;
+    private boolean mEnableRecording = false;
     private int mTraceLaunchCount = 0;
     private String mTraceDirectoryStr = null;
     private Bundle mResult = new Bundle();
@@ -166,6 +178,7 @@
     private boolean mCycleCleanUp = false;
     private boolean mTraceAll = false;
     private boolean mIterationCycle = false;
+    private UiDevice mDevice;
 
     enum IorapStatus {
         UNDEFINED,
@@ -222,7 +235,7 @@
         }
 
         try {
-            File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+            launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
 
             if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
                 throw new IOException("Unable to create the lauch file sub directory "
@@ -923,9 +936,16 @@
             mLaunchIterations = Integer.parseInt(launchIterations);
         }
         String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+
         if (forceStopApp != null) {
             mForceStopApp = Boolean.parseBoolean(forceStopApp);
         }
+
+        String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);
+
+        if (enableRecording != null) {
+            mEnableRecording = Boolean.parseBoolean(enableRecording);
+        }
         String appList = args.getString(KEY_APPS);
         if (appList == null)
             return;
@@ -1038,6 +1058,9 @@
     private AppLaunchResult startApp(String appName, String launchReason)
             throws NameNotFoundException, RemoteException {
         Log.i(TAG, "Starting " + appName);
+        if(mEnableRecording) {
+            startRecording(appName, launchReason);
+        }
 
         Intent startIntent = mNameToIntent.get(appName);
         if (startIntent == null) {
@@ -1053,6 +1076,10 @@
         } catch (InterruptedException e) {
             // ignore
         }
+
+        if(mEnableRecording) {
+            stopRecording();
+        }
         return runnable.getResult();
     }
 
@@ -1360,4 +1387,126 @@
         }
 
     }
+
+    /**
+     * Start the screen recording while launching the app.
+     *
+     * @param appName
+     * @param launchReason
+     */
+    private void startRecording(String appName, String launchReason) {
+        Log.v(TAG, "Started Recording");
+        mCurrentThread = new RecordingThread("test-screen-record",
+                String.format("%s_%s", appName, launchReason));
+        mCurrentThread.start();
+    }
+
+    /**
+     * Stop already started screen recording.
+     */
+    private void stopRecording() {
+        // Skip if not directory.
+        if (launchSubDir == null) {
+            return;
+        }
+
+        // Add some extra time to the video end.
+        SystemClock.sleep(VIDEO_TAIL_BUFFER);
+        // Ctrl + C all screen record processes.
+        mCurrentThread.cancel();
+        // Wait for the thread to completely die.
+        try {
+            mCurrentThread.join();
+        } catch (InterruptedException ex) {
+            Log.e(TAG, "Interrupted when joining the recording thread.", ex);
+        }
+        Log.v(TAG, "Stopped Recording");
+    }
+
+    /** Returns the recording's name for part {@code part} of launch description. */
+    private File getOutputFile(String description, int part) {
+        // Omit the iteration number for the first iteration.
+        final String fileName =
+                String.format(
+                        "%s-video%s.mp4", description, part == 1 ? "" : part);
+        return Paths.get(launchSubDir.getAbsolutePath(), description).toFile();
+    }
+
+
+    /**
+     * Encapsulates the start and stop screen recording logic.
+     * Copied from ScreenRecordCollector.
+     */
+    private class RecordingThread extends Thread {
+        private final String mDescription;
+        private final List<File> mRecordings;
+
+        private boolean mContinue;
+
+        public RecordingThread(String name, String description) {
+            super(name);
+
+            mContinue = true;
+            mRecordings = new ArrayList<>();
+
+            assertNotNull("No test description provided for recording.", description);
+            mDescription = description;
+        }
+
+        @Override
+        public void run() {
+            try {
+                // Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc.
+                for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) {
+                    File output = getOutputFile(mDescription, i);
+                    Log.d(
+                            TAG,
+                            String.format("Recording screen to %s", output.getAbsolutePath()));
+                    mRecordings.add(output);
+                    // Make sure not to block on this background command in the main thread so
+                    // that the test continues to run, but block in this thread so it does not
+                    // trigger a new screen recording session before the prior one completes.
+                    getDevice().executeShellCommand(
+                                    String.format("screenrecord %s", output.getAbsolutePath()));
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Caught exception while screen recording.");
+            }
+        }
+
+        public void cancel() {
+            mContinue = false;
+
+            // Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each.
+            try {
+                String[] pids = getDevice().executeShellCommand(
+                        "pidof screenrecord").split(" ");
+                for (String pid : pids) {
+                    // Avoid empty process ids, because of weird splitting behavior.
+                    if (pid.isEmpty()) {
+                        continue;
+                    }
+
+                    getDevice().executeShellCommand(
+                            String.format("kill -2 %s", pid));
+                    Log.d(
+                            TAG,
+                            String.format("Sent SIGINT 2 to screenrecord process (%s)", pid));
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Failed to kill screen recording process.");
+            }
+        }
+
+        public List<File> getRecordings() {
+            return mRecordings;
+        }
+    }
+
+    public UiDevice getDevice() {
+        if (mDevice == null) {
+            mDevice = UiDevice.getInstance(getInstrumentation());
+        }
+        return mDevice;
+    }
 }
diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
index f4f610b..fa292bd 100644
--- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
+++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
@@ -100,7 +100,6 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         setInteractive(false);
-        setLowProfile(true);
         setFullscreen(true);
         setContentView(R.layout.dream);
         setScreenBright(false);
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 5fe9498..b339950 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -26,51 +26,14 @@
 namespace stats_log_api_gen {
 
 static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) {
-    fprintf(out, "static int UNSET_VALUE = INT_MAX;\n");
-    fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n");
-
-    fprintf(out, "struct StateAtomFieldOptions {\n");
-    fprintf(out, "  std::vector<int> primaryFields;\n");
-    fprintf(out, "  int exclusiveField;\n");
-    fprintf(out, "  int defaultState = UNSET_VALUE;\n");
-    fprintf(out, "  int resetState = UNSET_VALUE;\n");
-    fprintf(out, "  bool nested;\n");
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
     fprintf(out, "struct AtomsInfo {\n");
-    fprintf(out,
-            "  const static std::set<int> "
-            "kTruncatingTimestampAtomBlackList;\n");
     fprintf(out, "  const static std::set<int> kAtomsWithAttributionChain;\n");
-    fprintf(out,
-            "  const static std::map<int, StateAtomFieldOptions> "
-            "kStateAtomsFieldOptions;\n");
     fprintf(out, "  const static std::set<int> kWhitelistedAtoms;\n");
     fprintf(out, "};\n");
     fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId);
 }
 
 static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
-    std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
-                                             "audio_state_changed",
-                                             "call_state_changed",
-                                             "phone_signal_strength_changed",
-                                             "mobile_bytes_transfer_by_fg_bg",
-                                             "mobile_bytes_transfer"};
-    fprintf(out,
-            "const std::set<int> "
-            "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n");
-    for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
-         atomIt++) {
-        if (kTruncatingAtomNames.find((*atomIt)->name) != kTruncatingAtomNames.end()) {
-            const string constant = make_constant_name((*atomIt)->name);
-            fprintf(out, "    %d, // %s\n", (*atomIt)->code, constant.c_str());
-        }
-    }
-
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
 
     fprintf(out, "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
     for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
@@ -100,49 +63,6 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out,
-            "static std::map<int, StateAtomFieldOptions> "
-            "getStateAtomFieldOptions() {\n");
-    fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
-    fprintf(out, "    StateAtomFieldOptions* opt;\n");
-    for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
-         atomIt++) {
-        if ((*atomIt)->primaryFields.size() == 0 && (*atomIt)->exclusiveField == 0) {
-            continue;
-        }
-        fprintf(out,
-                "\n    // Adding primary and exclusive fields for atom "
-                "(%d)%s\n",
-                (*atomIt)->code, (*atomIt)->name.c_str());
-        fprintf(out, "    opt = &(options[%d /* %s */]);\n", (*atomIt)->code,
-                make_constant_name((*atomIt)->name).c_str());
-        fprintf(out, "    opt->primaryFields.reserve(%lu);\n", (*atomIt)->primaryFields.size());
-        for (const auto& field : (*atomIt)->primaryFields) {
-            fprintf(out, "    opt->primaryFields.push_back(%d);\n", field);
-        }
-
-        fprintf(out, "    opt->exclusiveField = %d;\n", (*atomIt)->exclusiveField);
-        if ((*atomIt)->defaultState != INT_MAX) {
-            fprintf(out, "    opt->defaultState = %d;\n", (*atomIt)->defaultState);
-        } else {
-            fprintf(out, "    opt->defaultState = UNSET_VALUE;\n");
-        }
-
-        if ((*atomIt)->triggerStateReset != INT_MAX) {
-            fprintf(out, "    opt->resetState = %d;\n", (*atomIt)->triggerStateReset);
-        } else {
-            fprintf(out, "    opt->resetState = UNSET_VALUE;\n");
-        }
-        fprintf(out, "    opt->nested = %d;\n", (*atomIt)->nested);
-    }
-
-    fprintf(out, "    return options;\n");
-    fprintf(out, "}\n");
-
-    fprintf(out,
-            "const std::map<int, StateAtomFieldOptions> "
-            "AtomsInfo::kStateAtomsFieldOptions = "
-            "getStateAtomFieldOptions();\n");
 }
 
 int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) {
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index 2ecf309..e55a89f 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -1,14 +1,41 @@
-# used by wifi-service
-# TODO (b/153596226): Find a solution for networkstack's AIDL parcelables & interfaces.
-# Parcelable class names are serialized in the wire, so renaming them
-# will result in the class not being found for any parcelable received/sent from the
-# wifi-service jar.
+## used by service-wifi ##
 
-# Note: This rule is needed to ensure the rule below does not rename a Parcelable (see TODO above).
-rule android.net.DhcpResultsParcelable* @0
+# Network Stack AIDL interface.
+rule android.net.DhcpResultsParcelable* com.android.wifi.x.@0
+rule android.net.IIpMemoryStore* com.android.wifi.x.@0
+rule android.net.IIpMemoryStoreCallbacks* com.android.wifi.x.@0
+rule android.net.INetd* com.android.wifi.x.@0
+rule android.net.INetdUnsolicitedEventListener* com.android.wifi.x.@0
+rule android.net.INetworkStackConnector* com.android.wifi.x.@0
+rule android.net.InformationElementParcelable* com.android.wifi.x.@0
+rule android.net.InitialConfigurationParcelable* com.android.wifi.x.@0
+rule android.net.InterfaceConfigurationParcel* com.android.wifi.x.@0
+rule android.net.Layer2InformationParcelable* com.android.wifi.x.@0
+rule android.net.Layer2PacketParcelable* com.android.wifi.x.@0
+rule android.net.MarkMaskParcel* com.android.wifi.x.@0
+rule android.net.NattKeepalivePacketDataParcelable* com.android.wifi.x.@0
+rule android.net.PrivateDnsConfigParcel* com.android.wifi.x.@0
+rule android.net.ProvisioningConfigurationParcelable* com.android.wifi.x.@0
+rule android.net.ResolverParamsParcel* com.android.wifi.x.@0
+rule android.net.RouteInfoParcel* com.android.wifi.x.@0
+rule android.net.ScanResultInfoParcelable* com.android.wifi.x.@0
+rule android.net.TetherConfigParcel* com.android.wifi.x.@0
+rule android.net.TetherOffloadRuleParcel* com.android.wifi.x.@0
+rule android.net.TetherStatsParcel* com.android.wifi.x.@0
+rule android.net.UidRangeParcel* com.android.wifi.x.@0
+rule android.net.dhcp.DhcpLeaseParcelable* com.android.wifi.x.@0
+rule android.net.dhcp.DhcpServingParamsParcel* com.android.wifi.x.@0
+rule android.net.ip.IIpClient* com.android.wifi.x.@0
+rule android.net.ip.IIpClientCallbacks* com.android.wifi.x.@0
+rule android.net.ipmemorystore.Blob* com.android.wifi.x.@0
+rule android.net.ipmemorystore.IOnBlobRetrievedListener* com.android.wifi.x.@0
+rule android.net.ipmemorystore.IOnStatusListener* com.android.wifi.x.@0
+rule android.net.ipmemorystore.NetworkAttributesParcelable* com.android.wifi.x.@0
+rule android.net.ipmemorystore.SameL3NetworkResponseParcelable* com.android.wifi.x.@0
+rule android.net.ipmemorystore.StatusParcelable* com.android.wifi.x.@0
+
+# Net utils (includes Network Stack helper classes).
 rule android.net.DhcpResults* com.android.wifi.x.@0
-# Note: This rule is needed to ensure the rule below does not rename a Parcelable (see TODO above).
-rule android.net.InterfaceConfigurationParcel* @0
 rule android.net.InterfaceConfiguration* com.android.wifi.x.@0
 rule android.net.IpMemoryStore* com.android.wifi.x.@0
 rule android.net.NetworkMonitorManager* com.android.wifi.x.@0
@@ -19,8 +46,6 @@
 rule android.net.ip.IpClientUtil* com.android.wifi.x.@0
 rule android.net.ipmemorystore.OnBlobRetrievedListener* com.android.wifi.x.@0
 rule android.net.ipmemorystore.OnStatusListener* com.android.wifi.x.@0
-# Note: This rule is needed to ensure the rule below does not rename a Parcelable (see TODO above).
-rule android.net.ipmemorystore.StatusParcelable* @0
 rule android.net.ipmemorystore.Status* com.android.wifi.x.@0
 rule android.net.networkstack.ModuleNetworkStackClient* com.android.wifi.x.@0
 rule android.net.networkstack.NetworkStackClientBase* com.android.wifi.x.@0
@@ -81,7 +106,7 @@
 # Use our statically linked nanohttpd
 rule fi.iki.elonen.** com.android.wifi.x.@0
 
-# used by both framework-wifi and wifi-service
+## used by both framework-wifi and service-wifi ##
 rule android.content.pm.BaseParceledListSlice* com.android.wifi.x.@0
 rule android.content.pm.ParceledListSlice* com.android.wifi.x.@0
 rule android.net.shared.Inet4AddressUtils* com.android.wifi.x.@0