Merge "Move cts tests using reflection" into pi-dev
diff --git a/api/current.txt b/api/current.txt
index 8a64dd0..607109d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38395,17 +38395,17 @@
 
 package android.se.omapi {
 
-  public class Channel {
+  public final class Channel implements java.nio.channels.Channel {
     method public void close();
     method public byte[] getSelectResponse();
     method public android.se.omapi.Session getSession();
     method public boolean isBasicChannel();
-    method public boolean isClosed();
+    method public boolean isOpen();
     method public boolean selectNext() throws java.io.IOException;
     method public byte[] transmit(byte[]) throws java.io.IOException;
   }
 
-  public class Reader {
+  public final class Reader {
     method public void closeSessions();
     method public java.lang.String getName();
     method public android.se.omapi.SEService getSEService();
@@ -38413,26 +38413,28 @@
     method public android.se.omapi.Session openSession() throws java.io.IOException;
   }
 
-  public class SEService {
-    ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener);
+  public final class SEService {
+    ctor public SEService(android.content.Context, java.util.concurrent.Executor, android.se.omapi.SEService.OnConnectedListener);
     method public android.se.omapi.Reader[] getReaders();
     method public java.lang.String getVersion();
     method public boolean isConnected();
     method public void shutdown();
   }
 
-  public static abstract interface SEService.SecureElementListener {
-    method public abstract void onServiceConnected();
+  public static abstract interface SEService.OnConnectedListener {
+    method public abstract void onConnected();
   }
 
-  public class Session {
+  public final class Session {
     method public void close();
     method public void closeChannels();
     method public byte[] getATR();
     method public android.se.omapi.Reader getReader();
     method public boolean isClosed();
     method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
+    method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
     method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
+    method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
   }
 
 }
@@ -40955,7 +40957,7 @@
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
     method public final void removeExtras(java.lang.String...);
-    method public void requestBluetoothAudio(java.lang.String);
+    method public void requestBluetoothAudio(android.bluetooth.BluetoothDevice);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void sendRemoteRttRequest();
     method public final void sendRttInitiationFailure(int);
@@ -41166,7 +41168,7 @@
     method public void onCanAddCallChanged(boolean);
     method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
     method public void onSilenceRinger();
-    method public final void requestBluetoothAudio(java.lang.String);
+    method public final void requestBluetoothAudio(android.bluetooth.BluetoothDevice);
     method public final void setAudioRoute(int);
     method public final void setMuted(boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -43376,6 +43378,7 @@
   public class PrecomputedText implements android.text.Spannable {
     method public char charAt(int);
     method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
+    method public void getBounds(int, int, android.graphics.Rect);
     method public int getParagraphCount();
     method public int getParagraphEnd(int);
     method public int getParagraphStart(int);
@@ -43384,7 +43387,7 @@
     method public int getSpanFlags(java.lang.Object);
     method public int getSpanStart(java.lang.Object);
     method public <T> T[] getSpans(int, int, java.lang.Class<T>);
-    method public java.lang.CharSequence getText();
+    method public float getWidth(int, int);
     method public int length();
     method public int nextSpanTransition(int, int, java.lang.Class);
     method public void removeSpan(java.lang.Object);
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 50b05cd..fae186a 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -366,7 +366,7 @@
     sp<AlarmMonitor> periodicAlarmMonitor;
     sp<StatsLogProcessor> processor = new StatsLogProcessor(
         uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
-    processor->OnConfigUpdated(key, config);
+    processor->OnConfigUpdated(0, key, config);
     return processor;
 }
 
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 13f2679..f00cdb3 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -191,11 +191,12 @@
     }
 }
 
-void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
+void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
+                                        const StatsdConfig& config) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     VLOG("Updated configuration for key %s", key.ToString().c_str());
     sp<MetricsManager> newMetricsManager =
-        new MetricsManager(key, config, mTimeBaseSec, mUidMap,
+        new MetricsManager(key, config, mTimeBaseSec, (timestampNs - 1) / NS_PER_SEC + 1, mUidMap,
                            mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
 
     if (newMetricsManager->isConfigValid()) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 387a929..1c4698f 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -42,7 +42,8 @@
 
     void OnLogEvent(LogEvent* event);
 
-    void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
+    void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
+                         const StatsdConfig& config);
     void OnConfigRemoved(const ConfigKey& key);
 
     size_t GetMetricsSize(const ConfigKey& key) const;
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 249cb59..70e6e84 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -30,7 +30,8 @@
 namespace os {
 namespace statsd {
 
-AlarmTracker::AlarmTracker(uint64_t startMillis,
+AlarmTracker::AlarmTracker(const uint64_t startMillis,
+                           const uint64_t currentMillis,
                            const Alarm& alarm, const ConfigKey& configKey,
                            const sp<AlarmMonitor>& alarmMonitor)
     : mAlarmConfig(alarm),
@@ -38,7 +39,11 @@
       mAlarmMonitor(alarmMonitor) {
     VLOG("AlarmTracker() called");
     mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC;
+    // startMillis is the time statsd is created. We need to find the 1st alarm timestamp after
+    // the config is added to statsd.
+    mAlarmSec = findNextAlarmSec(currentMillis / MS_PER_SEC);  // round up
     mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
+    VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec);
     if (mAlarmMonitor != nullptr) {
         mAlarmMonitor->add(mInternalAlarm);
     }
@@ -55,9 +60,13 @@
     mSubscriptions.push_back(subscription);
 }
 
-uint64_t AlarmTracker::findNextAlarmSec(uint64_t currentTimeSec) {
-    int periodsForward = (currentTimeSec - mAlarmSec) * MS_PER_SEC / mAlarmConfig.period_millis();
-    return mAlarmSec + (periodsForward + 1) * mAlarmConfig.period_millis() / MS_PER_SEC;
+int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) {
+    if (currentTimeSec <= mAlarmSec) {
+        return mAlarmSec;
+    }
+    int64_t periodsForward =
+        ((currentTimeSec - mAlarmSec) * MS_PER_SEC - 1) / mAlarmConfig.period_millis() + 1;
+    return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC;
 }
 
 void AlarmTracker::informAlarmsFired(
@@ -68,12 +77,14 @@
         return;
     }
     if (!mSubscriptions.empty()) {
+        VLOG("AlarmTracker triggers the subscribers.");
         triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey,
                            mSubscriptions);
     }
     firedAlarms.erase(mInternalAlarm);
     mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up
     mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
+    VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec);
     if (mAlarmMonitor != nullptr) {
         mAlarmMonitor->add(mInternalAlarm);
     }
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
index 13180a5..962a014 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.h
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -34,7 +34,8 @@
 
 class AlarmTracker : public virtual RefBase {
 public:
-    AlarmTracker(uint64_t startMillis,
+    AlarmTracker(const uint64_t startMillis,
+                 const uint64_t currentMillis,
                  const Alarm& alarm, const ConfigKey& configKey,
                  const sp<AlarmMonitor>& subscriberAlarmMonitor);
 
@@ -53,13 +54,13 @@
         return mInternalAlarm == nullptr ? 0 : mInternalAlarm->timestampSec;
     }
 
-    uint64_t findNextAlarmSec(uint64_t currentTimeMillis);
+    int64_t findNextAlarmSec(int64_t currentTimeMillis);
 
     // statsd_config.proto Alarm message that defines this tracker.
     const Alarm mAlarmConfig;
 
     // A reference to the Alarm's config key.
-    const ConfigKey& mConfigKey;
+    const ConfigKey mConfigKey;
 
     // The subscriptions that depend on this alarm.
     std::vector<Subscription> mSubscriptions;
@@ -68,7 +69,7 @@
     sp<AlarmMonitor> mAlarmMonitor;
 
     // The current expected alarm time in seconds.
-    uint64_t mAlarmSec;
+    int64_t mAlarmSec;
 
     // The current alarm.
     sp<const InternalAlarm> mInternalAlarm;
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index ae0af64..15671a6 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -127,7 +127,7 @@
     std::vector<Subscription> mSubscriptions;
 
     // A reference to the Alert's config key.
-    const ConfigKey& mConfigKey;
+    const ConfigKey mConfigKey;
 
     // Number of past buckets. One less than the total number of buckets needed
     // for the anomaly detection (since the current bucket is not in the past).
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5bb2015..ab9c7e8 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -108,7 +108,7 @@
         ResourceConfigurationChanged resource_configuration_changed = 66;
         BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
         BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
-        BluetoothA2dpAudioStateChanged bluetooth_a2dp_audio_state_changed = 69;
+        // 69 is blank but need not be.
         UsbConnectorStateChanged usb_connector_state_changed = 70;
         SpeakerImpedanceReported speaker_impedance_reported = 71;
         HardwareFailed hardware_failed = 72;
@@ -1008,22 +1008,6 @@
 }
 
 /**
- * Logs when Bluetooth A2dp audio streaming state changes.
- *
- * Logged from:
- *    TODO(b/73971848)
- */
-message BluetoothA2dpAudioStateChanged {
-    // Whether or not audio is being played using Bluetooth A2dp.
-    enum State {
-        UNKNOWN = 0;
-        PLAY = 1;
-        STOP = 2;
-    }
-    optional State state = 1;
-}
-
-/**
  * Logs when something is plugged into or removed from the USB-C connector.
  *
  * Logged from:
diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h
index 19ccfcf..54e7770 100644
--- a/cmds/statsd/src/config/ConfigListener.h
+++ b/cmds/statsd/src/config/ConfigListener.h
@@ -40,7 +40,8 @@
     /**
      * A configuration was added or updated.
      */
-    virtual void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) = 0;
+    virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
+                                 const StatsdConfig& config) = 0;
 
     /**
      * A configuration was removed.
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index ff25091..f9aaad4 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -21,6 +21,7 @@
 #include "storage/StorageManager.h"
 
 #include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
 #include "stats_util.h"
 
 #include <android-base/file.h>
@@ -109,9 +110,10 @@
         }
     }
 
+    const int64_t timestampNs = getElapsedRealtimeNs();
     // Tell everyone
     for (sp<ConfigListener> listener:broadcastList) {
-        listener->OnConfigUpdated(key, config);
+        listener->OnConfigUpdated(timestampNs, key, config);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 00188e8..c61fa76 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -49,7 +49,7 @@
 const int FIELD_ID_METRICS = 1;
 
 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
-                               const long timeBaseSec,
+                               const long timeBaseSec, const long currentTimeSec,
                                const sp<UidMap> &uidMap,
                                const sp<AlarmMonitor>& anomalyAlarmMonitor,
                                const sp<AlarmMonitor>& periodicAlarmMonitor)
@@ -58,7 +58,7 @@
       mLastReportWallClockNs(getWallClockNs()) {
     mConfigValid =
             initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
-                             timeBaseSec, mTagIds, mAllAtomMatchers,
+                             timeBaseSec, currentTimeSec, mTagIds, mAllAtomMatchers,
                              mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
                              mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
                              mTrackerToConditionMap, mNoReportMetricIds);
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index da0cd4a..4fd64b7 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -36,7 +36,8 @@
 // A MetricsManager is responsible for managing metrics from one single config source.
 class MetricsManager : public PackageInfoListener {
 public:
-    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const long timeBaseSec,
+    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config,
+                   const long timeBaseSec, const long currentTimeSec,
                    const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
                    const sp<AlarmMonitor>& periodicAlarmMonitor);
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 50eca05..b7c5795 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -599,10 +599,11 @@
 
 bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
                 const sp<AlarmMonitor>& periodicAlarmMonitor,
-                const long timeBaseSec,
+                const long timeBaseSec, const long currentTimeSec,
                 vector<sp<AlarmTracker>>& allAlarmTrackers) {
     unordered_map<int64_t, int> alarmTrackerMap;
     uint64_t startMillis = (uint64_t)timeBaseSec * MS_PER_SEC;
+    uint64_t currentTimeMillis = (uint64_t)currentTimeSec * MS_PER_SEC;
     for (int i = 0; i < config.alarm_size(); i++) {
         const Alarm& alarm = config.alarm(i);
         if (alarm.offset_millis() <= 0) {
@@ -615,7 +616,8 @@
         }
         alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
         allAlarmTrackers.push_back(
-            new AlarmTracker(startMillis, alarm, key, periodicAlarmMonitor));
+            new AlarmTracker(startMillis, currentTimeMillis,
+                             alarm, key, periodicAlarmMonitor));
     }
     for (int i = 0; i < config.subscription_size(); ++i) {
         const Subscription& subscription = config.subscription(i);
@@ -644,7 +646,7 @@
                       const UidMap& uidMap,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& periodicAlarmMonitor,
-                      const long timeBaseSec,
+                      const long timeBaseSec, const long currentTimeSec,
                       set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -682,7 +684,8 @@
         ALOGE("initAlerts failed");
         return false;
     }
-    if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseSec, allPeriodicAlarmTrackers)) {
+    if (!initAlarms(config, key, periodicAlarmMonitor,
+                    timeBaseSec, currentTimeSec, allPeriodicAlarmTrackers)) {
         ALOGE("initAlarms failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 386de0b..3754ae0 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -98,7 +98,7 @@
                       const UidMap& uidMap,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& periodicAlarmMonitor,
-                      const long timeBaseSec,
+                      const long timeBaseSec, const long currentTimeSec,
                       std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 838745e..9455304 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -44,7 +44,8 @@
  */
 class MockListener : public ConfigListener {
 public:
-    MOCK_METHOD2(OnConfigUpdated, void(const ConfigKey& key, const StatsdConfig& config));
+    MOCK_METHOD3(OnConfigUpdated, void(const int64_t timestampNs, const ConfigKey& key,
+                                       const StatsdConfig& config));
     MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key));
 };
 
@@ -88,25 +89,25 @@
         manager->StartupForTest();
 
         // Add another one
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")),
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")),
             StatsdConfigEq(91)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91);
 
         // Update It
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")),
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")),
             StatsdConfigEq(92)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92);
 
         // Add one with the same uid but a different name
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("yyy")),
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")),
             StatsdConfigEq(93)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93);
 
         // Add one with the same name but a different uid
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, StringToId("zzz")),
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")),
             StatsdConfigEq(94)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94);
@@ -142,7 +143,7 @@
 
     StatsdConfig config;
 
-    EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _)).Times(5);
+    EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _, _)).Times(5);
     EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx"))));
     EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy"))));
     EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz"))));
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 5d8c3f7..07378db 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -287,7 +287,7 @@
 
     EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap,
                                  anomalyAlarmMonitor, periodicAlarmMonitor,
-                                 timeBaseSec, allTagIds, allAtomMatchers,
+                                 timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                  allAlarmTrackers,
                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
@@ -315,7 +315,7 @@
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
                                   anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, allTagIds, allAtomMatchers,
+                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
@@ -340,7 +340,7 @@
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
                                   anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, allTagIds, allAtomMatchers,
+                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
@@ -364,7 +364,7 @@
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
                                   anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, allTagIds, allAtomMatchers,
+                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
@@ -388,7 +388,7 @@
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
                                   anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, allTagIds, allAtomMatchers,
+                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
@@ -413,7 +413,7 @@
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
                                   anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, allTagIds, allAtomMatchers,
+                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
@@ -438,7 +438,7 @@
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
                                   anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, allTagIds, allAtomMatchers,
+                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 4b9a87d..5a8ba79 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -43,7 +43,7 @@
 class MockMetricsManager : public MetricsManager {
 public:
     MockMetricsManager() : MetricsManager(
-        ConfigKey(1, 12345), StatsdConfig(), 1000,
+        ConfigKey(1, 12345), StatsdConfig(), 1000, 1000,
         new UidMap(),
         new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
                          [](const sp<IStatsCompanionService>&){}),
@@ -135,7 +135,7 @@
     ConfigKey key(3, 4);
     StatsdConfig config;
     config.add_allowed_log_source("AID_ROOT");
-    p.OnConfigUpdated(key, config);
+    p.OnConfigUpdated(0, key, config);
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index 3330ee9..5e6de1c 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -39,25 +39,24 @@
     Alarm alarm;
     alarm.set_offset_millis(15 * MS_PER_SEC);
     alarm.set_period_millis(60 * 60 * MS_PER_SEC);  // 1hr
-    uint64_t startMillis = 100000000 * MS_PER_SEC;
-    AlarmTracker tracker(startMillis, alarm, kConfigKey,
-                         subscriberAlarmMonitor);
+    int64_t startMillis = 100000000 * MS_PER_SEC;
+    AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor);
 
-    EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15);
+    EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
 
     uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10;
     std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet =
         subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
     EXPECT_TRUE(firedAlarmSet.empty());
     tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
-    EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15);
+    EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
 
     currentTimeSec = startMillis / MS_PER_SEC + 7000;
     firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
     EXPECT_EQ(firedAlarmSet.size(), 1u);
     tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
     EXPECT_TRUE(firedAlarmSet.empty());
-    EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15 + 2 * 60 * 60);
+    EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15 + 2 * 60 * 60));
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index d0840f0..0acd297 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -455,7 +455,7 @@
                 [](const sp<IStatsCompanionService>&){});
     sp<StatsLogProcessor> processor = new StatsLogProcessor(
         uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
-    processor->OnConfigUpdated(key, config);
+    processor->OnConfigUpdated(timeBaseSec, key, config);
     return processor;
 }
 
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 61ae6e7..3a3a708 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -20,7 +20,7 @@
 Landroid/app/AppOpsManager$PackageOps;->getPackageName()Ljava/lang/String;
 Landroid/app/AppOpsManager$PackageOps;->getUid()I
 Landroid/app/IActivityController$Stub;-><init>()V
-Landroid/app/IActivityManager;->cancelRecentsAnimation()V
+Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
 Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
 Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
 Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e1a02fa..919f714 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -450,7 +450,7 @@
             in Intent intent, in String resolvedType, in Bundle options, int userId);
     void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
             in IRecentsAnimationRunner recentsAnimationRunner);
-    void cancelRecentsAnimation();
+    void cancelRecentsAnimation(boolean restoreHomeStackPosition);
     int startActivityFromRecents(int taskId, in Bundle options);
     Bundle getActivityOptions(in IBinder token);
     List<IBinder> getAppTasks(in String callingPackage);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ddd0656..5067e19 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -73,6 +73,7 @@
     ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
     int getDeletedChannelCount(String pkg, int uid);
+    int getBlockedChannelCount(String pkg, int uid);
     void deleteNotificationChannelGroup(String pkg, String channelGroupId);
     NotificationChannelGroup getNotificationChannelGroup(String pkg, String channelGroupId);
     ParceledListSlice getNotificationChannelGroups(String pkg);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1776eac..95e4f93 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -279,12 +279,12 @@
             }});
 
         registerService(Context.IPSEC_SERVICE, IpSecManager.class,
-                new StaticServiceFetcher<IpSecManager>() {
+                new CachedServiceFetcher<IpSecManager>() {
             @Override
-            public IpSecManager createService() {
+            public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE);
                 IIpSecService service = IIpSecService.Stub.asInterface(b);
-                return new IpSecManager(service);
+                return new IpSecManager(ctx, service);
             }});
 
         registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4124536..7ebe0f9 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -43,6 +43,9 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.RejectedExecutionException;
@@ -924,6 +927,37 @@
                     idCount++;
                 }
             }
+
+            // The sort logic must match the logic in
+            // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
+            Arrays.sort(cameraIds, new Comparator<String>() {
+                    @Override
+                    public int compare(String s1, String s2) {
+                        int s1Int = 0, s2Int = 0;
+                        try {
+                            s1Int = Integer.parseInt(s1);
+                        } catch (NumberFormatException e) {
+                            s1Int = -1;
+                        }
+
+                        try {
+                            s2Int = Integer.parseInt(s2);
+                        } catch (NumberFormatException e) {
+                            s2Int = -1;
+                        }
+
+                        // Uint device IDs first
+                        if (s1Int >= 0 && s2Int >= 0) {
+                            return s1Int - s2Int;
+                        } else if (s1Int >= 0) {
+                            return -1;
+                        } else if (s2Int >= 0) {
+                            return 1;
+                        } else {
+                            // Simple string compare if both id are not uint
+                            return s1.compareTo(s2);
+                        }
+                    }});
             return cameraIds;
         }
 
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 3a3ddcc..d6774d4 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -45,25 +45,31 @@
             in String localAddr,
             in String remoteAddr,
             in Network underlyingNetwork,
-            in IBinder binder);
+            in IBinder binder,
+            in String callingPackage);
 
     void addAddressToTunnelInterface(
             int tunnelResourceId,
-            in LinkAddress localAddr);
+            in LinkAddress localAddr,
+            in String callingPackage);
 
     void removeAddressFromTunnelInterface(
             int tunnelResourceId,
-            in LinkAddress localAddr);
+            in LinkAddress localAddr,
+            in String callingPackage);
 
-    void deleteTunnelInterface(int resourceId);
+    void deleteTunnelInterface(int resourceId, in String callingPackage);
 
-    IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder);
+    IpSecTransformResponse createTransform(
+            in IpSecConfig c, in IBinder binder, in String callingPackage);
 
     void deleteTransform(int transformId);
 
-    void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
+    void applyTransportModeTransform(
+            in ParcelFileDescriptor socket, int direction, int transformId);
 
-    void applyTunnelModeTransform(int tunnelResourceId, int direction, int transformResourceId);
+    void applyTunnelModeTransform(
+            int tunnelResourceId, int direction, int transformResourceId, in String callingPackage);
 
     void removeTransportModeTransforms(in ParcelFileDescriptor socket);
 }
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 1525508..e0654fd 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -140,6 +140,7 @@
         }
     }
 
+    private final Context mContext;
     private final IIpSecService mService;
 
     /**
@@ -661,6 +662,7 @@
      */
     @SystemApi
     public static final class IpSecTunnelInterface implements AutoCloseable {
+        private final String mOpPackageName;
         private final IIpSecService mService;
         private final InetAddress mRemoteAddress;
         private final InetAddress mLocalAddress;
@@ -688,7 +690,8 @@
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
         public void addAddress(@NonNull LinkAddress address) throws IOException {
             try {
-                mService.addAddressToTunnelInterface(mResourceId, address);
+                mService.addAddressToTunnelInterface(
+                        mResourceId, address, mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -706,16 +709,18 @@
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
         public void removeAddress(@NonNull LinkAddress address) throws IOException {
             try {
-                mService.removeAddressFromTunnelInterface(mResourceId, address);
+                mService.removeAddressFromTunnelInterface(
+                        mResourceId, address, mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
 
-        private IpSecTunnelInterface(@NonNull IIpSecService service,
+        private IpSecTunnelInterface(@NonNull Context ctx, @NonNull IIpSecService service,
                 @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
                 @NonNull Network underlyingNetwork)
                 throws ResourceUnavailableException, IOException {
+            mOpPackageName = ctx.getOpPackageName();
             mService = service;
             mLocalAddress = localAddress;
             mRemoteAddress = remoteAddress;
@@ -727,7 +732,8 @@
                                 localAddress.getHostAddress(),
                                 remoteAddress.getHostAddress(),
                                 underlyingNetwork,
-                                new Binder());
+                                new Binder(),
+                                mOpPackageName);
                 switch (result.status) {
                     case Status.OK:
                         break;
@@ -756,7 +762,7 @@
         @Override
         public void close() {
             try {
-                mService.deleteTunnelInterface(mResourceId);
+                mService.deleteTunnelInterface(mResourceId, mOpPackageName);
                 mResourceId = INVALID_RESOURCE_ID;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -801,7 +807,8 @@
     public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
             @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
             throws ResourceUnavailableException, IOException {
-        return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+        return new IpSecTunnelInterface(
+                mContext, mService, localAddress, remoteAddress, underlyingNetwork);
     }
 
     /**
@@ -827,7 +834,8 @@
             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         try {
             mService.applyTunnelModeTransform(
-                    tunnel.getResourceId(), direction, transform.getResourceId());
+                    tunnel.getResourceId(), direction,
+                    transform.getResourceId(), mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -839,7 +847,8 @@
      * @param context the application context for this manager
      * @hide
      */
-    public IpSecManager(IIpSecService service) {
+    public IpSecManager(Context ctx, IIpSecService service) {
+        mContext = ctx;
         mService = checkNotNull(service, "missing service");
     }
 }
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 099fe02..fb5f46c 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -130,7 +130,8 @@
         synchronized (this) {
             try {
                 IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
+                IpSecTransformResponse result = svc.createTransform(
+                        mConfig, new Binder(), mContext.getOpPackageName());
                 int status = result.status;
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index c8efede..5db3c1a 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Channel {
+public final class Channel implements java.nio.channels.Channel {
 
     private static final String TAG = "OMAPI.Channel";
     private Session mSession;
@@ -64,7 +64,7 @@
      * before closing the channel.
      */
     public void close() {
-        if (!isClosed()) {
+        if (isOpen()) {
             synchronized (mLock) {
                 try {
                     mChannel.close();
@@ -76,21 +76,21 @@
     }
 
     /**
-     * Tells if this channel is closed.
+     * Tells if this channel is open.
      *
-     * @return <code>true</code> if the channel is closed or in case of an error.
-     *         <code>false</code> otherwise.
+     * @return <code>false</code> if the channel is closed or in case of an error.
+     *         <code>true</code> otherwise.
      */
-    public boolean isClosed() {
+    public boolean isOpen() {
         if (!mService.isConnected()) {
             Log.e(TAG, "service not connected to system");
-            return true;
+            return false;
         }
         try {
-            return mChannel.isClosed();
+            return !mChannel.isClosed();
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in isClosed()");
-            return true;
+            return false;
         }
     }
 
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 9be3da6..80262f7 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -37,7 +37,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Reader {
+public final class Reader {
 
     private static final String TAG = "OMAPI.Reader";
     private final String mName;
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index 311dc4c..14727f0 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.concurrent.Executor;
 
 /**
  * The SEService realises the communication to available Secure Elements on the
@@ -40,7 +41,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class SEService {
+public final class SEService {
 
     /**
      * Error code used with ServiceSpecificException.
@@ -62,11 +63,11 @@
     /**
      * Interface to send call-backs to the application when the service is connected.
      */
-    public interface SecureElementListener {
+    public interface OnConnectedListener {
         /**
          * Called by the framework when the service is connected.
          */
-        void onServiceConnected();
+        void onConnected();
     }
 
     /**
@@ -74,16 +75,22 @@
      * SEService could be bound to the backend.
      */
     private class SEListener extends ISecureElementListener.Stub {
-        public SecureElementListener mListener = null;
+        public OnConnectedListener mListener = null;
+        public Executor mExecutor = null;
 
         @Override
         public IBinder asBinder() {
             return this;
         }
 
-        public void onServiceConnected() {
-            if (mListener != null) {
-                mListener.onServiceConnected();
+        public void onConnected() {
+            if (mListener != null && mExecutor != null) {
+                mExecutor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        mListener.onConnected();
+                    }
+                });
             }
         }
     }
@@ -116,22 +123,26 @@
      * the specified listener is called or if isConnected() returns
      * <code>true</code>. <br>
      * The call-back object passed as a parameter will have its
-     * onServiceConnected() method called when the connection actually happen.
+     * onConnected() method called when the connection actually happen.
      *
      * @param context
      *            the context of the calling application. Cannot be
      *            <code>null</code>.
      * @param listener
-     *            a SecureElementListener object.
+     *            a OnConnectedListener object.
+     * @param executor
+     *            an Executor which will be used when invoking the callback.
      */
-    public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
+    public SEService(@NonNull Context context, @NonNull Executor executor,
+            @NonNull OnConnectedListener listener) {
 
-        if (context == null) {
-            throw new NullPointerException("context must not be null");
+        if (context == null || listener == null || executor == null) {
+            throw new NullPointerException("Arguments must not be null");
         }
 
         mContext = context;
         mSEListener.mListener = listener;
+        mSEListener.mExecutor = executor;
 
         mConnection = new ServiceConnection() {
 
@@ -140,7 +151,7 @@
 
                 mSecureElementService = ISecureElementService.Stub.asInterface(service);
                 if (mSEListener != null) {
-                    mSEListener.onServiceConnected();
+                    mSEListener.onConnected();
                 }
                 Log.i(TAG, "Service onServiceConnected");
             }
@@ -171,12 +182,12 @@
     }
 
     /**
-     * Returns the list of available Secure Element readers.
+     * Returns an array of available Secure Element readers.
      * There must be no duplicated objects in the returned list.
      * All available readers shall be listed even if no card is inserted.
      *
-     * @return The readers list, as an array of Readers. If there are no
-     * readers the returned array is of length 0.
+     * @return An array of Readers. If there are no readers the returned array
+     * is of length 0.
      */
     public @NonNull Reader[] getReaders() {
         if (mSecureElementService == null) {
@@ -212,7 +223,8 @@
      * (including any binding to an underlying service).
      * As a result isConnected() will return false after shutdown() was called.
      * After this method call, the SEService object is not connected.
-     * It is recommended to call this method in the termination method of the calling application
+     * This method should be called when connection to the Secure Element is not needed
+     * or in the termination method of the calling application
      * (or part of this application) which is bound to this SEService.
      */
     public void shutdown() {
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index adfeddd..d5f8c82 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class Session {
+public final class Session {
 
     private final Object mLock = new Object();
     private final SEService mService;
@@ -225,6 +225,32 @@
     }
 
     /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openBasicChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array, or null if no Applet is to be selected.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element session is used after
+     *             being closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+     *             selected.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device
+     * @return an instance of Channel if available or null.
+     */
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid) throws IOException {
+        return openBasicChannel(aid, (byte) 0x00);
+    }
+
+    /**
      * Open a logical channel with the Secure Element, selecting the Applet represented by
      * the given AID. If the AID is null, which means no Applet is to be selected on this
      * channel, the default Applet is used. It's up to the Secure Element to choose which
@@ -304,4 +330,32 @@
             }
         }
     }
+
+    /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openLogicalChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element is used after being
+     *             closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not
+     *             available or cannot be selected or a logical channel is already
+     *             open to a non-multiselectable Applet.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device.
+     * @return an instance of Channel. Null if the Secure Element is unable to
+     *         provide a new logical channel.
+     */
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid) throws IOException {
+        return openLogicalChannel(aid, (byte) 0x00);
+    }
 }
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 413df05..44789d6 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.FloatRange;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,13 +45,17 @@
  * <pre>
  * An example usage is:
  * <code>
- *  void asyncSetText(final TextView textView, final String longString, Handler bgThreadHandler) {
+ *  static void asyncSetText(TextView textView, final String longString, Executor bgExecutor) {
  *      // construct precompute related parameters using the TextView that we will set the text on.
- *      final PrecomputedText.Params params = textView.getTextParams();
- *      bgThreadHandler.post(() -> {
- *          final PrecomputedText precomputedText =
- *                  PrecomputedText.create(expensiveLongString, params);
+ *      final PrecomputedText.Params params = textView.getTextMetricsParams();
+ *      final Reference textViewRef = new WeakReference<>(textView);
+ *      bgExecutor.submit(() -> {
+ *          TextView textView = textViewRef.get();
+ *          if (textView == null) return;
+ *          final PrecomputedText precomputedText = PrecomputedText.create(longString, params);
  *          textView.post(() -> {
+ *              TextView textView = textViewRef.get();
+ *              if (textView == null) return;
  *              textView.setText(precomputedText);
  *          });
  *      });
@@ -363,6 +368,7 @@
 
     /**
      * Return the underlying text.
+     * @hide
      */
     public @NonNull CharSequence getText() {
         return mText;
@@ -451,27 +457,61 @@
             + ", gave " + pos);
     }
 
-    /** @hide */
-    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+    /**
+     * Returns text width for the given range.
+     * Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise
+     * IllegalArgumentException will be thrown.
+     *
+     * @param start the inclusive start offset in the text
+     * @param end the exclusive end offset in the text
+     * @return the text width
+     * @throws IllegalArgumentException if start and end offset are in the different paragraph.
+     */
+    public @FloatRange(from = 0) float getWidth(@IntRange(from = 0) int start,
+            @IntRange(from = 0) int end) {
+        Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset");
+        Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset");
+        Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset");
+
+        if (start == end) {
+            return 0;
+        }
         final int paraIndex = findParaIndex(start);
         final int paraStart = getParagraphStart(paraIndex);
         final int paraEnd = getParagraphEnd(paraIndex);
         if (start < paraStart || paraEnd < end) {
-            throw new RuntimeException("Cannot measured across the paragraph:"
+            throw new IllegalArgumentException("Cannot measured across the paragraph:"
                 + "para: (" + paraStart + ", " + paraEnd + "), "
                 + "request: (" + start + ", " + end + ")");
         }
         return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
     }
 
-    /** @hide */
+    /**
+     * Retrieves the text bounding box for the given range.
+     * Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise
+     * IllegalArgumentException will be thrown.
+     *
+     * @param start the inclusive start offset in the text
+     * @param end the exclusive end offset in the text
+     * @param bounds the output rectangle
+     * @throws IllegalArgumentException if start and end offset are in the different paragraph.
+     */
     public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull Rect bounds) {
+        Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset");
+        Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset");
+        Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset");
+        Preconditions.checkNotNull(bounds);
+        if (start == end) {
+            bounds.set(0, 0, 0, 0);
+            return;
+        }
         final int paraIndex = findParaIndex(start);
         final int paraStart = getParagraphStart(paraIndex);
         final int paraEnd = getParagraphEnd(paraIndex);
         if (start < paraStart || paraEnd < end) {
-            throw new RuntimeException("Cannot measured across the paragraph:"
+            throw new IllegalArgumentException("Cannot measured across the paragraph:"
                 + "para: (" + paraStart + ", " + paraEnd + "), "
                 + "request: (" + start + ", " + end + ")");
         }
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 89684ca..c0b40c8 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -59,4 +59,9 @@
     * taken.
     */
     void setAnimationTargetsBehindSystemBars(boolean behindSystemBars);
+
+    /**
+     * Informs the system that the primary split-screen stack should be minimized.
+     */
+    void setSplitScreenMinimized(boolean minimized);
 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 03dd77f..514ff76 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -93,7 +93,6 @@
     void noteVibratorOff(int uid);
     void noteGpsChanged(in WorkSource oldSource, in WorkSource newSource);
     void noteGpsSignalQuality(int signalLevel);
-    void noteUsbConnectionState(boolean connected);
     void noteScreenState(int state);
     void noteScreenBrightness(int brightness);
     void noteUserActivity(int uid, int event);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 89f6156..5da3874 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -21,10 +21,13 @@
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.net.Uri;
@@ -766,7 +769,10 @@
     int mCameraOnNesting;
     StopwatchTimer mCameraOnTimer;
 
-    int mUsbDataState; // 0: unknown, 1: disconnected, 2: connected
+    private static final int USB_DATA_UNKNOWN = 0;
+    private static final int USB_DATA_DISCONNECTED = 1;
+    private static final int USB_DATA_CONNECTED = 2;
+    int mUsbDataState = USB_DATA_UNKNOWN;
 
     int mGpsSignalQualityBin = -1;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -5241,8 +5247,30 @@
         }
     }
 
-    public void noteUsbConnectionStateLocked(boolean connected) {
-        int newState = connected ? 2 : 1;
+    private void registerUsbStateReceiver(Context context) {
+        final IntentFilter usbStateFilter = new IntentFilter();
+        usbStateFilter.addAction(UsbManager.ACTION_USB_STATE);
+        context.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final boolean state = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+                synchronized (BatteryStatsImpl.this) {
+                    noteUsbConnectionStateLocked(state);
+                }
+            }
+        }, usbStateFilter);
+        synchronized (this) {
+            if (mUsbDataState == USB_DATA_UNKNOWN) {
+                final Intent usbState = context.registerReceiver(null, usbStateFilter);
+                final boolean initState = usbState != null && usbState.getBooleanExtra(
+                        UsbManager.USB_CONNECTED, false);
+                noteUsbConnectionStateLocked(initState);
+            }
+        }
+    }
+
+    private void noteUsbConnectionStateLocked(boolean connected) {
+        int newState = connected ? USB_DATA_CONNECTED : USB_DATA_DISCONNECTED;
         if (mUsbDataState != newState) {
             mUsbDataState = newState;
             if (connected) {
@@ -13218,6 +13246,7 @@
 
     public void systemServicesReady(Context context) {
         mConstants.startObserving(context.getContentResolver());
+        registerUsbStateReceiver(context);
     }
 
     @VisibleForTesting
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
new file mode 100644
index 0000000..ed42e2e
--- /dev/null
+++ b/core/proto/OWNERS
@@ -0,0 +1,13 @@
+# Be sure you are familiar with proto when you modify this directory.
+
+# Metrics
+bookatz@google.com
+cjyu@google.com
+jinyithu@google.com
+joeo@google.com
+kwekua@google.com
+singhtejinder@google.com
+
+# Frameworks
+ogunwale@google.com
+jjaggi@google.com
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f4715fc..b7b5f23 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4248,14 +4248,9 @@
                   android:exported="false">
         </receiver>
 
-        <receiver android:name="com.android.server.am.BatteryStatsService$UsbConnectionReceiver"
+        <receiver android:name="com.android.server.stats.StatsCompanionService$PeriodicAlarmReceiver"
+                  android:permission="android.permission.STATSCOMPANION"
                   android:exported="false">
-            <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.hardware.usb.action.USB_STATE" />
-            </intent-filter>
         </receiver>
 
         <service android:name="android.hardware.location.GeofenceHardwareService"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eebf581..02d6c9f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1205,7 +1205,6 @@
   <java-symbol type="string" name="ssl_ca_cert_noti_by_administrator" />
   <java-symbol type="string" name="ssl_ca_cert_noti_managed" />
   <java-symbol type="string" name="work_profile_deleted" />
-  <java-symbol type="string" name="work_profile_deleted_description" />
   <java-symbol type="string" name="work_profile_deleted_details" />
   <java-symbol type="string" name="work_profile_deleted_description_dpm_wipe" />
   <java-symbol type="string" name="work_profile_deleted_reason_maximum_password_failure" />
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index a4a1b94..95aff9a 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -55,6 +55,7 @@
   <hidden-api-whitelisted-app package="com.android.companiondevicemanager" />
   <hidden-api-whitelisted-app package="com.android.customlocale2" />
   <hidden-api-whitelisted-app package="com.android.defcontainer" />
+  <hidden-api-whitelisted-app package="com.android.development" />
   <hidden-api-whitelisted-app package="com.android.documentsui" />
   <hidden-api-whitelisted-app package="com.android.dreams.basic" />
   <hidden-api-whitelisted-app package="com.android.egg" />
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 32d4c77..3ca9295 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -151,7 +151,12 @@
             mGrContext->freeGpuResources();
             break;
         case TrimMemoryMode::UiHidden:
-            mGrContext->purgeUnlockedResources(mMaxResourceBytes - mBackgroundResourceBytes, true);
+            // Here we purge all the unlocked scratch resources and then toggle the resources cache
+            // limits between the background and max amounts. This causes the unlocked resources
+            // that have persistent data to be purged in LRU order.
+            mGrContext->purgeUnlockedResources(true);
+            mGrContext->setResourceCacheLimits(mMaxResources, mBackgroundResourceBytes);
+            mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
             break;
     }
 }
@@ -161,7 +166,10 @@
         return;
     }
     mGrContext->flush();
-    mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
+    // Here we purge all the unlocked scratch resources (leaving those resources w/ persistent data)
+    // and then purge those w/ persistent data based on age.
+    mGrContext->purgeUnlockedResources(true);
+    mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(10));
 }
 
 sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() {
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index b1106f0..c235715 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -20,6 +20,8 @@
 #include "renderthread/EglManager.h"
 #include "tests/common/TestUtils.h"
 
+#include <SkImagePriv.h>
+
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
@@ -49,7 +51,12 @@
         surfaces.push_back(surface);
     }
 
-    ASSERT_TRUE(1 < surfaces.size());
+    // create an image and pin it so that we have something with a unique key in the cache
+    sk_sp<Bitmap> bitmap =
+            Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h));
+    sk_sp<SkColorFilter> filter;
+    sk_sp<SkImage> image = bitmap->makeImage(&filter);
+    ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
 
     // attempt to trim all memory while we still hold strong refs
     renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
@@ -61,11 +68,14 @@
         surfaces[i].reset();
     }
 
+    // unpin the image which should add a unique purgeable key to the cache
+    SkImage_unpinAsTexture(image.get(), grContext);
+
     // verify that we have enough purgeable bytes
     const size_t purgeableBytes = grContext->getResourceCachePurgeableBytes();
     ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
 
-    // UI hidden and make sure only some got purged
+    // UI hidden and make sure only some got purged (unique should remain)
     renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
     ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
     ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index fa3f99a..1276881 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -89,6 +89,10 @@
     ProviderProperties getProviderProperties(String provider);
     String getNetworkProviderPackage();
 
+    boolean isProviderEnabledForUser(String provider, int userId);
+    boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
+    boolean isLocationEnabledForUser(int userId);
+    void setLocationEnabledForUser(boolean enabled, int userId);
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
     void setTestProviderLocation(String provider, in Location loc, String opPackageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index a523958..38286a3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1249,40 +1249,11 @@
     @SystemApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
-        final List<String> allProvidersList = getAllProviders();
-        // Update all providers on device plus gps and network provider when disabling location.
-        Set<String> allProvidersSet = new ArraySet<>(allProvidersList.size() + 2);
-        allProvidersSet.addAll(allProvidersList);
-        // When disabling location, disable gps and network provider that could have been enabled by
-        // location mode api.
-        if (enabled == false) {
-            allProvidersSet.add(GPS_PROVIDER);
-            allProvidersSet.add(NETWORK_PROVIDER);
+        try {
+            mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        if (allProvidersSet.isEmpty()) {
-            return;
-        }
-        // to ensure thread safety, we write the provider name with a '+' or '-'
-        // and let the SettingsProvider handle it rather than reading and modifying
-        // the list of enabled providers.
-        final String prefix = enabled ? "+" : "-";
-        StringBuilder locationProvidersAllowed = new StringBuilder();
-        for (String provider : allProvidersSet) {
-            checkProvider(provider);
-            if (provider.equals(PASSIVE_PROVIDER)) {
-                continue;
-            }
-            locationProvidersAllowed.append(prefix);
-            locationProvidersAllowed.append(provider);
-            locationProvidersAllowed.append(",");
-        }
-        // Remove the trailing comma
-        locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
-        Settings.Secure.putStringForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                locationProvidersAllowed.toString(),
-                userHandle.getIdentifier());
     }
 
     /**
@@ -1295,22 +1266,11 @@
      */
     @SystemApi
     public boolean isLocationEnabledForUser(UserHandle userHandle) {
-        final String allowedProviders = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                userHandle.getIdentifier());
-        if (allowedProviders == null) {
-            return false;
+        try {
+            return mService.isLocationEnabledForUser(userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-        for(String provider : getAllProviders()) {
-            if (provider.equals(PASSIVE_PROVIDER)) {
-                continue;
-            }
-            if (providerList.contains(provider)) {
-                return true;
-            }
-        }
-        return false;
     }
 
     /**
@@ -1362,9 +1322,12 @@
     @SystemApi
     public boolean isProviderEnabledForUser(String provider, UserHandle userHandle) {
         checkProvider(provider);
-        String allowedProviders = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userHandle.getIdentifier());
-        return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+
+        try {
+            return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -1383,16 +1346,13 @@
     public boolean setProviderEnabledForUser(
             String provider, boolean enabled, UserHandle userHandle) {
         checkProvider(provider);
-        // to ensure thread safety, we write the provider name with a '+' or '-'
-        // and let the SettingsProvider handle it rather than reading and modifying
-        // the list of enabled providers.
-        if (enabled) {
-            provider = "+" + provider;
-        } else {
-            provider = "-" + provider;
+
+        try {
+            return mService.setProviderEnabledForUser(
+                    provider, enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        return Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider, userHandle.getIdentifier());
     }
 
     /**
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml b/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml
index 6f153c1..78304fd 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml
@@ -41,16 +41,4 @@
 
     </FrameLayout>
 
-    <com.android.systemui.statusbar.policy.DeadZone
-        android:id="@+id/deadzone"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:layout_gravity="top"
-        systemui:minSize="@dimen/navigation_bar_deadzone_size"
-        systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
-        systemui:holdTime="@integer/navigation_bar_deadzone_hold"
-        systemui:decayTime="@integer/navigation_bar_deadzone_decay"
-        systemui:orientation="horizontal"
-        />
-
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml
index 3e60794..953abc7 100644
--- a/packages/SystemUI/res/layout/navigation_layout.xml
+++ b/packages/SystemUI/res/layout/navigation_layout.xml
@@ -46,16 +46,4 @@
 
     </com.android.systemui.statusbar.phone.NearestTouchFrame>
 
-    <com.android.systemui.statusbar.policy.DeadZone
-        android:id="@+id/deadzone"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:layout_gravity="top"
-        systemui:minSize="@dimen/navigation_bar_deadzone_size"
-        systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
-        systemui:holdTime="@integer/navigation_bar_deadzone_hold"
-        systemui:decayTime="@integer/navigation_bar_deadzone_decay"
-        systemui:orientation="horizontal"
-        />
-
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/navigation_layout_rot90.xml b/packages/SystemUI/res/layout/navigation_layout_rot90.xml
index 39cdff4..0e17e5b5 100644
--- a/packages/SystemUI/res/layout/navigation_layout_rot90.xml
+++ b/packages/SystemUI/res/layout/navigation_layout_rot90.xml
@@ -46,16 +46,4 @@
 
     </com.android.systemui.statusbar.phone.NearestTouchFrame>
 
-    <com.android.systemui.statusbar.policy.DeadZone
-        android:id="@+id/deadzone"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:layout_gravity="top"
-        systemui:minSize="@dimen/navigation_bar_deadzone_size"
-        systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
-        systemui:holdTime="@integer/navigation_bar_deadzone_hold"
-        systemui:decayTime="@integer/navigation_bar_deadzone_decay"
-        systemui:orientation="vertical"
-        />
-
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 9d336e2..f0138a6 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -60,7 +60,11 @@
 
             <include layout="@layout/heads_up_status_bar_layout" />
 
+            <!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
+             individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
+             DISABLE_NOTIFICATION_ICONS, respectively -->
             <LinearLayout
+                android:id="@+id/status_bar_left_side"
                 android:layout_height="match_parent"
                 android:layout_width="match_parent"
                 android:clipChildren="false"
@@ -76,8 +80,6 @@
                     android:gravity="center_vertical|start"
                 />
 
-                <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
-                     PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
                 <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                     android:id="@+id/notification_icon_area"
                     android:layout_width="0dp"
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index f7e2344..63bbe62 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -28,4 +28,7 @@
 
     <!-- We have only space for one notification on phone landscape layouts. -->
     <integer name="keyguard_max_notification_count">1</integer>
+
+    <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
+    <integer name="navigation_bar_deadzone_orientation">1</integer>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 67dabdb..f91af03 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -35,4 +35,7 @@
     <!-- Animation duration when using long press on recents to dock -->
     <integer name="long_press_dock_anim_duration">290</integer>
 
+    <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
+    <integer name="navigation_bar_deadzone_orientation">0</integer>
+
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 711d550..f49d3de4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -93,6 +93,9 @@
     <integer name="navigation_bar_deadzone_hold">333</integer>
     <integer name="navigation_bar_deadzone_decay">333</integer>
 
+    <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
+    <integer name="navigation_bar_deadzone_orientation">0</integer>
+
     <bool name="config_dead_zone_flash">false</bool>
 
     <!-- Whether to enable dimming navigation buttons when wallpaper is not visible, should be
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 1aad27f..6aa2754 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -258,9 +258,9 @@
     /**
      * Cancels the remote recents animation started from {@link #startRecentsActivity}.
      */
-    public void cancelRecentsAnimation() {
+    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
         try {
-            ActivityManager.getService().cancelRecentsAnimation();
+            ActivityManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to cancel recents animation", e);
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index 79c1cb1..bff0d9b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -17,10 +17,27 @@
 package com.android.systemui.shared.system;
 
 import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+import sun.misc.Resource;
+
 public class NavigationBarCompat {
+    /**
+     * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
+     * home button press/long press over are ignored and will start to drag when exceeded and the
+     * touch slop is when the respected operation will occur when exceeded. Touch slop must be
+     * larger than the drag slop.
+     */
+    public static final int QUICK_STEP_DRAG_SLOP_PX = convertDpToPixel(10);
+    public static final int QUICK_SCRUB_DRAG_SLOP_PX = convertDpToPixel(20);
+    public static final int QUICK_STEP_TOUCH_SLOP_PX = convertDpToPixel(40);
+    public static final int QUICK_SCRUB_TOUCH_SLOP_PX = convertDpToPixel(35);
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW})
     public @interface HitTarget{}
@@ -42,7 +59,6 @@
      * Interaction type: whether the gesture to swipe up from the navigation bar will trigger
      * launcher to show overview
      */
-
     public static final int FLAG_DISABLE_SWIPE_UP = 0x1;
     /**
      * Interaction type: enable quick scrub interaction on the home button
@@ -58,4 +74,8 @@
      * Interaction type: show/hide the back button while this service is connected to launcher
      */
     public static final int FLAG_HIDE_BACK_BUTTON = 0x8;
+
+    private static int convertDpToPixel(float dp){
+        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 5fa6c79..80e226d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -61,6 +61,14 @@
         }
     }
 
+    public void setSplitScreenMinimized(boolean minimized) {
+        try {
+            mAnimationController.setSplitScreenMinimized(minimized);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to set minimize dock", e);
+        }
+    }
+
     public void finish(boolean toHome) {
         try {
             mAnimationController.finish(toHome);
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index 5a2263c..ae6ee2a 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -73,7 +73,7 @@
     void onAppOpChanged(int code, int uid, String packageName, boolean active);
 
     /**
-     * Gets active app ops for this user and package.
+     * Gets active app ops for this user and package
      */
     @Nullable ArraySet<Integer> getAppOps(int userId, String packageName);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 8cff56d..8e59842 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Handler;
@@ -39,6 +40,7 @@
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.GraphicBufferCompat;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.CallbackController;
@@ -71,11 +73,13 @@
     private final DeviceProvisionedController mDeviceProvisionedController
             = Dependency.get(DeviceProvisionedController.class);
     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
+    private final Intent mQuickStepIntent;
 
     private IOverviewProxy mOverviewProxy;
     private int mConnectionBackoffAttempts;
     private CharSequence mOnboardingText;
     private @InteractionType int mInteractionFlags;
+    private boolean mIsEnabled;
 
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
@@ -130,14 +134,23 @@
                     });
                 }
             } finally {
+                Prefs.putInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, mInteractionFlags);
                 Binder.restoreCallingIdentity(token);
             }
         }
     };
 
-    private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            updateEnabledState();
+
+            // When launcher service is disabled, reset interaction flags because it is inactive
+            if (!isEnabled()) {
+                mInteractionFlags = 0;
+                Prefs.remove(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS);
+            }
+
             // Reconnect immediately, instead of waiting for resume to arrive.
             startConnectionToCurrentUser();
         }
@@ -196,17 +209,21 @@
         mConnectionBackoffAttempts = 0;
         mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
                 com.android.internal.R.string.config_recentsComponentName));
+        mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
+                .setPackage(mRecentsComponentName.getPackageName());
+        mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, 0);
 
         // Listen for the package update changes.
         if (SystemServicesProxy.getInstance(context)
                 .isSystemUser(mDeviceProvisionedController.getCurrentUser())) {
+            updateEnabledState();
             mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
             filter.addDataScheme("package");
             filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
                     PatternMatcher.PATTERN_LITERAL);
             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            mContext.registerReceiver(mLauncherAddedReceiver, filter);
+            mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
         }
     }
 
@@ -222,7 +239,7 @@
         disconnectFromLauncherService();
 
         // If user has not setup yet or already connected, do not try to connect
-        if (!mDeviceProvisionedController.isCurrentUserSetup()) {
+        if (!mDeviceProvisionedController.isCurrentUserSetup() || !isEnabled()) {
             return;
         }
         mHandler.removeCallbacks(mConnectionRunnable);
@@ -248,6 +265,7 @@
     public void addCallback(OverviewProxyListener listener) {
         mConnectionCallbacks.add(listener);
         listener.onConnectionChanged(mOverviewProxy != null);
+        listener.onInteractionFlagsChanged(mInteractionFlags);
     }
 
     @Override
@@ -256,7 +274,11 @@
     }
 
     public boolean shouldShowSwipeUpUI() {
-        return getProxy() != null && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
+        return isEnabled() && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
+    }
+
+    public boolean isEnabled() {
+        return mIsEnabled;
     }
 
     public IOverviewProxy getProxy() {
@@ -292,6 +314,11 @@
         }
     }
 
+    private void updateEnabledState() {
+        mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent, 0,
+                ActivityManagerWrapper.getInstance().getCurrentUserId()) != null;
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(TAG_OPS + " state:");
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 2a27147..7f7a769 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -54,7 +54,8 @@
             Key.HAS_SEEN_RECENTS_ONBOARDING,
             Key.SEEN_RINGER_GUIDANCE_COUNT,
             Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
-            Key.TOUCHED_RINGER_TOGGLE
+            Key.TOUCHED_RINGER_TOGGLE,
+            Key.QUICK_STEP_INTERACTION_FLAGS
     })
     public @interface Key {
         @Deprecated
@@ -93,6 +94,7 @@
         String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
         String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
         String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
+        String QUICK_STEP_INTERACTION_FLAGS = "QuickStepInteractionFlags";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
index e18ac74..cdf4ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.classifier;
 
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import java.util.ArrayList;
@@ -49,13 +52,18 @@
 public class AnglesClassifier extends StrokeClassifier {
     private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
 
+    public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.ang",
+            Build.IS_DEBUGGABLE);
+
+    private static String TAG = "ANG";
+
     public AnglesClassifier(ClassifierData classifierData) {
         mClassifierData = classifierData;
     }
 
     @Override
     public String getTag() {
-        return "ANG";
+        return TAG;
     }
 
     @Override
@@ -170,18 +178,31 @@
 
         public float getAnglesVariance() {
             float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+            if (VERBOSE) {
+                FalsingLog.i(TAG, "getAnglesVariance: (first pass) " + anglesVariance);
+                FalsingLog.i(TAG, "   - mFirstLength=" + mFirstLength);
+                FalsingLog.i(TAG, "   - mLength=" + mLength);
+            }
             if (mFirstLength < mLength / 2f) {
                 anglesVariance = Math.min(anglesVariance, mFirstAngleVariance
                         + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount));
+                if (VERBOSE) FalsingLog.i(TAG, "getAnglesVariance: (second pass) " + anglesVariance);
             }
             return anglesVariance;
         }
 
         public float getAnglesPercentage() {
             if (mAnglesCount == 0.0f) {
+                if (VERBOSE) FalsingLog.i(TAG, "getAnglesPercentage: count==0, result=1");
                 return 1.0f;
             }
-            return (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
+            final float result = (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
+            if (VERBOSE) {
+                FalsingLog.i(TAG, "getAnglesPercentage: left=" + mLeftAngles + " right="
+                        + mRightAngles + " straight=" + mStraightAngles + " count=" + mAnglesCount
+                        + " result=" + result);
+            }
+            return result;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
index 6883dd0..5f6c1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
@@ -20,8 +20,6 @@
     public static float evaluate(float value, int type) {
         final boolean secureUnlock = type == Classifier.BOUNCER_UNLOCK;
         float evaluation = 0.0f;
-        if (value > 0.05) evaluation++;
-        if (value > 0.10) evaluation++;
         if (value > 0.20) evaluation++;
         if (value > 0.40 && !secureUnlock) evaluation++;
         if (value > 0.80 && !secureUnlock) evaluation++;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
index 6df72b1..66f0cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.classifier;
 
+import android.os.Build;
+import android.os.SystemProperties;
 import android.view.MotionEvent;
 
 import java.util.ArrayList;
@@ -34,6 +36,10 @@
  * should be in this interval.
  */
 public class SpeedAnglesClassifier extends StrokeClassifier {
+    public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.spd_ang",
+            Build.IS_DEBUGGABLE);
+    public static final String TAG = "SPD_ANG";
+
     private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
 
     public SpeedAnglesClassifier(ClassifierData classifierData) {
@@ -42,7 +48,7 @@
 
     @Override
     public String getTag() {
-        return "SPD_ANG";
+        return TAG;
     }
 
     @Override
@@ -135,14 +141,24 @@
         }
 
         public float getAnglesVariance() {
-            return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
+            final float v = mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
+            if (VERBOSE) {
+                FalsingLog.i(TAG, "getAnglesVariance: sum^2=" + mSumSquares
+                        + " count=" + mCount + " result=" + v);
+            }
+            return v;
         }
 
         public float getAnglesPercentage() {
             if (mAnglesCount == 0.0f) {
                 return 1.0f;
             }
-            return (mAcceleratingAngles) / mAnglesCount;
+            final float v = (mAcceleratingAngles) / mAnglesCount;
+            if (VERBOSE) {
+                FalsingLog.i(TAG, "getAnglesPercentage: angles=" + mAcceleratingAngles
+                        + " count=" + mAnglesCount + " result=" + v);
+            }
+            return v;
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 49b00ce..a70b358 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -229,10 +229,11 @@
                         // Bump the notification when the bucket dropped.
                         .setWhen(mWarningTriggerTimeMs)
                         .setShowWhen(false)
-                        .setContentTitle(title)
                         .setContentText(contentText)
+                        .setContentTitle(title)
                         .setOnlyAlertOnce(true)
                         .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
+                        .setStyle(new Notification.BigTextStyle().bigText(contentText))
                         .setVisibility(Notification.VISIBILITY_PUBLIC);
         if (hasBatterySettings()) {
             nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
@@ -483,16 +484,24 @@
         d.setMessage(mContext.getString(R.string.auto_saver_enabled_text,
                 getLowBatteryAutoTriggerDefaultLevel()));
 
-        // Negative == "got it". Just close the dialog. Battery saver has already been enabled.
-        d.setNegativeButton(R.string.auto_saver_okay_action, null);
-        d.setPositiveButton(R.string.open_saver_setting_action, (dialog, which) ->
-                mContext.startActivity(actionBatterySaverSetting));
+        // "Got it". Just close the dialog. Automatic battery has been enabled already.
+        d.setPositiveButton(R.string.auto_saver_okay_action,
+                (dialog, which) -> onAutoSaverEnabledConfirmationClosed());
+
+        // "Settings" -> Opens the battery saver settings activity.
+        d.setNeutralButton(R.string.open_saver_setting_action, (dialog, which) -> {
+            mContext.startActivity(actionBatterySaverSetting);
+            onAutoSaverEnabledConfirmationClosed();
+        });
         d.setShowForAllUsers(true);
-        d.setOnDismissListener((dialog) -> mSaverEnabledConfirmation = null);
+        d.setOnDismissListener((dialog) -> onAutoSaverEnabledConfirmationClosed());
         d.show();
         mSaverEnabledConfirmation = d;
     }
 
+    private void onAutoSaverEnabledConfirmationClosed() {
+        mSaverEnabledConfirmation = null;
+    }
 
     private void setSaverMode(boolean mode, boolean needFirstTimeWarning) {
         BatterySaverUtils.setPowerSaveMode(mContext, mode, needFirstTimeWarning);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
index eb95866..20e3cee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -44,4 +44,9 @@
     public TextView getAppLabel() {
         return mSecondLine;
     }
+
+    @Override
+    protected boolean animationsEnabled() {
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 862a6a4..e7e756f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -159,13 +159,13 @@
         return getColorForState(getContext(), state);
     }
 
-    public static void animateGrayScale(int fromColor, int toColor, ImageView iv,
-            final Runnable endRunnable) {
+    private void animateGrayScale(int fromColor, int toColor, ImageView iv,
+        final Runnable endRunnable) {
         if (iv instanceof AlphaControlledSlashImageView) {
             ((AlphaControlledSlashImageView)iv)
                     .setFinalImageTintList(ColorStateList.valueOf(toColor));
         }
-        if (ValueAnimator.areAnimatorsEnabled()) {
+        if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
             final float fromAlpha = Color.alpha(fromColor);
             final float toAlpha = Color.alpha(toColor);
             final float fromChannel = Color.red(fromColor);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 09d928f..cc60f87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -179,7 +179,7 @@
     protected void handleStateChanged(QSTile.State state) {
         int circleColor = getCircleColor(state.state);
         if (circleColor != mCircleColor) {
-            if (mBg.isShown()) {
+            if (mBg.isShown() && animationsEnabled()) {
                 ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
                         .setDuration(QS_ANIM_LENGTH);
                 animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
@@ -205,6 +205,10 @@
         }
     }
 
+    protected boolean animationsEnabled() {
+        return true;
+    }
+
     private int getCircleColor(int state) {
         switch (state) {
             case Tile.STATE_ACTIVE:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 19da3db..6fcb1c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -126,7 +126,7 @@
         @Override
         public void onTaskStackChangedBackground() {
             // Skip background preloading recents in SystemUI if the overview services is bound
-            if (Dependency.get(OverviewProxyService.class).getProxy() != null) {
+            if (Dependency.get(OverviewProxyService.class).isEnabled()) {
                 return;
             }
 
@@ -300,7 +300,7 @@
 
     public void onBootCompleted() {
         // Skip preloading tasks if we are already bound to the service
-        if (Dependency.get(OverviewProxyService.class).getProxy() != null) {
+        if (Dependency.get(OverviewProxyService.class).isEnabled()) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
index 2ec78cf..019c680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
@@ -62,7 +62,7 @@
     public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
         mFsc.onAppOpChanged(code, uid, packageName, active);
         mPresenter.getHandler().post(() -> {
-          mEntryManager.updateNotificationsForAppOps(code, uid, packageName, active);
+          mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
         });
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 4b6ab64..ab46b39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -383,8 +383,6 @@
         }
         mGroupManager.onEntryAdded(entry);
 
-        updateAppOps(entry);
-
         updateRankingAndSort(mRankingMap);
     }
 
@@ -403,25 +401,14 @@
         updateRankingAndSort(ranking);
     }
 
-    private void updateAppOps(Entry entry) {
-        final int uid = entry.notification.getUid();
-        final String pkg = entry.notification.getPackageName();
-        ArraySet<Integer> activeOps = mFsc.getAppOps(entry.notification.getUserId(), pkg);
-        if (activeOps != null) {
-            int N = activeOps.size();
-            for (int i = 0; i < N; i++) {
-                updateAppOp(activeOps.valueAt(i), uid, pkg, true);
-            }
-        }
-    }
-
-    public void updateAppOp(int appOp, int uid, String pkg, boolean showIcon) {
+    public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
         synchronized (mEntries) {
             final int N = mEntries.size();
             for (int i = 0; i < N; i++) {
                 Entry entry = mEntries.valueAt(i);
                 if (uid == entry.notification.getUid()
-                    && pkg.equals(entry.notification.getPackageName())) {
+                        && pkg.equals(entry.notification.getPackageName())
+                        && key.equals(entry.key)) {
                     if (showIcon) {
                         entry.mActiveAppOps.add(appOp);
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 45df450..849cfdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -43,6 +43,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.NotificationMessagingUtil;
@@ -665,6 +666,7 @@
         }
         // Add the expanded view and icon.
         mNotificationData.add(entry);
+        tagForeground(entry.notification);
         updateNotifications();
     }
 
@@ -726,6 +728,19 @@
         mPendingNotifications.put(key, shadeEntry);
     }
 
+    @VisibleForTesting
+    protected void tagForeground(StatusBarNotification notification) {
+        ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
+                notification.getUserId(), notification.getPackageName());
+        if (activeOps != null) {
+            int N = activeOps.size();
+            for (int i = 0; i < N; i++) {
+                updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
+                        notification.getPackageName(), true);
+            }
+        }
+    }
+
     @Override
     public void addNotification(StatusBarNotification notification,
             NotificationListenerService.RankingMap ranking) {
@@ -736,10 +751,11 @@
         }
     }
 
-    public void updateNotificationsForAppOps(int appOp, int uid, String pkg, boolean showIcon) {
-        if (mForegroundServiceController.getStandardLayoutKey(
-                UserHandle.getUserId(uid), pkg) != null) {
-            mNotificationData.updateAppOp(appOp, uid, pkg, showIcon);
+    public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
+        String foregroundKey = mForegroundServiceController.getStandardLayoutKey(
+                UserHandle.getUserId(uid), pkg);
+        if (foregroundKey != null) {
+            mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
             updateNotifications();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 75b31c5..9fcb090 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.StatusBarManager.DISABLE_CLOCK;
 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
 import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
 
@@ -96,6 +97,7 @@
         mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
         mClockView = mStatusBar.findViewById(R.id.clock);
         showSystemIconArea(false);
+        showClock(false);
         initEmergencyCryptkeeperText();
         initOperatorName();
     }
@@ -163,6 +165,13 @@
                 showNotificationIconArea(animate);
             }
         }
+        if ((diff1 & DISABLE_CLOCK) != 0) {
+            if ((state1 & DISABLE_CLOCK) != 0) {
+                hideClock(animate);
+            } else {
+                showClock(animate);
+            }
+        }
     }
 
     protected int adjustDisableFlags(int state) {
@@ -171,6 +180,7 @@
                 && shouldHideNotificationIcons()) {
             state |= DISABLE_NOTIFICATION_ICONS;
             state |= DISABLE_SYSTEM_INFO;
+            state |= DISABLE_CLOCK;
         }
         if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
             if (mNetworkController.hasEmergencyCryptKeeperText()) {
@@ -195,11 +205,17 @@
 
     public void hideSystemIconArea(boolean animate) {
         animateHide(mSystemIconArea, animate);
-        animateHide(mClockView, animate);
     }
 
     public void showSystemIconArea(boolean animate) {
         animateShow(mSystemIconArea, animate);
+    }
+
+    public void hideClock(boolean animate) {
+        animateHide(mClockView, animate);
+    }
+
+    public void showClock(boolean animate) {
         animateShow(mClockView, animate);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f216695..4c4eb60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -124,7 +124,7 @@
     private TintedKeyButtonDrawable mRotateSuggestionIcon;
 
     private GestureHelper mGestureHelper;
-    private DeadZone mDeadZone;
+    private final DeadZone mDeadZone;
     private final NavigationBarTransitions mBarTransitions;
     private final OverviewProxyService mOverviewProxyService;
 
@@ -263,6 +263,7 @@
                 new ButtonDispatcher(R.id.accessibility_button));
         mButtonDispatchers.put(R.id.rotate_suggestion,
                 new ButtonDispatcher(R.id.rotate_suggestion));
+        mDeadZone = new DeadZone(this);
     }
 
     public BarTransitions getBarTransitions() {
@@ -297,6 +298,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mDeadZone.onTouchEvent(event)) {
+            // Consumed the touch event
+            return true;
+        }
         switch (event.getActionMasked()) {
             case ACTION_DOWN:
                 int x = (int) event.getX();
@@ -319,6 +324,10 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (mDeadZone.onTouchEvent(event)) {
+            // Consumed the touch event
+            return true;
+        }
         if (mGestureHelper.onTouchEvent(event)) {
             return true;
         }
@@ -389,7 +398,7 @@
 
     public boolean isQuickScrubEnabled() {
         return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
-                && mOverviewProxyService.getProxy() != null && isOverviewEnabled()
+                && mOverviewProxyService.isEnabled() && isOverviewEnabled()
                 && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0);
     }
 
@@ -587,7 +596,7 @@
         // recents buttons when disconnected from launcher service in screen pinning mode,
         // as they are used for exiting.
         final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
-        if (mOverviewProxyService.getProxy() != null) {
+        if (mOverviewProxyService.isEnabled()) {
             // Use interaction flags to show/hide navigation buttons but will be shown if required
             // to exit screen pinning.
             final int flags = mOverviewProxyService.getInteractionFlags();
@@ -818,6 +827,7 @@
     @Override
     protected void onDraw(Canvas canvas) {
         mGestureHelper.onDraw(canvas);
+        mDeadZone.onDraw(canvas);
         super.onDraw(canvas);
     }
 
@@ -889,10 +899,8 @@
     public void reorient() {
         updateCurrentView();
 
-        mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
-
         ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
-        mDeadZone.setDisplayRotation(mCurrentRotation);
+        mDeadZone.onConfigurationChanged(mCurrentRotation);
 
         // force the low profile & disabled states into compliance
         mBarTransitions.init();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 04cb620..304a499 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -488,7 +488,7 @@
                 mUpdateFlingVelocity = vel;
             }
         } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
-                && !mStatusBar.isBouncerShowing()) {
+                && !mStatusBar.isBouncerShowing() && !mStatusBar.isKeyguardFadingAway()) {
             long timePassed = SystemClock.uptimeMillis() - mDownTime;
             if (timePassed < ViewConfiguration.getLongPressTimeout()) {
                 // Lets show the user that he can actually expand the panel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index 12bdfc6..a7d5aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -43,10 +43,9 @@
     }
 
     public void init() {
-        mLeftSide = mView.findViewById(R.id.notification_icon_area);
+        mLeftSide = mView.findViewById(R.id.status_bar_left_side);
         mStatusIcons = mView.findViewById(R.id.statusIcons);
         mBattery = mView.findViewById(R.id.battery);
-        mClock = mView.findViewById(R.id.clock);
         applyModeBackground(-1, getMode(), false /*animate*/);
         applyMode(getMode(), false /*animate*/);
     }
@@ -89,8 +88,7 @@
             anims.playTogether(
                     animateTransitionTo(mLeftSide, newAlpha),
                     animateTransitionTo(mStatusIcons, newAlpha),
-                    animateTransitionTo(mBattery, newAlphaBC),
-                    animateTransitionTo(mClock, newAlphaBC)
+                    animateTransitionTo(mBattery, newAlphaBC)
                     );
             if (isLightsOut(mode)) {
                 anims.setDuration(LIGHTS_OUT_DURATION);
@@ -101,7 +99,6 @@
             mLeftSide.setAlpha(newAlpha);
             mStatusIcons.setAlpha(newAlpha);
             mBattery.setAlpha(newAlphaBC);
-            mClock.setAlpha(newAlphaBC);
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index a51cd93..d3790d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -51,6 +51,10 @@
 import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
 import static com.android.systemui.OverviewProxyService.TAG_OPS;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
+import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_DRAG_SLOP_PX;
+import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_TOUCH_SLOP_PX;
+import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_DRAG_SLOP_PX;
+import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_TOUCH_SLOP_PX;
 
 /**
  * Class to detect gestures on the navigation bar and implement quick scrub.
@@ -69,6 +73,7 @@
     private float mTranslation;
     private int mTouchDownX;
     private int mTouchDownY;
+    private boolean mDragScrubActive;
     private boolean mDragPositive;
     private boolean mIsVertical;
     private boolean mIsRTL;
@@ -82,7 +87,6 @@
     private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator();
     private final Rect mTrackRect = new Rect();
     private final Paint mTrackPaint = new Paint();
-    private final int mScrollTouchSlop;
     private final OverviewProxyService mOverviewEventSender;
     private final int mTrackThickness;
     private final int mTrackPadding;
@@ -115,6 +119,7 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             mQuickScrubActive = false;
+            mDragScrubActive = false;
             mTranslation = 0;
             mQuickScrubEndAnimator.setCurrentPlayTime(mQuickScrubEndAnimator.getDuration());
             mHomeButtonView = null;
@@ -123,7 +128,6 @@
 
     public QuickStepController(Context context) {
         mContext = context;
-        mScrollTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mOverviewEventSender = Dependency.get(OverviewProxyService.class);
         mTrackThickness = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_thickness);
         mTrackPadding = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_edge_padding);
@@ -168,8 +172,8 @@
     }
 
     private boolean handleTouchEvent(MotionEvent event) {
-        if (!mNavigationBarView.isQuickScrubEnabled()
-                && !mNavigationBarView.isQuickStepSwipeUpEnabled()) {
+        if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
+                && !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
             mNavigationBarView.getHomeButton().setDelayTouchFeedback(false /* delay */);
             return false;
         }
@@ -177,8 +181,8 @@
 
         final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
         final boolean homePressed = mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME;
-        int action = event.getAction();
-        switch (action & MotionEvent.ACTION_MASK) {
+        int action = event.getActionMasked();
+        switch (action) {
             case MotionEvent.ACTION_DOWN: {
                 int x = (int) event.getX();
                 int y = (int) event.getY();
@@ -199,28 +203,29 @@
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
-                if (mQuickStepStarted || !mAllowGestureDetection){
+                if (mQuickStepStarted || !mAllowGestureDetection || mHomeButtonView == null){
                     break;
                 }
                 int x = (int) event.getX();
                 int y = (int) event.getY();
                 int xDiff = Math.abs(x - mTouchDownX);
                 int yDiff = Math.abs(y - mTouchDownY);
-                boolean exceededTouchSlopX = xDiff > mScrollTouchSlop && xDiff > yDiff;
-                boolean exceededTouchSlopY = yDiff > mScrollTouchSlop && yDiff > xDiff;
-                boolean exceededTouchSlop, exceededPerpendicularTouchSlop;
+
+                boolean exceededScrubTouchSlop, exceededSwipeUpTouchSlop, exceededScrubDragSlop;
                 int pos, touchDown, offset, trackSize;
 
                 if (mIsVertical) {
-                    exceededTouchSlop = exceededTouchSlopY;
-                    exceededPerpendicularTouchSlop = exceededTouchSlopX;
+                    exceededScrubTouchSlop = yDiff > QUICK_STEP_TOUCH_SLOP_PX && yDiff > xDiff;
+                    exceededSwipeUpTouchSlop = xDiff > QUICK_STEP_DRAG_SLOP_PX && xDiff > yDiff;
+                    exceededScrubDragSlop = yDiff > QUICK_SCRUB_DRAG_SLOP_PX && yDiff > xDiff;
                     pos = y;
                     touchDown = mTouchDownY;
                     offset = pos - mTrackRect.top;
                     trackSize = mTrackRect.height();
                 } else {
-                    exceededTouchSlop = exceededTouchSlopX;
-                    exceededPerpendicularTouchSlop = exceededTouchSlopY;
+                    exceededScrubTouchSlop = xDiff > QUICK_STEP_TOUCH_SLOP_PX && xDiff > yDiff;
+                    exceededSwipeUpTouchSlop = yDiff > QUICK_SCRUB_TOUCH_SLOP_PX && yDiff > xDiff;
+                    exceededScrubDragSlop = xDiff > QUICK_SCRUB_DRAG_SLOP_PX && xDiff > yDiff;
                     pos = x;
                     touchDown = mTouchDownX;
                     offset = pos - mTrackRect.left;
@@ -228,7 +233,7 @@
                 }
                 // Decide to start quickstep if dragging away from the navigation bar, otherwise in
                 // the parallel direction, decide to start quickscrub. Only one may run.
-                if (!mQuickScrubActive && exceededPerpendicularTouchSlop) {
+                if (!mQuickScrubActive && exceededSwipeUpTouchSlop) {
                     if (mNavigationBarView.isQuickStepSwipeUpEnabled()) {
                         startQuickStep(event);
                     }
@@ -244,29 +249,38 @@
                     offset -= mIsVertical ? mTrackRect.height() : mTrackRect.width();
                 }
 
-                // Control the button movement
-                if (!mQuickScrubActive && exceededTouchSlop) {
-                    boolean allowDrag = !mDragPositive
-                            ? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
-                    if (allowDrag) {
+                final boolean allowDrag = !mDragPositive
+                        ? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
+                if (allowDrag) {
+                    // Passing the drag slop is for visual feedback and will not initiate anything
+                    if (!mDragScrubActive && exceededScrubDragSlop) {
                         mDownOffset = offset;
+                        mDragScrubActive = true;
+                    }
+
+                    // Passing the drag slop then touch slop will start quick step
+                    if (!mQuickScrubActive && exceededScrubTouchSlop) {
                         homeButton.abortCurrentGesture();
                         startQuickScrub();
                     }
                 }
-                if (mQuickScrubActive && (mDragPositive && offset >= 0
+
+                if ((mQuickScrubActive || mDragScrubActive) && (mDragPositive && offset >= 0
                         || !mDragPositive && offset <= 0)) {
-                    float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
                     mTranslation = !mDragPositive
-                        ? Utilities.clamp(offset - mDownOffset, -trackSize, 0)
-                        : Utilities.clamp(offset - mDownOffset, 0, trackSize);
-                    try {
-                        mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction);
-                        if (DEBUG_OVERVIEW_PROXY) {
-                            Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
+                            ? Utilities.clamp(offset - mDownOffset, -trackSize, 0)
+                            : Utilities.clamp(offset - mDownOffset, 0, trackSize);
+                    if (mQuickScrubActive) {
+                        float scrubFraction =
+                                Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
+                        try {
+                            mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction);
+                            if (DEBUG_OVERVIEW_PROXY) {
+                                Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
+                            }
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Failed to send progress of quick scrub.", e);
                         }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Failed to send progress of quick scrub.", e);
                     }
                     if (mIsVertical) {
                         mHomeButtonView.setTranslationY(mTranslation);
@@ -283,7 +297,9 @@
         }
 
         // Proxy motion events to launcher if not handled by quick scrub
-        if (!mQuickScrubActive && mAllowGestureDetection) {
+        // Proxy motion events up/cancel that would be sent after long press on any nav button
+        if (!mQuickScrubActive && (mAllowGestureDetection || action == MotionEvent.ACTION_CANCEL
+                || action == MotionEvent.ACTION_UP)) {
             proxyMotionEvents(event);
         }
         return mQuickScrubActive || mQuickStepStarted;
@@ -370,10 +386,14 @@
         mOverviewEventSender.notifyQuickStepStarted();
         mNavigationBarView.getHomeButton().abortCurrentGesture();
         mHandler.removeCallbacksAndMessages(null);
+
+        if (mDragScrubActive) {
+            animateEnd();
+        }
     }
 
     private void startQuickScrub() {
-        if (!mQuickScrubActive) {
+        if (!mQuickScrubActive && mDragScrubActive) {
             mQuickScrubActive = true;
             mLightTrackColor = mContext.getColor(R.color.quick_step_track_background_light);
             mDarkTrackColor = mContext.getColor(R.color.quick_step_track_background_dark);
@@ -391,15 +411,17 @@
     }
 
     private void endQuickScrub(boolean animate) {
-        if (mQuickScrubActive) {
+        if (mQuickScrubActive || mDragScrubActive) {
             animateEnd();
-            try {
-                mOverviewEventSender.getProxy().onQuickScrubEnd();
-                if (DEBUG_OVERVIEW_PROXY) {
-                    Log.d(TAG_OPS, "Quick Scrub End");
+            if (mQuickScrubActive) {
+                try {
+                    mOverviewEventSender.getProxy().onQuickScrubEnd();
+                    if (DEBUG_OVERVIEW_PROXY) {
+                        Log.d(TAG_OPS, "Quick Scrub End");
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to send end of quick scrub.", e);
                 }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to send end of quick scrub.", e);
             }
         }
         if (mHomeButtonView != null && !animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 2c025b5..28f3162 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -807,7 +807,7 @@
     @VisibleForTesting
     protected WakeLock createWakeLock() {
          return new DelayedWakeLock(getHandler(),
-                WakeLock.createPartial(mContext, "Doze"));
+                WakeLock.createPartial(mContext, "Scrims"));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 750d2a5..f7a97be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1425,11 +1425,15 @@
     }
 
     public void addQsTile(ComponentName tile) {
-        mQSPanel.getHost().addTile(tile);
+        if (mQSPanel.getHost() != null) {
+            mQSPanel.getHost().addTile(tile);
+        }
     }
 
     public void remQsTile(ComponentName tile) {
-        mQSPanel.getHost().removeTile(tile);
+        if (mQSPanel.getHost() != null) {
+            mQSPanel.getHost().removeTile(tile);
+        }
     }
 
     public void clickTile(ComponentName tile) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 378dad7..6a8d3a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -69,6 +69,10 @@
         setButton(BUTTON_NEGATIVE, mContext.getString(resId), onClick);
     }
 
+    public void setNeutralButton(int resId, OnClickListener onClick) {
+        setButton(BUTTON_NEUTRAL, mContext.getString(resId), onClick);
+    }
+
     public static void setShowForAllUsers(Dialog dialog, boolean show) {
         if (show) {
             dialog.getWindow().getAttributes().privateFlags |=
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 06040e2..4a11754 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -17,18 +17,16 @@
 package com.android.systemui.statusbar.policy;
 
 import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.os.SystemClock;
-import android.util.AttributeSet;
 import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.Surface;
-import android.view.View;
 
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
@@ -38,7 +36,7 @@
  * outside the navigation bar (since this is when accidental taps are more likely), then contracts
  * back over time (since a later tap might be intended for the top of the bar).
  */
-public class DeadZone extends View {
+public class DeadZone {
     public static final String TAG = "DeadZone";
 
     public static final boolean DEBUG = false;
@@ -47,6 +45,7 @@
 
     private static final boolean CHATTY = true; // print to logcat when we eat a click
     private final StatusBar mStatusBar;
+    private final NavigationBarView mNavigationBarView;
 
     private boolean mShouldFlash;
     private float mFlashFrac = 0f;
@@ -67,31 +66,11 @@
         }
     };
 
-    public DeadZone(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public DeadZone(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DeadZone,
-                defStyle, 0);
-
-        mHold = a.getInteger(R.styleable.DeadZone_holdTime, 0);
-        mDecay = a.getInteger(R.styleable.DeadZone_decayTime, 0);
-
-        mSizeMin = a.getDimensionPixelSize(R.styleable.DeadZone_minSize, 0);
-        mSizeMax = a.getDimensionPixelSize(R.styleable.DeadZone_maxSize, 0);
-
-        int index = a.getInt(R.styleable.DeadZone_orientation, -1);
-        mVertical = (index == VERTICAL);
-
-        if (DEBUG)
-            Slog.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
-                    + (mVertical ? " vertical" : " horizontal"));
-
-        setFlashOnTouchCapture(context.getResources().getBoolean(R.bool.config_dead_zone_flash));
-        mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
+    public DeadZone(NavigationBarView view) {
+        mNavigationBarView = view;
+        mStatusBar = SysUiServiceProvider.getComponent(mNavigationBarView.getContext(),
+                StatusBar.class);
+        onConfigurationChanged(HORIZONTAL);
     }
 
     static float lerp(float a, float b, float f) {
@@ -112,11 +91,29 @@
     public void setFlashOnTouchCapture(boolean dbg) {
         mShouldFlash = dbg;
         mFlashFrac = 0f;
-        postInvalidate();
+        mNavigationBarView.postInvalidate();
+    }
+
+    public void onConfigurationChanged(int rotation) {
+        mDisplayRotation = rotation;
+
+        final Resources res = mNavigationBarView.getResources();
+        mHold = res.getInteger(R.integer.navigation_bar_deadzone_hold);
+        mDecay = res.getInteger(R.integer.navigation_bar_deadzone_decay);
+
+        mSizeMin = res.getDimensionPixelSize(R.dimen.navigation_bar_deadzone_size);
+        mSizeMax = res.getDimensionPixelSize(R.dimen.navigation_bar_deadzone_size_max);
+        int index = res.getInteger(R.integer.navigation_bar_deadzone_orientation);
+        mVertical = (index == VERTICAL);
+
+        if (DEBUG) {
+            Slog.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+                    + (mVertical ? " vertical" : " horizontal"));
+        }
+        setFlashOnTouchCapture(res.getBoolean(R.bool.config_dead_zone_flash));
     }
 
     // I made you a touch event...
-    @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (DEBUG) {
             Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
@@ -143,7 +140,7 @@
             final boolean consumeEvent;
             if (mVertical) {
                 if (mDisplayRotation == Surface.ROTATION_270) {
-                    consumeEvent = event.getX() > getWidth() - size;
+                    consumeEvent = event.getX() > mNavigationBarView.getWidth() - size;
                 } else {
                     consumeEvent = event.getX() < size;
                 }
@@ -155,8 +152,8 @@
                     Slog.v(TAG, "consuming errant click: (" + event.getX() + "," + event.getY() + ")");
                 }
                 if (mShouldFlash) {
-                    post(mDebugFlash);
-                    postInvalidate();
+                    mNavigationBarView.post(mDebugFlash);
+                    mNavigationBarView.postInvalidate();
                 }
                 return true; // ...but I eated it
             }
@@ -168,19 +165,18 @@
         mLastPokeTime = event.getEventTime();
         if (DEBUG)
             Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
-        if (mShouldFlash) postInvalidate();
+        if (mShouldFlash) mNavigationBarView.postInvalidate();
     }
 
     public void setFlash(float f) {
         mFlashFrac = f;
-        postInvalidate();
+        mNavigationBarView.postInvalidate();
     }
 
     public float getFlash() {
         return mFlashFrac;
     }
 
-    @Override
     public void onDraw(Canvas can) {
         if (!mShouldFlash || mFlashFrac <= 0f) {
             return;
@@ -202,10 +198,6 @@
 
         if (DEBUG && size > mSizeMin)
             // crazy aggressive redrawing here, for debugging only
-            postInvalidateDelayed(100);
-    }
-
-    public void setDisplayRotation(int rotation) {
-        mDisplayRotation = rotation;
+            mNavigationBarView.postInvalidateDelayed(100);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 5d7e938..2fed3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -52,8 +52,11 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.VibratorHelper;
 
+import static android.view.KeyEvent.KEYCODE_HOME;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_TOUCH_SLOP_PX;
+import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_TOUCH_SLOP_PX;
 
 public class KeyButtonView extends ImageView implements ButtonInterface {
     private static final String TAG = KeyButtonView.class.getSimpleName();
@@ -62,9 +65,9 @@
     private int mContentDescriptionRes;
     private long mDownTime;
     private int mCode;
-    private int mTouchSlop;
     private int mTouchDownX;
     private int mTouchDownY;
+    private boolean mIsVertical;
     private boolean mSupportsLongpress = true;
     private AudioManager mAudioManager;
     private boolean mGestureAborted;
@@ -115,7 +118,6 @@
         a.recycle();
 
         setClickable(true);
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
         mRipple = new KeyButtonRipple(context, this);
@@ -200,7 +202,7 @@
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
-        final boolean isProxyConnected = mOverviewProxyService.getProxy() != null;
+        final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();
         final int action = ev.getAction();
         int x, y;
         if (action == MotionEvent.ACTION_DOWN) {
@@ -226,7 +228,7 @@
                     // Provide the same haptic feedback that the system offers for virtual keys.
                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                 }
-                if (!isProxyConnected) {
+                if (!showSwipeUI) {
                     playSoundEffect(SoundEffectConstants.CLICK);
                 }
                 removeCallbacks(mCheckLongPress);
@@ -235,8 +237,11 @@
             case MotionEvent.ACTION_MOVE:
                 x = (int)ev.getRawX();
                 y = (int)ev.getRawY();
-                boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > mTouchSlop;
-                boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > mTouchSlop;
+
+                boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) >
+                        (mIsVertical ? QUICK_SCRUB_TOUCH_SLOP_PX : QUICK_STEP_TOUCH_SLOP_PX);
+                boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) >
+                        (mIsVertical ? QUICK_STEP_TOUCH_SLOP_PX : QUICK_SCRUB_TOUCH_SLOP_PX);
                 if (exceededTouchSlopX || exceededTouchSlopY) {
                     // When quick step is enabled, prevent animating the ripple triggered by
                     // setPressed and decide to run it on touch up
@@ -255,7 +260,7 @@
                 final boolean doIt = isPressed() && !mLongClicked;
                 setPressed(false);
                 final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
-                if (isProxyConnected) {
+                if (showSwipeUI) {
                     if (doIt) {
                         if (doHapticFeedback) {
                             mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
@@ -270,8 +275,12 @@
                 if (mCode != 0) {
                     if (doIt) {
                         // If there was a pending remote recents animation, then we need to
-                        // cancel the animation now before we handle the button itself
-                        ActivityManagerWrapper.getInstance().cancelRecentsAnimation();
+                        // cancel the animation now before we handle the button itself. In the case
+                        // where we are going home and the recents animation has already started,
+                        // just cancel the recents animation, leaving the home stack in place
+                        boolean isHomeKey = mCode == KEYCODE_HOME;
+                        ActivityManagerWrapper.getInstance().cancelRecentsAnimation(!isHomeKey);
+
                         sendEvent(KeyEvent.ACTION_UP, 0);
                         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                     } else {
@@ -342,7 +351,7 @@
 
     @Override
     public void setVertical(boolean vertical) {
-        //no op
+        mIsVertical = vertical;
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index 5ec3dff..e1814098 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -23,7 +23,7 @@
  */
 public class DelayedWakeLock implements WakeLock {
 
-    private static final long RELEASE_DELAY_MS = 120;
+    private static final long RELEASE_DELAY_MS = 240;
 
     private final Handler mHandler;
     private final WakeLock mInner;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 6f71e55..13157fe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -213,7 +213,7 @@
                     .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
                     .withEndAction(() -> {
                         mWindow.getDecorView().requestAccessibilityFocus();
-                        if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true)) {
+                        if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
                             mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500);
                         }
                     })
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
index 2a48c4b..dc0e2b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
@@ -86,7 +86,7 @@
         mListener.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         waitForIdleSync(mHandler);
-        verify(mEntryManager, times(1)).updateNotificationsForAppOps(
+        verify(mEntryManager, times(1)).updateNotificationsForAppOp(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index 5e27fde..5ec77ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -139,32 +139,7 @@
         Assert.assertTrue(mRow.getEntry().channel != null);
     }
 
-    @Test
-    public void testAdd_appOpsAdded() {
-        ArraySet<Integer> expected = new ArraySet<>();
-        expected.add(3);
-        expected.add(235);
-        expected.add(1);
-        when(mFsc.getAppOps(mRow.getEntry().notification.getUserId(),
-                mRow.getEntry().notification.getPackageName())).thenReturn(expected);
 
-        mNotificationData.add(mRow.getEntry());
-        assertEquals(expected.size(),
-                mNotificationData.get(mRow.getEntry().key).mActiveAppOps.size());
-        for (int op : expected) {
-            assertTrue(" entry missing op " + op,
-                    mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
-        }
-    }
-
-    @Test
-    public void testAdd_noExistingAppOps() {
-        when(mFsc.getAppOps(mRow.getEntry().notification.getUserId(),
-                mRow.getEntry().notification.getPackageName())).thenReturn(null);
-
-        mNotificationData.add(mRow.getEntry());
-        assertEquals(0, mNotificationData.get(mRow.getEntry().key).mActiveAppOps.size());
-    }
 
     @Test
     public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
@@ -181,7 +156,9 @@
 
         for (int op : expectedOps) {
             mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
-                    NotificationTestHelper.PKG, true);
+                    NotificationTestHelper.PKG, mRow.getEntry().key, true);
+            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+                    NotificationTestHelper.PKG, row2.getEntry().key, true);
         }
         for (int op : expectedOps) {
             assertTrue(mRow.getEntry().key + " doesn't have op " + op,
@@ -205,12 +182,12 @@
 
         for (int op : expectedOps) {
             mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
-                    NotificationTestHelper.PKG, true);
+                    NotificationTestHelper.PKG, row2.getEntry().key, true);
         }
 
         expectedOps.remove(OP_ACCEPT_HANDOVER);
         mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
-                NotificationTestHelper.PKG, false);
+                NotificationTestHelper.PKG, row2.getEntry().key, false);
 
         assertTrue(mRow.getEntry().key + " doesn't have op " + OP_CAMERA,
                 mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index 3703d6a..7cfd7a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -37,18 +37,15 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.ArraySet;
 import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
@@ -56,12 +53,13 @@
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiOffloadThread;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
+import junit.framework.Assert;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -285,13 +283,12 @@
         com.android.systemui.util.Assert.isNotMainThread();
 
         when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
-                .thenReturn("something");
+                .thenReturn(mEntry.key);
         mEntry.row = mRow;
         mEntryManager.getNotificationData().add(mEntry);
 
-
         mHandler.post(() -> {
-            mEntryManager.updateNotificationsForAppOps(
+            mEntryManager.updateNotificationsForAppOp(
                     AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
                     mEntry.notification.getPackageName(), true);
         });
@@ -309,10 +306,65 @@
         when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
                 .thenReturn(null);
         mHandler.post(() -> {
-            mEntryManager.updateNotificationsForAppOps(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
+            mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
         });
         waitForIdleSync(mHandler);
 
         verify(mPresenter, never()).updateNotificationViews();
     }
+
+    @Test
+    public void testAddNotificationExistingAppOps() {
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        ArraySet<Integer> expected = new ArraySet<>();
+        expected.add(3);
+        expected.add(235);
+        expected.add(1);
+
+        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(expected);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
+
+        mEntryManager.tagForeground(mEntry.notification);
+
+        Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size());
+        for (int op : expected) {
+            assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op));
+        }
+    }
+
+    @Test
+    public void testAdd_noExistingAppOps() {
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
+        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(null);
+
+        mEntryManager.tagForeground(mEntry.notification);
+        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
+    }
+
+    @Test
+    public void testAdd_existingAppOpsNotForegroundNoti() {
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(3);
+        ops.add(235);
+        ops.add(1);
+        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(ops);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn("something else");
+
+        mEntryManager.tagForeground(mEntry.notification);
+        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 2edcd01..9e8fa22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -89,15 +89,11 @@
 
         assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
-        assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock)
-                .getVisibility());
 
         fragment.disable(0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
-        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
-                .getVisibility());
     }
 
     @Test
@@ -115,4 +111,20 @@
 
         Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
     }
+
+    @Test
+    public void testDisableClock() throws Exception {
+        mFragments.dispatchResume();
+        processAllMessages();
+
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        fragment.initNotificationIconArea(mMockNotificiationAreaController);
+        fragment.disable(StatusBarManager.DISABLE_CLOCK, 0, false);
+
+        assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+
+        fragment.disable(0, 0, false);
+
+        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+    }
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c6878d7..c66c7b0 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5562,6 +5562,16 @@
     // OS: P
     PREVIOUSLY_CONNECTED_DEVICES = 1370;
 
+    // ACTION: A Settings Slice is requested
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_SETTINGS_SLICE_REQUESTED = 1371;
+
+    // ACTION: A Settings Slice is updated with new value
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_SETTINGS_SLICE_CHANGED = 1372;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index bde6bd8..cd90e3f 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -24,6 +24,8 @@
 import static android.system.OsConstants.SOCK_DGRAM;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.IIpSecService;
@@ -42,6 +44,7 @@
 import android.net.TrafficStats;
 import android.net.util.NetdService;
 import android.os.Binder;
+import android.os.DeadSystemException;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -974,6 +977,13 @@
         return service;
     }
 
+    @NonNull
+    private AppOpsManager getAppOpsManager() {
+        AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
+        return appOps;
+    }
+
     /** @hide */
     @VisibleForTesting
     public IpSecService(Context context, IpSecServiceConfiguration config) {
@@ -1240,7 +1250,9 @@
      */
     @Override
     public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
-            String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) {
+            String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
+            String callingPackage) {
+        enforceTunnelPermissions(callingPackage);
         checkNotNull(binder, "Null Binder passed to createTunnelInterface");
         checkNotNull(underlyingNetwork, "No underlying network was specified");
         checkInetAddress(localAddr);
@@ -1320,8 +1332,8 @@
      */
     @Override
     public synchronized void addAddressToTunnelInterface(
-            int tunnelResourceId, LinkAddress localAddr) {
-        enforceNetworkStackPermission();
+            int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
+        enforceTunnelPermissions(callingPackage);
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
         // Get tunnelInterface record; if no such interface is found, will throw
@@ -1352,10 +1364,10 @@
      */
     @Override
     public synchronized void removeAddressFromTunnelInterface(
-            int tunnelResourceId, LinkAddress localAddr) {
-        enforceNetworkStackPermission();
-        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+            int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
+        enforceTunnelPermissions(callingPackage);
 
+        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
         // Get tunnelInterface record; if no such interface is found, will throw
         // IllegalArgumentException
         TunnelInterfaceRecord tunnelInterfaceInfo =
@@ -1383,7 +1395,9 @@
      * server
      */
     @Override
-    public synchronized void deleteTunnelInterface(int resourceId) throws RemoteException {
+    public synchronized void deleteTunnelInterface(
+            int resourceId, String callingPackage) throws RemoteException {
+        enforceTunnelPermissions(callingPackage);
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
         releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
     }
@@ -1469,7 +1483,6 @@
             case IpSecTransform.MODE_TRANSPORT:
                 break;
             case IpSecTransform.MODE_TUNNEL:
-                enforceNetworkStackPermission();
                 break;
             default:
                 throw new IllegalArgumentException(
@@ -1477,9 +1490,20 @@
         }
     }
 
-    private void enforceNetworkStackPermission() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK,
-                "IpSecService");
+    private void enforceTunnelPermissions(String callingPackage) {
+        checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
+        switch (getAppOpsManager().noteOp(
+                    AppOpsManager.OP_MANAGE_IPSEC_TUNNELS,
+                    Binder.getCallingUid(), callingPackage)) {
+            case AppOpsManager.MODE_DEFAULT:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
+                break;
+            case AppOpsManager.MODE_ALLOWED:
+                return;
+            default:
+                throw new SecurityException("Request to ignore AppOps for non-legacy API");
+        }
     }
 
     private void createOrUpdateTransform(
@@ -1535,8 +1559,12 @@
      * result in all of those sockets becoming unable to send or receive data.
      */
     @Override
-    public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
-            throws RemoteException {
+    public synchronized IpSecTransformResponse createTransform(
+            IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
+        checkNotNull(c);
+        if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
+            enforceTunnelPermissions(callingPackage);
+        }
         checkIpSecConfig(c);
         checkNotNull(binder, "Null Binder passed to createTransform");
         final int resourceId = mNextResourceId++;
@@ -1657,8 +1685,9 @@
      */
     @Override
     public synchronized void applyTunnelModeTransform(
-            int tunnelResourceId, int direction, int transformResourceId) throws RemoteException {
-        enforceNetworkStackPermission();
+            int tunnelResourceId, int direction,
+            int transformResourceId, String callingPackage) throws RemoteException {
+        enforceTunnelPermissions(callingPackage);
         checkDirection(direction);
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 26b83f5..fb5fba0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1344,13 +1344,7 @@
      * @param provider the name of the location provider
      */
     private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
-        if (mEnabledProviders.contains(provider)) {
-            return true;
-        }
-        if (mDisabledProviders.contains(provider)) {
-            return false;
-        }
-        return isLocationProviderEnabledForUser(provider, mCurrentUserId);
+        return isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId);
     }
 
     /**
@@ -1359,13 +1353,33 @@
      * processes belonging to background users.
      *
      * @param provider the name of the location provider
-     * @param uid      the requestor's UID
+     * @param userId   the user id to query
      */
-    private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
+    private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
+        if (mEnabledProviders.contains(provider)) {
+            return true;
+        }
+        if (mDisabledProviders.contains(provider)) {
+            return false;
+        }
+        return isLocationProviderEnabledForUser(provider, userId);
+    }
+
+
+    /**
+     * Returns "true" if access to the specified location provider is allowed by the specified
+     * user's settings. Access to all location providers is forbidden to non-location-provider
+     * processes belonging to background users.
+     *
+     * @param provider the name of the location provider
+     * @param uid      the requestor's UID
+     * @param userId   the user id to query
+     */
+    private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) {
         if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
             return false;
         }
-        return isAllowedByCurrentUserSettingsLocked(provider);
+        return isAllowedByUserSettingsLockedForUser(provider, userId);
     }
 
     /**
@@ -1572,7 +1586,8 @@
                         continue;
                     }
                     if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
-                        if (enabledOnly && !isAllowedByUserSettingsLocked(name, uid)) {
+                        if (enabledOnly
+                                && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
                             continue;
                         }
                         if (criteria != null && !LocationProvider.propertiesMeetCriteria(
@@ -2098,7 +2113,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid);
+        boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid, mCurrentUserId);
         if (isProviderEnabled) {
             applyRequirementsLocked(name);
         } else {
@@ -2219,7 +2234,7 @@
                 LocationProviderInterface provider = mProvidersByName.get(name);
                 if (provider == null) return null;
 
-                if (!isAllowedByUserSettingsLocked(name, uid)) return null;
+                if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
 
                 Location location;
                 if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
@@ -2540,6 +2555,173 @@
     }
 
     /**
+     *  Returns the current location enabled/disabled status for a user
+     *
+     *  @param userId the id of the user
+     *  @return true if location is enabled
+     */
+    @Override
+    public boolean isLocationEnabledForUser(int userId) {
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                final String allowedProviders = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        userId);
+                if (allowedProviders == null) {
+                    return false;
+                }
+                final List<String> providerList = Arrays.asList(allowedProviders.split(","));
+                for(String provider : mRealProviders.keySet()) {
+                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                        continue;
+                    }
+                    if (providerList.contains(provider)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     *  Enable or disable location for a user
+     *
+     *  @param enabled true to enable location, false to disable location
+     *  @param userId the id of the user
+     */
+    @Override
+    public void setLocationEnabledForUser(boolean enabled, int userId) {
+        mContext.enforceCallingPermission(
+            android.Manifest.permission.WRITE_SECURE_SETTINGS,
+            "Requires WRITE_SECURE_SETTINGS permission");
+
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                final Set<String> allRealProviders = mRealProviders.keySet();
+                // Update all providers on device plus gps and network provider when disabling
+                // location
+                Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
+                allProvidersSet.addAll(allRealProviders);
+                // When disabling location, disable gps and network provider that could have been
+                // enabled by location mode api.
+                if (enabled == false) {
+                    allProvidersSet.add(LocationManager.GPS_PROVIDER);
+                    allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
+                }
+                if (allProvidersSet.isEmpty()) {
+                    return;
+                }
+                // to ensure thread safety, we write the provider name with a '+' or '-'
+                // and let the SettingsProvider handle it rather than reading and modifying
+                // the list of enabled providers.
+                final String prefix = enabled ? "+" : "-";
+                StringBuilder locationProvidersAllowed = new StringBuilder();
+                for (String provider : allProvidersSet) {
+                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                        continue;
+                    }
+                    locationProvidersAllowed.append(prefix);
+                    locationProvidersAllowed.append(provider);
+                    locationProvidersAllowed.append(",");
+                }
+                // Remove the trailing comma
+                locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+                Settings.Secure.putStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        locationProvidersAllowed.toString(),
+                        userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     *  Returns the current enabled/disabled status of a location provider and user
+     *
+     *  @param provider name of the provider
+     *  @param userId the id of the user
+     *  @return true if the provider exists and is enabled
+     */
+    @Override
+    public boolean isProviderEnabledForUser(String provider, int userId) {
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
+        // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
+        // so we discourage its use
+        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+
+        int uid = Binder.getCallingUid();
+        synchronized (mLock) {
+            LocationProviderInterface p = mProvidersByName.get(provider);
+            return p != null
+                    && isAllowedByUserSettingsLocked(provider, uid, userId);
+        }
+    }
+
+    /**
+     * Enable or disable a single location provider.
+     *
+     * @param provider name of the provider
+     * @param enabled true to enable the provider. False to disable the provider
+     * @param userId the id of the user to set
+     * @return true if the value was set, false on errors
+     */
+    @Override
+    public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS,
+                "Requires WRITE_SECURE_SETTINGS permission");
+
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
+        // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
+        // so we discourage its use
+        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                // No such provider exists
+                if (!mProvidersByName.containsKey(provider)) return false;
+
+                // If it is a test provider, do not write to Settings.Secure
+                if (mMockProviders.containsKey(provider)) {
+                    setTestProviderEnabled(provider, enabled);
+                    return true;
+                }
+
+                // to ensure thread safety, we write the provider name with a '+' or '-'
+                // and let the SettingsProvider handle it rather than reading and modifying
+                // the list of enabled providers.
+                String providerChange = (enabled ? "+" : "-") + provider;
+                return Settings.Secure.putStringForUser(
+                        mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        providerChange, userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Read location provider status from Settings.Secure
      *
      * @param provider the location provider to query
@@ -2560,6 +2742,23 @@
     }
 
     /**
+     * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
+     * current user id
+     *
+     * @param userId the user id to get or set value
+     */
+    private void checkInteractAcrossUsersPermission(int userId) {
+        int uid = Binder.getCallingUid();
+        if (UserHandle.getUserId(uid) != userId) {
+            if (ActivityManager.checkComponentPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
+                    != PERMISSION_GRANTED) {
+                throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
+            }
+        }
+    }
+
+    /**
      * Returns "true" if the UID belongs to a bound location provider.
      *
      * @param uid the uid
@@ -3076,7 +3275,11 @@
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
+        setTestProviderEnabled(provider, enabled);
+    }
 
+    /** Enable or disable a test location provider. */
+    private void setTestProviderEnabled(String provider, boolean enabled) {
         synchronized (mLock) {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f97c6d6..db52b97 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -204,6 +204,8 @@
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_HOME_IN_PLACE;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -257,7 +259,6 @@
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.admin.DevicePolicyCache;
-import android.app.admin.DevicePolicyManager;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.IBackupManager;
@@ -5238,12 +5239,14 @@
     }
 
     @Override
-    public void cancelRecentsAnimation() {
+    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                mWindowManager.cancelRecentsAnimation();
+                mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
+                        ? REORDER_MOVE_HOME_TO_ORIGINAL_POSITION
+                        : REORDER_KEEP_HOME_IN_PLACE);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8ecd93e..9c2b1a5 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -19,16 +19,10 @@
 import android.app.ActivityManager;
 import android.app.job.JobProtoEnums;
 import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.hardware.usb.UsbManager;
 import android.net.wifi.WifiActivityEnergyInfo;
-import android.os.PowerManager.ServiceType;
-import android.os.PowerSaveState;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
@@ -37,18 +31,18 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelFormatException;
+import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.os.WorkSource;
-import android.os.WorkSource.WorkChain;
 import android.os.connectivity.CellularBatteryStats;
-import android.os.connectivity.WifiBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
 import android.os.health.HealthStatsParceler;
 import android.os.health.HealthStatsWriter;
 import android.os.health.UidHealthStats;
@@ -57,6 +51,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BatteryStatsHelper;
@@ -65,7 +60,6 @@
 import com.android.internal.os.RpmStats;
 import com.android.internal.util.DumpUtils;
 import com.android.server.LocalServices;
-import android.util.StatsLog;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -701,13 +695,6 @@
         }
     }
 
-    public void noteUsbConnectionState(boolean connected) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteUsbConnectionStateLocked(connected);
-        }
-    }
-
     public void notePhoneSignalStrength(SignalStrength signalStrength) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -1164,35 +1151,6 @@
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
 
-    public final static class UsbConnectionReceiver extends BroadcastReceiver {
-        private static final String TAG = UsbConnectionReceiver.class.getSimpleName();
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                final Intent usbState = context.registerReceiver(null, new IntentFilter(UsbManager.ACTION_USB_STATE));
-                if (usbState != null) {
-                    handleUsbState(usbState);
-                }
-            } else if (UsbManager.ACTION_USB_STATE.equals(action)) {
-                handleUsbState(intent);
-            }
-        }
-        private void handleUsbState(Intent intent) {
-            IBatteryStats bs = getService();
-            if (bs == null) {
-                Slog.w(TAG, "Could not access batterystats");
-                return;
-            }
-            boolean connected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
-            try {
-                bs.noteUsbConnectionState(connected);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Could not access batterystats: ", e);
-            }
-        }
-    }
-
     final class WakeupReasonThread extends Thread {
         private static final int MAX_REASON_SIZE = 512;
         private CharsetDecoder mDecoder;
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 9df321c..99337b8 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -23,6 +23,8 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_TOP;
 
 import android.app.ActivityOptions;
 import android.content.ComponentName;
@@ -32,6 +34,7 @@
 import android.os.Trace;
 import android.util.Slog;
 import android.view.IRecentsAnimationRunner;
+import com.android.server.wm.RecentsAnimationController;
 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
 import com.android.server.wm.WindowManagerService;
 
@@ -123,7 +126,7 @@
 
             // Fetch all the surface controls and pass them to the client to get the animation
             // started
-            mWindowManager.cancelRecentsAnimation();
+            mWindowManager.cancelRecentsAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
             mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
                     display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds());
 
@@ -140,7 +143,7 @@
     }
 
     @Override
-    public void onAnimationFinished(boolean moveHomeToTop) {
+    public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode) {
         synchronized (mService) {
             if (mWindowManager.getRecentsAnimationController() == null) return;
 
@@ -151,9 +154,8 @@
                         "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
                 mWindowManager.deferSurfaceLayout();
                 try {
-                    mWindowManager.cleanupRecentsAnimation(moveHomeToTop);
+                    mWindowManager.cleanupRecentsAnimation(reorderMode);
 
-                    // Move the home stack to the front
                     final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
                     if (homeActivity == null) {
                         return;
@@ -162,15 +164,19 @@
                     // Restore the launched-behind state
                     homeActivity.mLaunchTaskBehind = false;
 
-                    if (moveHomeToTop) {
+                    if (reorderMode == REORDER_MOVE_HOME_TO_TOP) {
                         // Bring the home stack to the front
                         final ActivityStack homeStack = homeActivity.getStack();
                         mStackSupervisor.mNoAnimActivities.add(homeActivity);
                         homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
-                    } else {
+                    } else if (reorderMode == REORDER_MOVE_HOME_TO_ORIGINAL_POSITION){
                         // Restore the home stack to its previous position
                         final ActivityDisplay display = homeActivity.getDisplay();
                         display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
+                    } else {
+                        // Keep home stack in place, nothing changes, so ignore the transition logic
+                        // below
+                        return;
                     }
 
                     mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
@@ -180,6 +186,11 @@
                     // No reason to wait for the pausing activity in this case, as the hiding of
                     // surfaces needs to be done immediately.
                     mWindowManager.executeAppTransition();
+
+                    // After reordering the stacks, reset the minimized state. At this point, either
+                    // home is now top-most and we will stay minimized (if in split-screen), or we
+                    // will have returned to the app, and the minimized state should be reset
+                    mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
                 } finally {
                     mWindowManager.continueSurfaceLayout();
                     Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c8b6b50..3e82c25 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -320,13 +320,13 @@
         0,  // STREAM_SYSTEM
         0,  // STREAM_RING
         0,  // STREAM_MUSIC
-        0,  // STREAM_ALARM
+        1,  // STREAM_ALARM
         0,  // STREAM_NOTIFICATION
         0,  // STREAM_BLUETOOTH_SCO
         0,  // STREAM_SYSTEM_ENFORCED
         0,  // STREAM_DTMF
         0,  // STREAM_TTS
-        0   // STREAM_ACCESSIBILITY
+        1   // STREAM_ACCESSIBILITY
     };
 
     /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
@@ -1208,6 +1208,8 @@
                             System.VOLUME_SETTINGS_INT[a11yStreamAlias];
                     mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
                             mStreamStates[a11yStreamAlias], caller);
+                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].refreshRange(
+                            mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY]);
                 }
             }
             if (sIndependentA11yVolume) {
@@ -1389,7 +1391,15 @@
     }
 
     private int rescaleIndex(int index, int srcStream, int dstStream) {
-        return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
+        final int rescaled =
+                (index * mStreamStates[dstStream].getMaxIndex()
+                        + mStreamStates[srcStream].getMaxIndex() / 2)
+                / mStreamStates[srcStream].getMaxIndex();
+        if (rescaled < mStreamStates[dstStream].getMinIndex()) {
+            return mStreamStates[dstStream].getMinIndex();
+        } else {
+            return rescaled;
+        }
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -4602,8 +4612,8 @@
     //  4       VolumeStreamState.class
     public class VolumeStreamState {
         private final int mStreamType;
-        private final int mIndexMin;
-        private final int mIndexMax;
+        private int mIndexMin;
+        private int mIndexMax;
 
         private boolean mIsMuted;
         private String mVolumeIndexSettingName;
@@ -4889,6 +4899,24 @@
         }
 
         /**
+         * Updates the min/max index values from another stream. Use this when changing the alias
+         * for the current stream type.
+         * @param sourceStreamType
+         */
+        // must be sync'd on mSettingsLock before VolumeStreamState.class
+        @GuardedBy("VolumeStreamState.class")
+        public void refreshRange(int sourceStreamType) {
+            mIndexMin = MIN_STREAM_VOLUME[sourceStreamType] * 10;
+            mIndexMax = MAX_STREAM_VOLUME[sourceStreamType] * 10;
+            // verify all current volumes are within bounds
+            for (int i = 0 ; i < mIndexMap.size(); i++) {
+                final int device = mIndexMap.keyAt(i);
+                final int index = mIndexMap.valueAt(i);
+                mIndexMap.put(device, getValidIndex(index));
+            }
+        }
+
+        /**
          * Copies all device/index pairs from the given VolumeStreamState after initializing
          * them with the volume for DEVICE_OUT_DEFAULT. No-op if the source VolumeStreamState
          * has the same stream type as this instance.
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 88df195..905c7e3 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -686,10 +686,15 @@
     }
 
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
-        ArrayList<AmbientBrightnessDayStats> stats = mAmbientBrightnessStatsTracker.getUserStats(
-                userId);
-        return (stats != null) ? new ParceledListSlice<>(stats) : new ParceledListSlice<>(
-                Collections.EMPTY_LIST);
+        if (mAmbientBrightnessStatsTracker != null) {
+            ArrayList<AmbientBrightnessDayStats> stats =
+                    mAmbientBrightnessStatsTracker.getUserStats(
+                            userId);
+            if (stats != null) {
+                return new ParceledListSlice<>(stats);
+            }
+        }
+        return ParceledListSlice.emptyList();
     }
 
     // Not allowed to keep the SensorEvent so used to copy the data we care about.
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index 3d6f7ad..720eaaa5 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/services/core/java/com/android/server/input/InputWindowHandle.java
@@ -92,7 +92,7 @@
     public int inputFeatures;
 
     // Display this input is on.
-    public final int displayId;
+    public int displayId;
 
     private native void nativeDispose();
 
diff --git a/services/core/java/com/android/server/net/watchlist/ReportEncoder.java b/services/core/java/com/android/server/net/watchlist/ReportEncoder.java
index 2a8f4d5..a482e05 100644
--- a/services/core/java/com/android/server/net/watchlist/ReportEncoder.java
+++ b/services/core/java/com/android/server/net/watchlist/ReportEncoder.java
@@ -49,6 +49,7 @@
      * Apply DP on watchlist results, and generate a serialized watchlist report ready to store
      * in DropBox.
      */
+    @Nullable
     static byte[] encodeWatchlistReport(WatchlistConfig config, byte[] userSecret,
             List<String> appDigestList, WatchlistReportDbHelper.AggregatedResult aggregatedResult) {
         Map<String, Boolean> resultMap = PrivacyUtils.createDpEncodedReportMap(
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index d793842..8352ca6 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -16,6 +16,7 @@
 
 package com.android.server.net.watchlist;
 
+import android.annotation.Nullable;
 import android.os.FileUtils;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -55,9 +56,6 @@
     private static final String NETWORK_WATCHLIST_DB_FOR_TEST_PATH =
             "/data/misc/network_watchlist/network_watchlist_for_test.xml";
 
-    // Hash for null / unknown config, a 32 byte array filled with content 0x00
-    private static final byte[] UNKNOWN_CONFIG_HASH = new byte[32];
-
     private static class XmlTags {
         private static final String WATCHLIST_CONFIG = "watchlist-config";
         private static final String SHA256_DOMAIN = "sha256-domain";
@@ -228,16 +226,21 @@
         return mIsSecureConfig;
     }
 
+    @Nullable
+    /**
+     * Get watchlist config SHA-256 digest.
+     * Return null if watchlist config does not exist.
+     */
     public byte[] getWatchlistConfigHash() {
         if (!mXmlFile.exists()) {
-            return UNKNOWN_CONFIG_HASH;
+            return null;
         }
         try {
             return DigestUtils.getSha256Hash(mXmlFile);
         } catch (IOException | NoSuchAlgorithmException e) {
             Log.e(TAG, "Unable to get watchlist config hash", e);
         }
-        return UNKNOWN_CONFIG_HASH;
+        return null;
     }
 
     /**
@@ -271,8 +274,10 @@
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("Watchlist config hash: " + HexDump.toHexString(getWatchlistConfigHash()));
+        final byte[] hash = getWatchlistConfigHash();
+        pw.println("Watchlist config hash: " + (hash != null ? HexDump.toHexString(hash) : null));
         pw.println("Domain CRC32 digest list:");
+        // mDomainDigests won't go from non-null to null so it's safe
         if (mDomainDigests != null) {
             mDomainDigests.crc32Digests.dump(fd, pw, args);
         }
@@ -281,6 +286,7 @@
             mDomainDigests.sha256Digests.dump(fd, pw, args);
         }
         pw.println("Ip CRC32 digest list:");
+        // mIpDigests won't go from non-null to null so it's safe
         if (mIpDigests != null) {
             mIpDigests.crc32Digests.dump(fd, pw, args);
         }
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
index b331b9c..864ce5d 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -346,6 +346,7 @@
      * @param ipAddresses Ip address that you want to search in watchlist.
      * @return Ip address that exists in watchlist, null if it does not match anything.
      */
+    @Nullable
     private String searchIpInWatchlist(String[] ipAddresses) {
         for (String ipAddress : ipAddresses) {
             if (isIpInWatchlist(ipAddress)) {
@@ -377,6 +378,7 @@
      * @param host Host that we want to search.
      * @return Domain that exists in watchlist, null if it does not match anything.
      */
+    @Nullable
     private String searchAllSubDomainsInWatchlist(String host) {
         if (host == null) {
             return null;
@@ -392,6 +394,7 @@
 
     /** Get all sub-domains in a host */
     @VisibleForTesting
+    @Nullable
     static String[] getAllSubDomains(String host) {
         if (host == null) {
             return null;
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
index 632ab81..c69934a 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -144,6 +144,7 @@
      * Aggregate all records in database before input timestamp, and return a
      * rappor encoded result.
      */
+    @Nullable
     public AggregatedResult getAggregatedRecords(long untilTimestamp) {
         final String selectStatement = WhiteListReportContract.TIMESTAMP + " < ?";
 
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
index e20a510..c2f3ba0 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -86,7 +86,7 @@
         }
     }
 
-    public void reloadSettings() {
+    private void reloadSettings() {
         if (!mXmlFile.exists()) {
             // No settings config
             return;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index d5a32aa..c98f6a2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -110,7 +110,7 @@
     protected final Object mMutex;
     private final UserProfiles mUserProfiles;
     private final IPackageManager mPm;
-    private final UserManager mUm;
+    protected final UserManager mUm;
     private final Config mConfig;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f31ca0a..7813e70 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -114,6 +114,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioAttributes;
@@ -492,8 +493,8 @@
                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
             for (ComponentName cn : approvedAssistants) {
                 try {
-                    getBinderService().setNotificationAssistantAccessGrantedForUser(cn,
-                            userId, true);
+                    getBinderService().setNotificationAssistantAccessGrantedForUser(
+                            cn, userId, true);
                 } catch (RemoteException e) {
                     e.printStackTrace();
                 }
@@ -535,6 +536,8 @@
             mConditionProviders.migrateToXml();
             savePolicyFile();
         }
+
+        mAssistants.ensureAssistant();
     }
 
     private void loadPolicyFile() {
@@ -2286,6 +2289,12 @@
         }
 
         @Override
+        public int getBlockedChannelCount(String pkg, int uid) {
+            enforceSystemOrSystemUI("getBlockedChannelCount");
+            return mRankingHelper.getBlockedChannelCount(pkg, uid);
+        }
+
+        @Override
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
                 String pkg, int uid, boolean includeDeleted) {
             checkCallerIsSystem();
@@ -6134,11 +6143,14 @@
             return !getServices().isEmpty();
         }
 
-        protected void upgradeXml(final int xmlVersion, final int userId) {
-            if (xmlVersion == 0) {
-                // one time approval of the OOB assistant
-                Slog.d(TAG, "Approving default notification assistant for user " + userId);
-                readDefaultAssistant(userId);
+        protected void ensureAssistant() {
+            final List<UserInfo> activeUsers = mUm.getUsers(true);
+            for (UserInfo userInfo : activeUsers) {
+                int userId = userInfo.getUserHandle().getIdentifier();
+                if (getAllowedPackages(userId).isEmpty()) {
+                    Slog.d(TAG, "Approving default notification assistant for user " + userId);
+                    readDefaultAssistant(userId);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 98d5c9a..43d393a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -619,7 +621,7 @@
             updateConfig();
             return;
         }
-        if (channel.getImportance() < NotificationManager.IMPORTANCE_NONE
+        if (channel.getImportance() < IMPORTANCE_NONE
                 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
             throw new IllegalArgumentException("Invalid importance level");
         }
@@ -959,6 +961,23 @@
         return deletedCount;
     }
 
+    public int getBlockedChannelCount(String pkg, int uid) {
+        Preconditions.checkNotNull(pkg);
+        int blockedCount = 0;
+        Record r = getRecord(pkg, uid);
+        if (r == null) {
+            return blockedCount;
+        }
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
+                blockedCount++;
+            }
+        }
+        return blockedCount;
+    }
+
     /**
      * Sets importance.
      */
@@ -969,12 +988,12 @@
     }
 
     public void setEnabled(String packageName, int uid, boolean enabled) {
-        boolean wasEnabled = getImportance(packageName, uid) != NotificationManager.IMPORTANCE_NONE;
+        boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
         if (wasEnabled == enabled) {
             return;
         }
         setImportance(packageName, uid,
-                enabled ? DEFAULT_IMPORTANCE : NotificationManager.IMPORTANCE_NONE);
+                enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
     }
 
     @VisibleForTesting
@@ -1199,7 +1218,7 @@
             ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
             for (int i = 0; i < N; i++) {
                 final Record r = mRecords.valueAt(i);
-                if (r.importance == NotificationManager.IMPORTANCE_NONE) {
+                if (r.importance == IMPORTANCE_NONE) {
                     packageBans.put(r.uid, r.pkg);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9d3f48b..45f1a2b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -67,6 +67,8 @@
     public static final int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10;
     /** Indicates that dexopt should convert to CompactDex. */
     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;
 
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 5a7893a..320affb 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -267,7 +267,7 @@
                 final StringBuilder builder = new StringBuilder();
 
                 // The current version.
-                builder.append("8 ");
+                builder.append("9 ");
 
                 builder.append("dexopt");
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 892fa12..ebab1a7 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -60,6 +60,7 @@
 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
 import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
 import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 
@@ -521,6 +522,10 @@
         return getDexFlags(pkg.applicationInfo, compilerFilter, options);
     }
 
+    private boolean isAppImageEnabled() {
+        return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0;
+    }
+
     private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
         int flags = info.flags;
         boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
@@ -547,6 +552,14 @@
             case PackageManagerService.REASON_INSTALL:
                  generateCompactDex = false;
         }
+        // Use app images only if it is enabled and we are compiling
+        // profile-guided (so the app image doesn't conservatively contain all classes).
+        // If the app didn't request for the splits to be loaded in isolation or if it does not
+        // declare inter-split dependencies, then all the splits will be loaded in the base
+        // apk class loader (in the order of their definition, otherwise disable app images
+        // because they are unsupported for multiple class loaders. b/7269679
+        boolean generateAppImage = isProfileGuidedFilter && (info.splitDependencies == null ||
+                !info.requestsIsolatedSplitLoading()) && isAppImageEnabled();
         int dexFlags =
                 (isPublic ? DEXOPT_PUBLIC : 0)
                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
@@ -554,6 +567,7 @@
                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
                 | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
+                | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 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 2e530af..0e0bfbd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17,12 +17,12 @@
 package com.android.server.pm;
 
 import static android.Manifest.permission.DELETE_PACKAGES;
-import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
-import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
 import static android.Manifest.permission.INSTALL_PACKAGES;
+import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
 import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
+import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
 import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
@@ -167,8 +167,8 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageList;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ActivityIntentInfo;
@@ -310,10 +310,10 @@
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.PermissionManagerService;
-import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionsState;
 import com.android.server.pm.permission.PermissionsState.PermissionState;
 import com.android.server.security.VerityUtils;
@@ -8185,35 +8185,22 @@
     private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId, name);
-        final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
-        // reader
+        final int callingUid = Binder.getCallingUid();
         synchronized (mPackages) {
             final PackageParser.Provider provider = mProvidersByAuthority.get(name);
             PackageSetting ps = provider != null
                     ? mSettings.mPackages.get(provider.owner.packageName)
                     : null;
             if (ps != null) {
-                final boolean isInstantApp = ps.getInstantApp(userId);
-                // normal application; filter out instant application provider
-                if (instantAppPkgName == null && isInstantApp) {
-                    return null;
-                }
-                // instant application; filter out other instant applications
-                if (instantAppPkgName != null
-                        && isInstantApp
-                        && !provider.owner.packageName.equals(instantAppPkgName)) {
-                    return null;
-                }
-                // instant application; filter out non-exposed provider
-                if (instantAppPkgName != null
-                        && !isInstantApp
-                        && (provider.info.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0) {
-                    return null;
-                }
                 // provider not enabled
                 if (!mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)) {
                     return null;
                 }
+                final ComponentName component =
+                        new ComponentName(provider.info.packageName, provider.info.name);
+                if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
+                    return null;
+                }
                 return PackageParser.generateProviderInfo(
                         provider, flags, ps.readUserState(userId), userId);
             }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index fae0b24..d4625e9 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -379,7 +379,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (DEBUG)
-                Slog.d(TAG, "Time to poll something.");
+                Slog.d(TAG, "Time to trigger periodic alarm.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
                     Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
@@ -455,13 +455,13 @@
     public void setAlarmForSubscriberTriggering(long timestampMs) {
         enforceCallingPermission();
         if (DEBUG)
-            Slog.d(TAG, "Setting periodic alarm at " + timestampMs);
+            Slog.d(TAG, "Setting periodic alarm in about " +
+                    (timestampMs - SystemClock.elapsedRealtime()));
         final long callingToken = Binder.clearCallingIdentity();
         try {
             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
             // only fire when it awakens.
-            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
-            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, timestampMs, mPeriodicAlarmIntent);
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, mPeriodicAlarmIntent);
         } finally {
             Binder.restoreCallingIdentity(callingToken);
         }
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index ba67ff6..112d93c 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -161,7 +161,10 @@
 
         // Timeout callback to ensure we continue the animation if waiting for resuming or app
         // windows drawn fails
-        private final Runnable mResumeRunnable = () -> resume();
+        private final Runnable mResumeRunnable = () -> {
+            if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn");
+            resume();
+        };
 
         BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
                 @SchedulePipModeChangedState int schedulePipModeChangedState,
@@ -213,7 +216,7 @@
 
                 // When starting an animation from fullscreen, pause here and wait for the
                 // windows-drawn signal before we start the rest of the transition down into PiP.
-                if (mMoveFromFullscreen) {
+                if (mMoveFromFullscreen && mTarget.shouldDeferStartOnMoveToFullscreen()) {
                     pause();
                 }
             } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 647a2d6..68be4e8 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -34,6 +34,12 @@
     void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
 
     /**
+     * @return Whether the animation should be paused waiting for the windows to draw before
+     *         entering PiP.
+     */
+    boolean shouldDeferStartOnMoveToFullscreen();
+
+    /**
      * Sets the size of the target (without any intermediate steps, like scheduling animation)
      * but freezes the bounds of any tasks in the target at taskBounds, to allow for more
      * flexibility during resizing. Only works for the pinned stack at the moment.  This will
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c4f2bd4..0771b53 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3568,7 +3568,8 @@
                     if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
                         t.setLayer(mSplitScreenDividerAnchor, layer++);
                     }
-                    if (s.isAppAnimating() && state != ALWAYS_ON_TOP_STATE) {
+                    if ((s.isTaskAnimating() || s.isAppAnimating())
+                            && state != ALWAYS_ON_TOP_STATE) {
                         // Ensure the animation layer ends up above the
                         // highest animating stack and no higher.
                         layerForAnimationLayer = layer++;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5e2bb10..2cd2ef1 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -666,13 +666,16 @@
         }
         final TaskStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
+        final boolean minimizedForRecentsAnimation = recentsAnim != null &&
+                recentsAnim.isSplitScreenMinimized();
         boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
         if (homeVisible && topSecondaryStack != null) {
             // Home should only be considered visible if it is greater or equal to the top secondary
             // stack in terms of z-order.
             homeVisible = homeStack.compareTo(topSecondaryStack) >= 0;
         }
-        setMinimizedDockedStack(homeVisible, animate);
+        setMinimizedDockedStack(homeVisible || minimizedForRecentsAnimation, animate);
     }
 
     private boolean isWithinDisplay(Task task) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 7274aee..c4f5197 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -29,6 +29,7 @@
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
 
+import android.annotation.IntDef;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.WindowConfiguration;
 import android.graphics.Point;
@@ -67,12 +68,25 @@
     private static final boolean DEBUG = false;
     private static final long FAILSAFE_DELAY = 1000;
 
+    public static final int REORDER_KEEP_HOME_IN_PLACE = 0;
+    public static final int REORDER_MOVE_HOME_TO_TOP = 1;
+    public static final int REORDER_MOVE_HOME_TO_ORIGINAL_POSITION = 2;
+
+    @IntDef(prefix = { "REORDER_MODE_" }, value = {
+            REORDER_KEEP_HOME_IN_PLACE,
+            REORDER_MOVE_HOME_TO_TOP,
+            REORDER_MOVE_HOME_TO_ORIGINAL_POSITION
+    })
+    public @interface ReorderMode {}
+
     private final WindowManagerService mService;
     private final IRecentsAnimationRunner mRunner;
     private final RecentsAnimationCallbacks mCallbacks;
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
     private final int mDisplayId;
-    private final Runnable mFailsafeRunnable = this::cancelAnimation;
+    private final Runnable mFailsafeRunnable = () -> {
+        cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
+    };
 
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mHomeAppToken;
@@ -84,16 +98,20 @@
     private boolean mPendingStart = true;
 
     // Set when the animation has been canceled
-    private boolean mCanceled = false;
+    private boolean mCanceled;
 
     // Whether or not the input consumer is enabled. The input consumer must be both registered and
     // enabled for it to start intercepting touch events.
     private boolean mInputConsumerEnabled;
 
+    // Whether or not the recents animation should cause the primary split-screen stack to be
+    // minimized
+    private boolean mSplitScreenMinimized;
+
     private Rect mTmpRect = new Rect();
 
     public interface RecentsAnimationCallbacks {
-        void onAnimationFinished(boolean moveHomeToTop);
+        void onAnimationFinished(@ReorderMode int reorderMode);
     }
 
     private final IRecentsAnimationController mController =
@@ -102,7 +120,7 @@
         @Override
         public TaskSnapshot screenshotTask(int taskId) {
             if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled);
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mService.getWindowManagerLock()) {
                     if (mCanceled) {
@@ -131,7 +149,7 @@
         @Override
         public void finish(boolean moveHomeToTop) {
             if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled);
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mService.getWindowManagerLock()) {
                     if (mCanceled) {
@@ -141,7 +159,9 @@
 
                 // Note, the callback will handle its own synchronization, do not lock on WM lock
                 // prior to calling the callback
-                mCallbacks.onAnimationFinished(moveHomeToTop);
+                mCallbacks.onAnimationFinished(moveHomeToTop
+                        ? REORDER_MOVE_HOME_TO_TOP
+                        : REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -150,7 +170,7 @@
         @Override
         public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars)
                 throws RemoteException {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mService.getWindowManagerLock()) {
                     for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
@@ -167,7 +187,7 @@
         public void setInputConsumerEnabled(boolean enabled) {
             if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled="
                     + mCanceled);
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mService.getWindowManagerLock()) {
                     if (mCanceled) {
@@ -182,6 +202,23 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void setSplitScreenMinimized(boolean minimized) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    if (mCanceled) {
+                        return;
+                    }
+
+                    mSplitScreenMinimized = minimized;
+                    mService.checkSplitScreenMinimizedChanged(true /* animate */);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     /**
@@ -222,14 +259,14 @@
 
         // Skip the animation if there is nothing to animate
         if (mPendingAnimations.isEmpty()) {
-            cancelAnimation();
+            cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
             return;
         }
 
         try {
             mRunner.asBinder().linkToDeath(this, 0);
         } catch (RemoteException e) {
-            cancelAnimation();
+            cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
             return;
         }
 
@@ -299,7 +336,7 @@
                 reasons).sendToTarget();
     }
 
-    void cancelAnimation() {
+    void cancelAnimation(@ReorderMode int reorderMode) {
         if (DEBUG) Log.d(TAG, "cancelAnimation()");
         synchronized (mService.getWindowManagerLock()) {
             if (mCanceled) {
@@ -314,18 +351,20 @@
                 Slog.e(TAG, "Failed to cancel recents animation", e);
             }
         }
+
         // Clean up and return to the previous app
         // Don't hold the WM lock here as it calls back to AM/RecentsAnimation
-        mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
+        mCallbacks.onAnimationFinished(reorderMode);
     }
 
-    void cleanupAnimation(boolean moveHomeToTop) {
+    void cleanupAnimation(@ReorderMode int reorderMode) {
         if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
                 + mPendingAnimations.size());
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
             adapter.mTask.setCanAffectSystemUiFlags(true);
-            if (moveHomeToTop) {
+            if (reorderMode == REORDER_MOVE_HOME_TO_TOP
+                    || reorderMode == REORDER_KEEP_HOME_IN_PLACE) {
                 adapter.mTask.dontAnimateDimExit();
             }
             adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
@@ -333,7 +372,7 @@
         mPendingAnimations.clear();
 
         mRunner.asBinder().unlinkToDeath(this, 0);
-
+        // Clear associated input consumers
         mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
         mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
     }
@@ -344,7 +383,7 @@
 
     @Override
     public void binderDied() {
-        cancelAnimation();
+        cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
     }
 
     void checkAnimationReady(WallpaperController wallpaperController) {
@@ -358,6 +397,10 @@
         }
     }
 
+    boolean isSplitScreenMinimized() {
+        return mSplitScreenMinimized;
+    }
+
     boolean isWallpaperVisible(WindowState w) {
         return w != null && w.mAppToken != null && mHomeAppToken == w.mAppToken
                 && isHomeAppOverWallpaper();
@@ -389,6 +432,15 @@
         return mHomeAppToken.windowsCanBeWallpaperTarget();
     }
 
+    boolean isAnimatingTask(Task task) {
+        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+            if (task == mPendingAnimations.get(i).mTask) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private boolean isAnimatingApp(AppWindowToken appToken) {
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final Task task = mPendingAnimations.get(i).mTask;
@@ -409,18 +461,18 @@
         private OnAnimationFinishedCallback mCapturedFinishCallback;
         private final boolean mIsRecentTaskInvisible;
         private RemoteAnimationTarget mTarget;
+        private final Point mPosition = new Point();
+        private final Rect mBounds = new Rect();
 
         TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
             mTask = task;
             mIsRecentTaskInvisible = isRecentTaskInvisible;
+            final WindowContainer container = mTask.getParent();
+            container.getRelativePosition(mPosition);
+            container.getBounds(mBounds);
         }
 
         RemoteAnimationTarget createRemoteAnimationApp() {
-            final Point position = new Point();
-            final Rect bounds = new Rect();
-            final WindowContainer container = mTask.getParent();
-            container.getRelativePosition(position);
-            container.getBounds(bounds);
             final WindowState mainWindow = mTask.getTopVisibleAppMainWindow();
             if (mainWindow == null) {
                 return null;
@@ -429,7 +481,7 @@
             InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
             mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
                     !mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
-                    insets, mTask.getPrefixOrderIndex(), position, bounds,
+                    insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
                     mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
             return mTarget;
         }
@@ -452,13 +504,14 @@
         @Override
         public void startAnimation(SurfaceControl animationLeash, Transaction t,
                 OnAnimationFinishedCallback finishCallback) {
+            t.setPosition(animationLeash, mPosition.x, mPosition.y);
             mCapturedLeash = animationLeash;
             mCapturedFinishCallback = finishCallback;
         }
 
         @Override
         public void onAnimationCancelled(SurfaceControl animationLeash) {
-            cancelAnimation();
+            cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e8d3210..95223d8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -44,6 +44,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
+import android.view.SurfaceControl;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
@@ -559,6 +560,23 @@
                 && !mStack.isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
     }
 
+    @Override
+    public SurfaceControl getAnimationLeashParent() {
+        // Reparent to the animation layer so that we aren't clipped by the non-minimized
+        // stack bounds, currently we only animate the task for the recents animation
+        return getAppAnimationLayer(false /* boosted */);
+    }
+
+    boolean isTaskAnimating() {
+        final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
+        if (recentsAnim != null) {
+            if (recentsAnim.isAnimatingTask(this)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     WindowState getTopVisibleAppMainWindow() {
         final AppWindowToken token = getTopVisibleAppToken();
         return token != null ? token.findMainWindow() : null;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 62754ad..2175c6b 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -33,8 +33,6 @@
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
 import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
@@ -47,6 +45,8 @@
 import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
 import static com.android.server.wm.StackProto.TASKS;
 import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
@@ -62,12 +62,10 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.SurfaceControl;
-
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.EventLogTags;
-
 import java.io.PrintWriter;
 
 public class TaskStack extends WindowContainer<Task> implements
@@ -1333,6 +1331,20 @@
         return mMinimizeAmount != 0f;
     }
 
+    /**
+     * @return {@code true} if we have a {@link Task} that is animating (currently only used for the
+     *         recents animation); {@code false} otherwise.
+     */
+    boolean isTaskAnimating() {
+        for (int j = mChildren.size() - 1; j >= 0; j--) {
+            final Task task = mChildren.get(j);
+            if (task.isTaskAnimating()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @CallSuper
     @Override
     public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
@@ -1687,6 +1699,25 @@
         }
     }
 
+    @Override
+    public boolean shouldDeferStartOnMoveToFullscreen() {
+        // Workaround for the recents animation -- normally we need to wait for the new activity to
+        // show before starting the PiP animation, but because we start and show the home activity
+        // early for the recents animation prior to the PiP animation starting, there is no
+        // subsequent all-drawn signal. In this case, we can skip the pause when the home stack is
+        // already visible and drawn.
+        final TaskStack homeStack = mDisplayContent.getHomeStack();
+        if (homeStack == null) {
+            return true;
+        }
+        final Task homeTask = homeStack.getTopChild();
+        final AppWindowToken homeApp = homeTask.getTopVisibleAppToken();
+        if (!homeTask.isVisible() || homeApp == null) {
+            return true;
+        }
+        return !homeApp.allDrawn;
+    }
+
     /**
      * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
      *         bounds and we have a deferred PiP mode changed callback set with the animation.
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index c509980..6de1579 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -26,6 +26,7 @@
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_ORIGINAL_POSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
@@ -618,7 +619,8 @@
 
             // If there was a recents animation in progress, cancel that animation
             if (mService.getRecentsAnimationController() != null) {
-                mService.getRecentsAnimationController().cancelAnimation();
+                mService.getRecentsAnimationController().cancelAnimation(
+                        REORDER_MOVE_HOME_TO_ORIGINAL_POSITION);
             }
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f1cd46b..28a57f5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2687,20 +2687,20 @@
         }
     }
 
-    public void cancelRecentsAnimation() {
+    public void cancelRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
         // Note: Do not hold the WM lock, this will lock appropriately in the call which also
         // calls through to AM/RecentsAnimation.onAnimationFinished()
         if (mRecentsAnimationController != null) {
             // This call will call through to cleanupAnimation() below after the animation is
             // canceled
-            mRecentsAnimationController.cancelAnimation();
+            mRecentsAnimationController.cancelAnimation(reorderMode);
         }
     }
 
-    public void cleanupRecentsAnimation(boolean moveHomeToTop) {
+    public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
         synchronized (mWindowMap) {
             if (mRecentsAnimationController != null) {
-                mRecentsAnimationController.cleanupAnimation(moveHomeToTop);
+                mRecentsAnimationController.cleanupAnimation(reorderMode);
                 mRecentsAnimationController = null;
                 mAppTransition.updateBooster();
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 54c2e9b..f015889 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -178,6 +178,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
@@ -1366,6 +1367,7 @@
         // Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
         if (dc != null) {
             mLayoutSeq = dc.mLayoutSeq - 1;
+            mInputWindowHandle.displayId = dc.getDisplayId();
         }
     }
 
@@ -1378,7 +1380,7 @@
     public int getDisplayId() {
         final DisplayContent displayContent = getDisplayContent();
         if (displayContent == null) {
-            return -1;
+            return Display.INVALID_DISPLAY;
         }
         return displayContent.getDisplayId();
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 7765387..6290ff1 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -649,7 +649,7 @@
         assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
         assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
                 null));
-        assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation());
+        assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
     }
 
     private void testGetTasksApis(boolean expectCallable) {
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 92cc537..45bee6e 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -30,9 +30,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ParceledListSlice;
 import android.database.ContentObserver;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
+import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
 import android.os.BatteryManager;
 import android.os.Handler;
@@ -571,6 +573,19 @@
         assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
     }
 
+    @Test
+    public void testNonNullAmbientStats() {
+        // getAmbientBrightnessStats should return an empty list rather than null when
+        // tracker isn't started or hasn't collected any data.
+        ParceledListSlice<AmbientBrightnessDayStats> slice = mTracker.getAmbientBrightnessStats(0);
+        assertNotNull(slice);
+        assertTrue(slice.getList().isEmpty());
+        startTracker(mTracker);
+        slice = mTracker.getAmbientBrightnessStats(0);
+        assertNotNull(slice);
+        assertTrue(slice.getList().isEmpty());
+    }
+
     private InputStream getInputStream(String data) {
         return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
     }
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
index 654acc2..678f018 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
@@ -114,13 +115,19 @@
     }
 
     @Test
-    public void testWatchlistConfig_getWatchlistConfigHash() throws Exception {
+    public void testWatchlistConfig_getWatchlistConfigHash_hasConfig() throws Exception {
         copyWatchlistConfigXml(mContext, TEST_XML_1, mTestXmlFile);
         WatchlistConfig config = new WatchlistConfig(mTestXmlFile);
         assertEquals(TEST_XML_1_HASH, HexDump.toHexString(config.getWatchlistConfigHash()));
     }
 
     @Test
+    public void testWatchlistConfig_getWatchlistConfigHash_withoutConfig() throws Exception {
+        WatchlistConfig config = new WatchlistConfig(mTestXmlFile);
+        assertNull(config.getWatchlistConfigHash());
+    }
+
+    @Test
     public void testWatchlistConfig_testDumpDoesNotCrash() throws Exception {
         WatchlistConfig config = new WatchlistConfig(new File("/not_exist_path.xml"));
         ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 2bfe274..6019958 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -159,6 +159,11 @@
         }
 
         @Override
+        public boolean shouldDeferStartOnMoveToFullscreen() {
+            return true;
+        }
+
+        @Override
         public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) {
             // TODO: Once we break the runs apart, we should fail() here if this is called outside
             //       of onAnimationStart() and onAnimationEnd()
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 1248eae..7c928c9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -326,6 +326,17 @@
         assertThat(app.mLayoutSeq, not(is(mDisplayContent.mLayoutSeq)));
     }
 
+    @Test
+    public void testDisplayIdUpdatedOnReparent() throws Exception {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        // fake a different display
+        app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1;
+        app.onDisplayChanged(mDisplayContent);
+
+        assertThat(app.mInputWindowHandle.displayId, is(mDisplayContent.getDisplayId()));
+        assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId()));
+    }
+
     private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
         reset(mPowerManagerWrapper);
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
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 689c2ce..a8b9dff 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -33,7 +33,6 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
-import android.util.Slog;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -108,14 +107,8 @@
     }
 
     @Test
-    public void testXmlUpgrade() throws Exception {
-        String xml = "<enabled_assistants/>";
-
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(
-                new ByteArrayInputStream(xml.toString().getBytes())), null);
-        parser.nextTag();
-        mAssistants.readXml(parser);
+    public void testXmlUpgrade() {
+        mAssistants.ensureAssistant();
 
         //once per user
         verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
@@ -133,42 +126,8 @@
         parser.nextTag();
         mAssistants.readXml(parser);
 
-        // once per user
-        verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+        verify(mNm, never()).readDefaultAssistant(anyInt());
         verify(mAssistants, times(1)).addApprovedList(
                 new ComponentName("b", "b").flattenToString(),10, true);
     }
-
-    @Test
-    public void testXmlUpgradeOnce() throws Exception {
-        String xml = "<enabled_assistants/>";
-
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(
-                new ByteArrayInputStream(xml.toString().getBytes())), null);
-        parser.nextTag();
-        mAssistants.readXml(parser);
-
-        XmlSerializer serializer = new FastXmlSerializer();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
-        serializer.startDocument(null, true);
-        mAssistants.writeXml(serializer, true);
-        serializer.endDocument();
-        serializer.flush();
-
-        //once per user
-        verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
-
-        Mockito.reset(mNm);
-
-        parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(
-                new ByteArrayInputStream(baos.toByteArray())), null);
-        parser.nextTag();
-        mAssistants.readXml(parser);
-
-        //once per user
-        verify(mNm, never()).readDefaultAssistant(anyInt());
-    }
 }
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 b82becd..124cb42 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2184,7 +2184,7 @@
 
     @Test
     public void testReadPolicyXml_readApprovedServicesFromXml() throws Exception {
-        final String preupgradeXml = "<notification-policy version=\"1\">"
+        final String upgradeXml = "<notification-policy version=\"1\">"
                 + "<zen></zen>"
                 + "<ranking></ranking>"
                 + "<enabled_listeners>"
@@ -2198,7 +2198,7 @@
                 + "</dnd_apps>"
                 + "</notification-policy>";
         mService.readPolicyXml(
-                new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false);
+                new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false);
         verify(mListeners, times(1)).readXml(any());
         verify(mConditionProviders, times(1)).readXml(any());
         verify(mAssistants, times(1)).readXml(any());
@@ -2207,6 +2207,7 @@
         verify(mListeners, times(1)).migrateToXml();
         verify(mConditionProviders, times(1)).migrateToXml();
         verify(mAssistants, times(1)).migrateToXml();
+        verify(mAssistants, times(2)).ensureAssistant();
     }
 
     @Test
@@ -2225,6 +2226,7 @@
         verify(mListeners, times(2)).migrateToXml();
         verify(mConditionProviders, times(2)).migrateToXml();
         verify(mAssistants, times(2)).migrateToXml();
+        verify(mAssistants, times(2)).ensureAssistant();
     }
 
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 0815876..54ed1e6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -1093,7 +1093,7 @@
         NotificationChannel channel2 =
                 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
         NotificationChannel channel3 =
-                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
+                new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_HIGH);
         mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
         mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
@@ -1106,6 +1106,24 @@
     }
 
     @Test
+    public void testGetBlockedChannelCount() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        NotificationChannel channel2 =
+                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_NONE);
+        NotificationChannel channel3 =
+                new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_NONE);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+
+        mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
+
+        assertEquals(1, mHelper.getBlockedChannelCount(PKG, UID));
+        assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID2));
+    }
+
+    @Test
     public void testCreateDeletedChannel() throws Exception {
         long[] vibration = new long[]{100, 67, 145, 156};
         NotificationChannel channel =
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 36333e4..3bf951d 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2600,7 +2600,6 @@
     }
 
     /**
-     *
      * Request audio routing to a specific bluetooth device. Calling this method may result in
      * the device routing audio to a different bluetooth device than the one specified if the
      * bluetooth stack is unable to route audio to the requested device.
@@ -2611,13 +2610,13 @@
      * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
      * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
      * <p>
-     * See also {@link InCallService#requestBluetoothAudio(String)}
-     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
-     *                         {@link BluetoothDevice#getAddress()}.
+     * See also {@link InCallService#requestBluetoothAudio(BluetoothDevice)}
+     * @param bluetoothDevice The bluetooth device to connect to.
      */
-    public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+    public void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
         for (Listener l : mListeners) {
-            l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
+            l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH,
+                    bluetoothDevice.getAddress());
         }
     }
 
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index af65c65..bd25ab2 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -428,12 +428,11 @@
      * A list of available devices can be obtained via
      * {@link CallAudioState#getSupportedBluetoothDevices()}
      *
-     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
-     *                         {@link BluetoothDevice#getAddress()}.
+     * @param bluetoothDevice The bluetooth device to connect to.
      */
-    public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+    public final void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
         if (mPhone != null) {
-            mPhone.requestBluetoothAudio(bluetoothAddress);
+            mPhone.requestBluetoothAudio(bluetoothDevice.getAddress());
         }
     }
 
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 970596d..a946e50 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.test.mock.MockContext;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.system.Os;
@@ -69,11 +70,17 @@
 
     private IpSecService mMockIpSecService;
     private IpSecManager mIpSecManager;
+    private MockContext mMockContext = new MockContext() {
+        @Override
+        public String getOpPackageName() {
+            return "fooPackage";
+        }
+    };
 
     @Before
     public void setUp() throws Exception {
         mMockIpSecService = mock(IpSecService.class);
-        mIpSecManager = new IpSecManager(mMockIpSecService);
+        mIpSecManager = new IpSecManager(mMockContext, mMockIpSecService);
     }
 
     /*
@@ -227,7 +234,7 @@
                 new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
         when(mMockIpSecService.createTunnelInterface(
                 eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()),
-                anyObject(), anyObject()))
+                anyObject(), anyObject(), anyString()))
                         .thenReturn(dummyResponse);
 
         IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface(
@@ -245,7 +252,7 @@
         assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName());
 
         tunnelIntf.close();
-        verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID));
+        verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID), anyString());
     }
 
     @Test
@@ -255,10 +262,12 @@
 
         tunnelIntf.addAddress(VTI_INNER_ADDRESS);
         verify(mMockIpSecService)
-                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+                .addAddressToTunnelInterface(
+                        eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString());
 
         tunnelIntf.removeAddress(VTI_INNER_ADDRESS);
         verify(mMockIpSecService)
-                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+                .addAddressToTunnelInterface(
+                        eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString());
     }
-}
\ No newline at end of file
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 410f754..e573d35 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.net.INetd;
 import android.net.IpSecAlgorithm;
@@ -40,6 +41,7 @@
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
+import android.test.mock.MockContext;
 import android.support.test.filters.SmallTest;
 import android.system.Os;
 
@@ -92,7 +94,28 @@
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
     };
 
-    Context mMockContext;
+    AppOpsManager mMockAppOps = mock(AppOpsManager.class);
+
+    MockContext mMockContext = new MockContext() {
+        @Override
+        public Object getSystemService(String name) {
+            switch(name) {
+                case Context.APP_OPS_SERVICE:
+                    return mMockAppOps;
+                default:
+                    return null;
+            }
+        }
+
+        @Override
+        public void enforceCallingOrSelfPermission(String permission, String message) {
+            if (permission == android.Manifest.permission.MANAGE_IPSEC_TUNNELS) {
+                return;
+            }
+            throw new SecurityException("Unavailable permission requested");
+        }
+    };
+
     INetd mMockNetd;
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
@@ -114,13 +137,22 @@
 
     @Before
     public void setUp() throws Exception {
-        mMockContext = mock(Context.class);
         mMockNetd = mock(INetd.class);
         mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
         mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
 
         // Injecting mock netd
         when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+        // A package granted the AppOp for MANAGE_IPSEC_TUNNELS will be MODE_ALLOWED.
+        when(mMockAppOps.noteOp(anyInt(), anyInt(), eq("blessedPackage")))
+            .thenReturn(AppOpsManager.MODE_ALLOWED);
+        // A system package will not be granted the app op, so this should fall back to
+        // a permissions check, which should pass.
+        when(mMockAppOps.noteOp(anyInt(), anyInt(), eq("systemPackage")))
+            .thenReturn(AppOpsManager.MODE_DEFAULT);
+        // A mismatch between the package name and the UID will return MODE_IGNORED.
+        when(mMockAppOps.noteOp(anyInt(), anyInt(), eq("badPackage")))
+            .thenReturn(AppOpsManager.MODE_IGNORED);
     }
 
     @Test
@@ -232,7 +264,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
         verify(mMockNetd)
@@ -267,7 +299,7 @@
         ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
         verify(mMockNetd)
@@ -301,12 +333,12 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
         // Attempting to create transform a second time with the same SPIs should throw an error...
         try {
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
                 fail("IpSecService should have thrown an error for reuse of SPI");
         } catch (IllegalStateException expected) {
         }
@@ -314,7 +346,7 @@
         // ... even if the transform is deleted
         mIpSecService.deleteTransform(createTransformResp.resourceId);
         try {
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
                 fail("IpSecService should have thrown an error for reuse of SPI");
         } catch (IllegalStateException expected) {
         }
@@ -327,7 +359,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
@@ -351,7 +383,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         mIpSecService.deleteTransform(createTransformResp.resourceId);
 
         verify(mMockNetd, times(1))
@@ -398,7 +430,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -435,7 +467,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
 
         int resourceId = createTransformResp.resourceId;
@@ -460,10 +492,10 @@
     }
 
     private IpSecTunnelInterfaceResponse createAndValidateTunnel(
-            String localAddr, String remoteAddr) {
+            String localAddr, String remoteAddr, String pkgName) {
         IpSecTunnelInterfaceResponse createTunnelResp =
                 mIpSecService.createTunnelInterface(
-                        mSourceAddr, mDestinationAddr, fakeNetwork, new Binder());
+                        mSourceAddr, mDestinationAddr, fakeNetwork, new Binder(), pkgName);
 
         assertNotNull(createTunnelResp);
         assertEquals(IpSecManager.Status.OK, createTunnelResp.status);
@@ -473,7 +505,7 @@
     @Test
     public void testCreateTunnelInterface() throws Exception {
         IpSecTunnelInterfaceResponse createTunnelResp =
-                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
 
         // Check that we have stored the tracking object, and retrieve it
         IpSecService.UserRecord userRecord =
@@ -495,12 +527,12 @@
     @Test
     public void testDeleteTunnelInterface() throws Exception {
         IpSecTunnelInterfaceResponse createTunnelResp =
-                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
 
-        mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId);
+        mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, "blessedPackage");
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
@@ -516,7 +548,7 @@
     @Test
     public void testTunnelInterfaceBinderDeath() throws Exception {
         IpSecTunnelInterfaceResponse createTunnelResp =
-                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -539,22 +571,34 @@
 
     @Test
     public void testAddRemoveAddressFromTunnelInterface() throws Exception {
-        IpSecTunnelInterfaceResponse createTunnelResp =
-                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+        for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) {
+            IpSecTunnelInterfaceResponse createTunnelResp =
+                    createAndValidateTunnel(mSourceAddr, mDestinationAddr, pkgName);
+            mIpSecService.addAddressToTunnelInterface(
+                    createTunnelResp.resourceId, mLocalInnerAddress, pkgName);
+            verify(mMockNetd, times(1))
+                    .interfaceAddAddress(
+                            eq(createTunnelResp.interfaceName),
+                            eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                            eq(mLocalInnerAddress.getPrefixLength()));
+            mIpSecService.removeAddressFromTunnelInterface(
+                    createTunnelResp.resourceId, mLocalInnerAddress, pkgName);
+            verify(mMockNetd, times(1))
+                    .interfaceDelAddress(
+                            eq(createTunnelResp.interfaceName),
+                            eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                            eq(mLocalInnerAddress.getPrefixLength()));
+            mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, pkgName);
+        }
+    }
 
-        mIpSecService.addAddressToTunnelInterface(createTunnelResp.resourceId, mLocalInnerAddress);
-        verify(mMockNetd)
-                .interfaceAddAddress(
-                        eq(createTunnelResp.interfaceName),
-                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
-                        eq(mLocalInnerAddress.getPrefixLength()));
-
-        mIpSecService.removeAddressFromTunnelInterface(
-                createTunnelResp.resourceId, mLocalInnerAddress);
-        verify(mMockNetd)
-                .interfaceDelAddress(
-                        eq(createTunnelResp.interfaceName),
-                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
-                        eq(mLocalInnerAddress.getPrefixLength()));
+    @Test
+    public void testAddTunnelFailsForBadPackageName() throws Exception {
+        try {
+            IpSecTunnelInterfaceResponse createTunnelResp =
+                    createAndValidateTunnel(mSourceAddr, mDestinationAddr, "badPackage");
+            fail("Expected a SecurityException for badPackage.");
+        } catch (SecurityException expected) {
+        }
     }
 }