Merge "null check on qspanel before called method on it." 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/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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1776eac..95e4f93 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -279,12 +279,12 @@
             }});
 
         registerService(Context.IPSEC_SERVICE, IpSecManager.class,
-                new StaticServiceFetcher<IpSecManager>() {
+                new CachedServiceFetcher<IpSecManager>() {
             @Override
-            public IpSecManager createService() {
+            public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE);
                 IIpSecService service = IIpSecService.Stub.asInterface(b);
-                return new IpSecManager(service);
+                return new IpSecManager(ctx, service);
             }});
 
         registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4124536..7ebe0f9 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -43,6 +43,9 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.RejectedExecutionException;
@@ -924,6 +927,37 @@
                     idCount++;
                 }
             }
+
+            // The sort logic must match the logic in
+            // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
+            Arrays.sort(cameraIds, new Comparator<String>() {
+                    @Override
+                    public int compare(String s1, String s2) {
+                        int s1Int = 0, s2Int = 0;
+                        try {
+                            s1Int = Integer.parseInt(s1);
+                        } catch (NumberFormatException e) {
+                            s1Int = -1;
+                        }
+
+                        try {
+                            s2Int = Integer.parseInt(s2);
+                        } catch (NumberFormatException e) {
+                            s2Int = -1;
+                        }
+
+                        // Uint device IDs first
+                        if (s1Int >= 0 && s2Int >= 0) {
+                            return s1Int - s2Int;
+                        } else if (s1Int >= 0) {
+                            return -1;
+                        } else if (s2Int >= 0) {
+                            return 1;
+                        } else {
+                            // Simple string compare if both id are not uint
+                            return s1.compareTo(s2);
+                        }
+                    }});
             return cameraIds;
         }
 
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 3a3ddcc..d6774d4 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -45,25 +45,31 @@
             in String localAddr,
             in String remoteAddr,
             in Network underlyingNetwork,
-            in IBinder binder);
+            in IBinder binder,
+            in String callingPackage);
 
     void addAddressToTunnelInterface(
             int tunnelResourceId,
-            in LinkAddress localAddr);
+            in LinkAddress localAddr,
+            in String callingPackage);
 
     void removeAddressFromTunnelInterface(
             int tunnelResourceId,
-            in LinkAddress localAddr);
+            in LinkAddress localAddr,
+            in String callingPackage);
 
-    void deleteTunnelInterface(int resourceId);
+    void deleteTunnelInterface(int resourceId, in String callingPackage);
 
-    IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder);
+    IpSecTransformResponse createTransform(
+            in IpSecConfig c, in IBinder binder, in String callingPackage);
 
     void deleteTransform(int transformId);
 
-    void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
+    void applyTransportModeTransform(
+            in ParcelFileDescriptor socket, int direction, int transformId);
 
-    void applyTunnelModeTransform(int tunnelResourceId, int direction, int transformResourceId);
+    void applyTunnelModeTransform(
+            int tunnelResourceId, int direction, int transformResourceId, in String callingPackage);
 
     void removeTransportModeTransforms(in ParcelFileDescriptor socket);
 }
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 1525508..e0654fd 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -140,6 +140,7 @@
         }
     }
 
+    private final Context mContext;
     private final IIpSecService mService;
 
     /**
@@ -661,6 +662,7 @@
      */
     @SystemApi
     public static final class IpSecTunnelInterface implements AutoCloseable {
+        private final String mOpPackageName;
         private final IIpSecService mService;
         private final InetAddress mRemoteAddress;
         private final InetAddress mLocalAddress;
@@ -688,7 +690,8 @@
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
         public void addAddress(@NonNull LinkAddress address) throws IOException {
             try {
-                mService.addAddressToTunnelInterface(mResourceId, address);
+                mService.addAddressToTunnelInterface(
+                        mResourceId, address, mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -706,16 +709,18 @@
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
         public void removeAddress(@NonNull LinkAddress address) throws IOException {
             try {
-                mService.removeAddressFromTunnelInterface(mResourceId, address);
+                mService.removeAddressFromTunnelInterface(
+                        mResourceId, address, mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
 
-        private IpSecTunnelInterface(@NonNull IIpSecService service,
+        private IpSecTunnelInterface(@NonNull Context ctx, @NonNull IIpSecService service,
                 @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
                 @NonNull Network underlyingNetwork)
                 throws ResourceUnavailableException, IOException {
+            mOpPackageName = ctx.getOpPackageName();
             mService = service;
             mLocalAddress = localAddress;
             mRemoteAddress = remoteAddress;
@@ -727,7 +732,8 @@
                                 localAddress.getHostAddress(),
                                 remoteAddress.getHostAddress(),
                                 underlyingNetwork,
-                                new Binder());
+                                new Binder(),
+                                mOpPackageName);
                 switch (result.status) {
                     case Status.OK:
                         break;
@@ -756,7 +762,7 @@
         @Override
         public void close() {
             try {
-                mService.deleteTunnelInterface(mResourceId);
+                mService.deleteTunnelInterface(mResourceId, mOpPackageName);
                 mResourceId = INVALID_RESOURCE_ID;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -801,7 +807,8 @@
     public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
             @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
             throws ResourceUnavailableException, IOException {
-        return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+        return new IpSecTunnelInterface(
+                mContext, mService, localAddress, remoteAddress, underlyingNetwork);
     }
 
     /**
@@ -827,7 +834,8 @@
             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         try {
             mService.applyTunnelModeTransform(
-                    tunnel.getResourceId(), direction, transform.getResourceId());
+                    tunnel.getResourceId(), direction,
+                    transform.getResourceId(), mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -839,7 +847,8 @@
      * @param context the application context for this manager
      * @hide
      */
-    public IpSecManager(IIpSecService service) {
+    public IpSecManager(Context ctx, IIpSecService service) {
+        mContext = ctx;
         mService = checkNotNull(service, "missing service");
     }
 }
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 099fe02..fb5f46c 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -130,7 +130,8 @@
         synchronized (this) {
             try {
                 IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
+                IpSecTransformResponse result = svc.createTransform(
+                        mConfig, new Binder(), mContext.getOpPackageName());
                 int status = result.status;
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index c8efede..5db3c1a 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Channel {
+public final class Channel implements java.nio.channels.Channel {
 
     private static final String TAG = "OMAPI.Channel";
     private Session mSession;
@@ -64,7 +64,7 @@
      * before closing the channel.
      */
     public void close() {
-        if (!isClosed()) {
+        if (isOpen()) {
             synchronized (mLock) {
                 try {
                     mChannel.close();
@@ -76,21 +76,21 @@
     }
 
     /**
-     * Tells if this channel is closed.
+     * Tells if this channel is open.
      *
-     * @return <code>true</code> if the channel is closed or in case of an error.
-     *         <code>false</code> otherwise.
+     * @return <code>false</code> if the channel is closed or in case of an error.
+     *         <code>true</code> otherwise.
      */
-    public boolean isClosed() {
+    public boolean isOpen() {
         if (!mService.isConnected()) {
             Log.e(TAG, "service not connected to system");
-            return true;
+            return false;
         }
         try {
-            return mChannel.isClosed();
+            return !mChannel.isClosed();
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in isClosed()");
-            return true;
+            return false;
         }
     }
 
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 9be3da6..80262f7 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -37,7 +37,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Reader {
+public final class Reader {
 
     private static final String TAG = "OMAPI.Reader";
     private final String mName;
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index 311dc4c..14727f0 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.concurrent.Executor;
 
 /**
  * The SEService realises the communication to available Secure Elements on the
@@ -40,7 +41,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class SEService {
+public final class SEService {
 
     /**
      * Error code used with ServiceSpecificException.
@@ -62,11 +63,11 @@
     /**
      * Interface to send call-backs to the application when the service is connected.
      */
-    public interface SecureElementListener {
+    public interface OnConnectedListener {
         /**
          * Called by the framework when the service is connected.
          */
-        void onServiceConnected();
+        void onConnected();
     }
 
     /**
@@ -74,16 +75,22 @@
      * SEService could be bound to the backend.
      */
     private class SEListener extends ISecureElementListener.Stub {
-        public SecureElementListener mListener = null;
+        public OnConnectedListener mListener = null;
+        public Executor mExecutor = null;
 
         @Override
         public IBinder asBinder() {
             return this;
         }
 
-        public void onServiceConnected() {
-            if (mListener != null) {
-                mListener.onServiceConnected();
+        public void onConnected() {
+            if (mListener != null && mExecutor != null) {
+                mExecutor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        mListener.onConnected();
+                    }
+                });
             }
         }
     }
@@ -116,22 +123,26 @@
      * the specified listener is called or if isConnected() returns
      * <code>true</code>. <br>
      * The call-back object passed as a parameter will have its
-     * onServiceConnected() method called when the connection actually happen.
+     * onConnected() method called when the connection actually happen.
      *
      * @param context
      *            the context of the calling application. Cannot be
      *            <code>null</code>.
      * @param listener
-     *            a SecureElementListener object.
+     *            a OnConnectedListener object.
+     * @param executor
+     *            an Executor which will be used when invoking the callback.
      */
-    public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
+    public SEService(@NonNull Context context, @NonNull Executor executor,
+            @NonNull OnConnectedListener listener) {
 
-        if (context == null) {
-            throw new NullPointerException("context must not be null");
+        if (context == null || listener == null || executor == null) {
+            throw new NullPointerException("Arguments must not be null");
         }
 
         mContext = context;
         mSEListener.mListener = listener;
+        mSEListener.mExecutor = executor;
 
         mConnection = new ServiceConnection() {
 
@@ -140,7 +151,7 @@
 
                 mSecureElementService = ISecureElementService.Stub.asInterface(service);
                 if (mSEListener != null) {
-                    mSEListener.onServiceConnected();
+                    mSEListener.onConnected();
                 }
                 Log.i(TAG, "Service onServiceConnected");
             }
@@ -171,12 +182,12 @@
     }
 
     /**
-     * Returns the list of available Secure Element readers.
+     * Returns an array of available Secure Element readers.
      * There must be no duplicated objects in the returned list.
      * All available readers shall be listed even if no card is inserted.
      *
-     * @return The readers list, as an array of Readers. If there are no
-     * readers the returned array is of length 0.
+     * @return An array of Readers. If there are no readers the returned array
+     * is of length 0.
      */
     public @NonNull Reader[] getReaders() {
         if (mSecureElementService == null) {
@@ -212,7 +223,8 @@
      * (including any binding to an underlying service).
      * As a result isConnected() will return false after shutdown() was called.
      * After this method call, the SEService object is not connected.
-     * It is recommended to call this method in the termination method of the calling application
+     * This method should be called when connection to the Secure Element is not needed
+     * or in the termination method of the calling application
      * (or part of this application) which is bound to this SEService.
      */
     public void shutdown() {
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index adfeddd..d5f8c82 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class Session {
+public final class Session {
 
     private final Object mLock = new Object();
     private final SEService mService;
@@ -225,6 +225,32 @@
     }
 
     /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openBasicChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array, or null if no Applet is to be selected.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element session is used after
+     *             being closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+     *             selected.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device
+     * @return an instance of Channel if available or null.
+     */
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid) throws IOException {
+        return openBasicChannel(aid, (byte) 0x00);
+    }
+
+    /**
      * Open a logical channel with the Secure Element, selecting the Applet represented by
      * the given AID. If the AID is null, which means no Applet is to be selected on this
      * channel, the default Applet is used. It's up to the Secure Element to choose which
@@ -304,4 +330,32 @@
             }
         }
     }
+
+    /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openLogicalChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element is used after being
+     *             closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not
+     *             available or cannot be selected or a logical channel is already
+     *             open to a non-multiselectable Applet.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device.
+     * @return an instance of Channel. Null if the Secure Element is unable to
+     *         provide a new logical channel.
+     */
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid) throws IOException {
+        return openLogicalChannel(aid, (byte) 0x00);
+    }
 }
diff --git a/core/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/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index a4a1b94..95aff9a 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -55,6 +55,7 @@
   <hidden-api-whitelisted-app package="com.android.companiondevicemanager" />
   <hidden-api-whitelisted-app package="com.android.customlocale2" />
   <hidden-api-whitelisted-app package="com.android.defcontainer" />
+  <hidden-api-whitelisted-app package="com.android.development" />
   <hidden-api-whitelisted-app package="com.android.documentsui" />
   <hidden-api-whitelisted-app package="com.android.dreams.basic" />
   <hidden-api-whitelisted-app package="com.android.egg" />
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 32d4c77..3ca9295 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -151,7 +151,12 @@
             mGrContext->freeGpuResources();
             break;
         case TrimMemoryMode::UiHidden:
-            mGrContext->purgeUnlockedResources(mMaxResourceBytes - mBackgroundResourceBytes, true);
+            // Here we purge all the unlocked scratch resources and then toggle the resources cache
+            // limits between the background and max amounts. This causes the unlocked resources
+            // that have persistent data to be purged in LRU order.
+            mGrContext->purgeUnlockedResources(true);
+            mGrContext->setResourceCacheLimits(mMaxResources, mBackgroundResourceBytes);
+            mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
             break;
     }
 }
@@ -161,7 +166,10 @@
         return;
     }
     mGrContext->flush();
-    mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
+    // Here we purge all the unlocked scratch resources (leaving those resources w/ persistent data)
+    // and then purge those w/ persistent data based on age.
+    mGrContext->purgeUnlockedResources(true);
+    mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(10));
 }
 
 sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() {
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index b1106f0..c235715 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -20,6 +20,8 @@
 #include "renderthread/EglManager.h"
 #include "tests/common/TestUtils.h"
 
+#include <SkImagePriv.h>
+
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
@@ -49,7 +51,12 @@
         surfaces.push_back(surface);
     }
 
-    ASSERT_TRUE(1 < surfaces.size());
+    // create an image and pin it so that we have something with a unique key in the cache
+    sk_sp<Bitmap> bitmap =
+            Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h));
+    sk_sp<SkColorFilter> filter;
+    sk_sp<SkImage> image = bitmap->makeImage(&filter);
+    ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
 
     // attempt to trim all memory while we still hold strong refs
     renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
@@ -61,11 +68,14 @@
         surfaces[i].reset();
     }
 
+    // unpin the image which should add a unique purgeable key to the cache
+    SkImage_unpinAsTexture(image.get(), grContext);
+
     // verify that we have enough purgeable bytes
     const size_t purgeableBytes = grContext->getResourceCachePurgeableBytes();
     ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
 
-    // UI hidden and make sure only some got purged
+    // UI hidden and make sure only some got purged (unique should remain)
     renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
     ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
     ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
diff --git a/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/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/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/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/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f216695..4c4eb60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -124,7 +124,7 @@
     private TintedKeyButtonDrawable mRotateSuggestionIcon;
 
     private GestureHelper mGestureHelper;
-    private DeadZone mDeadZone;
+    private final DeadZone mDeadZone;
     private final NavigationBarTransitions mBarTransitions;
     private final OverviewProxyService mOverviewProxyService;
 
@@ -263,6 +263,7 @@
                 new ButtonDispatcher(R.id.accessibility_button));
         mButtonDispatchers.put(R.id.rotate_suggestion,
                 new ButtonDispatcher(R.id.rotate_suggestion));
+        mDeadZone = new DeadZone(this);
     }
 
     public BarTransitions getBarTransitions() {
@@ -297,6 +298,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mDeadZone.onTouchEvent(event)) {
+            // Consumed the touch event
+            return true;
+        }
         switch (event.getActionMasked()) {
             case ACTION_DOWN:
                 int x = (int) event.getX();
@@ -319,6 +324,10 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (mDeadZone.onTouchEvent(event)) {
+            // Consumed the touch event
+            return true;
+        }
         if (mGestureHelper.onTouchEvent(event)) {
             return true;
         }
@@ -389,7 +398,7 @@
 
     public boolean isQuickScrubEnabled() {
         return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
-                && mOverviewProxyService.getProxy() != null && isOverviewEnabled()
+                && mOverviewProxyService.isEnabled() && isOverviewEnabled()
                 && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0);
     }
 
@@ -587,7 +596,7 @@
         // recents buttons when disconnected from launcher service in screen pinning mode,
         // as they are used for exiting.
         final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
-        if (mOverviewProxyService.getProxy() != null) {
+        if (mOverviewProxyService.isEnabled()) {
             // Use interaction flags to show/hide navigation buttons but will be shown if required
             // to exit screen pinning.
             final int flags = mOverviewProxyService.getInteractionFlags();
@@ -818,6 +827,7 @@
     @Override
     protected void onDraw(Canvas canvas) {
         mGestureHelper.onDraw(canvas);
+        mDeadZone.onDraw(canvas);
         super.onDraw(canvas);
     }
 
@@ -889,10 +899,8 @@
     public void reorient() {
         updateCurrentView();
 
-        mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
-
         ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
-        mDeadZone.setDisplayRotation(mCurrentRotation);
+        mDeadZone.onConfigurationChanged(mCurrentRotation);
 
         // force the low profile & disabled states into compliance
         mBarTransitions.init();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 04cb620..304a499 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -488,7 +488,7 @@
                 mUpdateFlingVelocity = vel;
             }
         } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
-                && !mStatusBar.isBouncerShowing()) {
+                && !mStatusBar.isBouncerShowing() && !mStatusBar.isKeyguardFadingAway()) {
             long timePassed = SystemClock.uptimeMillis() - mDownTime;
             if (timePassed < ViewConfiguration.getLongPressTimeout()) {
                 // Lets show the user that he can actually expand the panel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/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/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/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/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/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/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/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) {
+        }
     }
 }