Merge "Fix "auto battery saver" confirmation dialog button layout" into pi-dev
diff --git a/api/current.txt b/api/current.txt
index 83092f6..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";
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/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/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/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..04c4130 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4248,6 +4248,11 @@
                   android:exported="false">
         </receiver>
 
+        <receiver android:name="com.android.server.stats.StatsCompanionService$PeriodicAlarmReceiver"
+                  android:permission="android.permission.STATSCOMPANION"
+                  android:exported="false">
+        </receiver>
+
         <receiver android:name="com.android.server.am.BatteryStatsService$UsbConnectionReceiver"
                   android:exported="false">
             <intent-filter>
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/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/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 3c9dae0..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));
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/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..af77804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -168,8 +168,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;
         }
@@ -199,7 +199,7 @@
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
-                if (mQuickStepStarted || !mAllowGestureDetection){
+                if (mQuickStepStarted || !mAllowGestureDetection || mHomeButtonView == null){
                     break;
                 }
                 int x = (int) event.getX();
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/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..44c2757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -200,7 +200,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 +226,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);
@@ -255,7 +255,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);
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/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/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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f31ca0a..899d271 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2286,6 +2286,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();
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/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/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/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/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/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) {
+        }
     }
 }