Merge "idmap2: fix clang-tidy warnings [modernize-*]"
diff --git a/api/current.txt b/api/current.txt
index d4aa34c..bf3a1f4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11302,6 +11302,7 @@
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
     field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED";
     field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+    field public static final java.lang.String ACTION_SESSION_UPDATED = "android.content.pm.action.SESSION_UPDATED";
     field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
     field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION";
diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h
index 784c181..a6074e7 100644
--- a/cmds/incident_helper/src/TextParserBase.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -30,7 +30,7 @@
 public:
     String8 name;
 
-    TextParserBase(String8 name) : name(name) {};
+    explicit TextParserBase(String8 name) : name(name) {};
     virtual ~TextParserBase() {};
 
     virtual status_t Parse(const int in, const int out) const = 0;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index c02a349..09dc8e6 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -109,7 +109,7 @@
 class Reader
 {
 public:
-    Reader(const int fd);
+    explicit Reader(const int fd);
     ~Reader();
 
     bool readLine(std::string* line);
@@ -162,7 +162,7 @@
 class Message
 {
 public:
-    Message(Table* table);
+    explicit Message(Table* table);
     ~Message();
 
     // Reconstructs the typical proto message by adding its message fields.
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index 6252ad2..c63a183 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -97,7 +97,7 @@
 // ================================================================================
 class IncidentService : public BnIncidentManager {
 public:
-    IncidentService(const sp<Looper>& handlerLooper);
+    explicit IncidentService(const sp<Looper>& handlerLooper);
     virtual ~IncidentService();
 
     virtual Status reportIncident(const IncidentReportArgs& args);
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index a3df490..a0159d9 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -83,7 +83,7 @@
     static PrivacySpec new_spec(int dest);
 
 private:
-    PrivacySpec(uint8_t dest) : dest(dest) {}
+    explicit PrivacySpec(uint8_t dest) : dest(dest) {}
 };
 
 }  // namespace incidentd
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 45fd944..2a3abd7 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -89,7 +89,7 @@
     ReportRequestSet batch;
 
     Reporter();                       // PROD must use this constructor.
-    Reporter(const char* directory);  // For testing purpose only.
+    explicit Reporter(const char* directory);  // For testing purpose only.
     virtual ~Reporter();
 
     // Run the report as described in the batch and args parameters.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 10d2268..32ec1ba 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -415,7 +415,7 @@
     bool workerDone;
     status_t workerError;
 
-    WorkerThreadData(const WorkerThreadSection* section);
+    explicit WorkerThreadData(const WorkerThreadSection* section);
     virtual ~WorkerThreadData();
 };
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 0114ff4..f0b751d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -216,6 +216,7 @@
         "tests/anomaly/AnomalyTracker_test.cpp",
         "tests/ConfigManager_test.cpp",
         "tests/external/puller_util_test.cpp",
+	"tests/external/StatsPuller_test.cpp",
         "tests/indexed_priority_queue_test.cpp",
         "tests/LogEntryMatcher_test.cpp",
         "tests/LogEvent_test.cpp",
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 50b64b9..f2a4663 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -704,7 +704,7 @@
 status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
     int s = atoi(args[1].c_str());
     vector<shared_ptr<LogEvent> > stats;
-    if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) {
+    if (mPullerManager->Pull(s, &stats)) {
         for (const auto& it : stats) {
             dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
         }
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 3badb1f..bca858e 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -42,7 +42,7 @@
  * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
  */
 struct InternalAlarm : public RefBase {
-    InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
+    explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
     }
 
     const uint32_t timestampSec;
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index a6f88af..2c88147 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -29,7 +29,7 @@
 class ConditionWizard : public virtual android::RefBase {
 public:
     ConditionWizard(){};  // for testing
-    ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
+    explicit ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
         : mAllConditions(conditionTrackers){};
 
     virtual ~ConditionWizard(){};
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
index dc79519..4cc9393 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -33,7 +33,7 @@
 class ConfigKey {
 public:
     ConfigKey();
-    explicit ConfigKey(const ConfigKey& that);
+    ConfigKey(const ConfigKey& that);
     ConfigKey(int uid, const int64_t& id);
     ~ConfigKey();
 
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/cmds/statsd/src/external/PowerStatsPuller.h
index dd5ff8f..6f15bd6 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.h
+++ b/cmds/statsd/src/external/PowerStatsPuller.h
@@ -28,6 +28,8 @@
 class PowerStatsPuller : public StatsPuller {
 public:
     PowerStatsPuller();
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
index 9b238eaf5..f650fcc 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.h
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -28,7 +28,9 @@
  */
 class ResourceHealthManagerPuller : public StatsPuller {
 public:
-    ResourceHealthManagerPuller(int tagId);
+    explicit ResourceHealthManagerPuller(int tagId);
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.h b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
index 13c675a..5313792 100644
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.h
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
@@ -29,6 +29,8 @@
 class ResourceThermalManagerPuller : public StatsPuller {
 public:
     ResourceThermalManagerPuller();
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.h b/cmds/statsd/src/external/StatsCompanionServicePuller.h
index 0a49732..2e13320 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.h
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.h
@@ -25,14 +25,14 @@
 
 class StatsCompanionServicePuller : public StatsPuller {
 public:
-    StatsCompanionServicePuller(int tagId);
-    bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
+    explicit StatsCompanionServicePuller(int tagId);
 
     void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) override;
 
 private:
     Mutex mStatsCompanionServiceLock;
     sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 7043d66..c7c22ee 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -34,48 +34,52 @@
 
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
-    // Pullers can cause significant impact to system health and battery.
-    // So that we don't pull too frequently.
-    mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
-    VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
 }
 
-bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
     lock_guard<std::mutex> lock(mLock);
-    int64_t wallClockTimeNs = getWallClockNs();
+    int64_t elapsedTimeNs = getElapsedRealtimeNs();
     StatsdStats::getInstance().notePull(mTagId);
-    if (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs) {
-        (*data) = mCachedData;
-        StatsdStats::getInstance().notePullFromCache(mTagId);
-        StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
-        return true;
+    const bool shouldUseCache = elapsedTimeNs - mLastPullTimeNs <
+                                StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs;
+    if (shouldUseCache) {
+        if (mHasGoodData) {
+            (*data) = mCachedData;
+            StatsdStats::getInstance().notePullFromCache(mTagId);
+        }
+        return mHasGoodData;
     }
-    if (mMinPullIntervalNs > elapsedTimeNs - mLastPullTimeNs) {
-        mMinPullIntervalNs = elapsedTimeNs - mLastPullTimeNs;
-        StatsdStats::getInstance().updateMinPullIntervalSec(mTagId,
-                                                            mMinPullIntervalNs / NS_PER_SEC);
+
+    if (mLastPullTimeNs > 0) {
+        StatsdStats::getInstance().updateMinPullIntervalSec(
+                mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
     }
     mCachedData.clear();
     mLastPullTimeNs = elapsedTimeNs;
-    int64_t pullStartTimeNs = getElapsedRealtimeNs();
-    bool ret = PullInternal(&mCachedData);
-    if (!ret) {
-        mCachedData.clear();
-        return false;
+    mHasGoodData = PullInternal(&mCachedData);
+    if (!mHasGoodData) {
+        return mHasGoodData;
     }
-    StatsdStats::getInstance().notePullTime(mTagId, getElapsedRealtimeNs() - pullStartTimeNs);
-    for (const shared_ptr<LogEvent>& data : mCachedData) {
-        data->setElapsedTimestampNs(elapsedTimeNs);
-        data->setLogdWallClockTimestampNs(wallClockTimeNs);
+    const int64_t pullDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
+    StatsdStats::getInstance().notePullTime(mTagId, pullDurationNs);
+    const bool pullTimeOut =
+            pullDurationNs > StatsPullerManager::kAllPullAtomInfo.at(mTagId).pullTimeoutNs;
+    if (pullTimeOut) {
+        // Something went wrong. Discard the data.
+        clearCacheLocked();
+        mHasGoodData = false;
+        StatsdStats::getInstance().notePullTimeout(mTagId);
+        ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
+              (long long)pullDurationNs);
+        return mHasGoodData;
     }
 
     if (mCachedData.size() > 0) {
         mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
-        (*data) = mCachedData;
     }
 
-    StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
-    return ret;
+    (*data) = mCachedData;
+    return mHasGoodData;
 }
 
 int StatsPuller::ForceClearCache() {
@@ -84,6 +88,10 @@
 
 int StatsPuller::clearCache() {
     lock_guard<std::mutex> lock(mLock);
+    return clearCacheLocked();
+}
+
+int StatsPuller::clearCacheLocked() {
     int ret = mCachedData.size();
     mCachedData.clear();
     mLastPullTimeNs = 0;
@@ -91,7 +99,8 @@
 }
 
 int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
-    if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
+    if (timestampNs - mLastPullTimeNs >
+        StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs) {
         return clearCache();
     } else {
         return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index cafd797..c83c4f8 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -18,7 +18,6 @@
 
 #include <android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
-#include <utils/String16.h>
 #include <mutex>
 #include <vector>
 #include "packages/UidMap.h"
@@ -33,14 +32,20 @@
 
 class StatsPuller : public virtual RefBase {
 public:
-    StatsPuller(const int tagId);
+    explicit StatsPuller(const int tagId);
 
     virtual ~StatsPuller() {}
 
-    // Pulls the data. The returned data will have elapsedTimeNs set as timeNs
-    // and will have wallClockTimeNs set as current wall clock time.
-    // Return true if the pull is successful.
-    bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
+    // Pulls the most recent data.
+    // The data may be served from cache if consecutive pulls come within
+    // predefined cooldown time.
+    // Returns true if the pull was successful.
+    // Returns false when
+    //   1) the pull fails
+    //   2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+    // If a metric wants to make any change to the data, like timestamps, it
+    // should make a copy as this data may be shared with multiple metrics.
+    bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear cache immediately
     int ForceClearCache();
@@ -53,29 +58,30 @@
     virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
 
 protected:
-    // The atom tag id this puller pulls
     const int mTagId;
 
 private:
     mutable std::mutex mLock;
-    // Minimum time before this puller does actual pull again.
-    // If a pull request comes before cooldown, a cached version from purevious pull
-    // will be returned.
-    // The actual value should be determined by individual pullers.
-    int64_t mCoolDownNs;
-    // For puller stats
-    int64_t mMinPullIntervalNs = LONG_MAX;
 
+    // Real puller impl.
     virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
 
-    // Cache of data from last pull. If next request comes before cool down finishes,
-    // cached data will be returned.
-    std::vector<std::shared_ptr<LogEvent>> mCachedData;
+    bool mHasGoodData = false;
 
     int64_t mLastPullTimeNs;
 
+    // Cache of data from last pull. If next request comes before cool down finishes,
+    // cached data will be returned.
+    // Cached data is cleared when
+    //   1) A pull fails
+    //   2) A new pull request comes after cooldown time.
+    //   3) clearCache is called.
+    std::vector<std::shared_ptr<LogEvent>> mCachedData;
+
     int clearCache();
 
+    int clearCacheLocked();
+
     static sp<UidMap> mUidMap;
 };
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index c070ca3..4a716cf 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -53,195 +53,172 @@
 const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
         // wifi_bytes_transfer
         {android::util::WIFI_BYTES_TRANSFER,
-         {{2, 3, 4, 5},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+         {.additiveFields = {2, 3, 4, 5},
+          .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
         // wifi_bytes_transfer_by_fg_bg
         {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
-         {{3, 4, 5, 6},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+         {.additiveFields = {3, 4, 5, 6},
+          .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
         // mobile_bytes_transfer
         {android::util::MOBILE_BYTES_TRANSFER,
-         {{2, 3, 4, 5},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+         {.additiveFields = {2, 3, 4, 5},
+          .puller = new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
         // mobile_bytes_transfer_by_fg_bg
         {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
-         {{3, 4, 5, 6},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+         {.additiveFields = {3, 4, 5, 6},
+          .puller =
+                  new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
         // bluetooth_bytes_transfer
         {android::util::BLUETOOTH_BYTES_TRANSFER,
-         {{2, 3},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+         {.additiveFields = {2, 3},
+          .puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
         // kernel_wakelock
         {android::util::KERNEL_WAKELOCK,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+         {.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
         // subsystem_sleep_state
-        {android::util::SUBSYSTEM_SLEEP_STATE,
-         {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+        {android::util::SUBSYSTEM_SLEEP_STATE, {.puller = new SubsystemSleepStatePuller()}},
         // on_device_power_measurement
-        {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
+        {android::util::ON_DEVICE_POWER_MEASUREMENT, {.puller = new PowerStatsPuller()}},
         // cpu_time_per_freq
         {android::util::CPU_TIME_PER_FREQ,
-         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+         {.additiveFields = {3},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
         // cpu_time_per_uid
         {android::util::CPU_TIME_PER_UID,
-         {{2, 3},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
+         {.additiveFields = {2, 3},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
         // cpu_time_per_uid_freq
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_TIME_PER_UID_FREQ,
-         {{4},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
+         {.additiveFields = {4},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
         // cpu_active_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_ACTIVE_TIME,
-         {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+         {.additiveFields = {2},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
         // cpu_cluster_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_CLUSTER_TIME,
-         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+         {.additiveFields = {3},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
         // wifi_activity_energy_info
         {android::util::WIFI_ACTIVITY_INFO,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+         {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
         // modem_activity_info
         {android::util::MODEM_ACTIVITY_INFO,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+         {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
         // bluetooth_activity_info
         {android::util::BLUETOOTH_ACTIVITY_INFO,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+         {.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
         // system_elapsed_realtime
         {android::util::SYSTEM_ELAPSED_REALTIME,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+         {.pullTimeoutNs = NS_PER_SEC / 2,
+          .coolDownNs = NS_PER_SEC,
+          .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
         // system_uptime
         {android::util::SYSTEM_UPTIME,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+         {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
         // remaining_battery_capacity
         {android::util::REMAINING_BATTERY_CAPACITY,
-         {{},
-          1 * NS_PER_SEC,
-          new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+         {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
         // full_battery_capacity
         {android::util::FULL_BATTERY_CAPACITY,
-         {{},
-          1 * NS_PER_SEC,
-          new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+         {.puller = new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
         // battery_voltage
         {android::util::BATTERY_VOLTAGE,
-         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
-        // battery_level
+         {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+        // battery_voltage
         {android::util::BATTERY_LEVEL,
-         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+         {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
-         {{4, 5, 6, 7, 8, 9},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+         {.additiveFields = {4, 5, 6, 7, 8, 9},
+          .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
         // native_process_memory_state
         {android::util::NATIVE_PROCESS_MEMORY_STATE,
-         {{3, 4, 5, 6},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
+         {.additiveFields = {3, 4, 5, 6},
+          .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
         {android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
-         {{3},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
+         {.additiveFields = {3},
+          .puller =
+                  new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
         // temperature
-        {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
+        {android::util::TEMPERATURE, {.puller = new ResourceThermalManagerPuller()}},
         // binder_calls
         {android::util::BINDER_CALLS,
-         {{4, 5, 6, 8, 12},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
+         {.additiveFields = {4, 5, 6, 8, 12},
+          .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
         // binder_calls_exceptions
         {android::util::BINDER_CALLS_EXCEPTIONS,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
         // looper_stats
         {android::util::LOOPER_STATS,
-         {{5, 6, 7, 8, 9},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
+         {.additiveFields = {5, 6, 7, 8, 9},
+          .puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
         // Disk Stats
         {android::util::DISK_STATS,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}},
         // Directory usage
         {android::util::DIRECTORY_USAGE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
         // Size of app's code, data, and cache
         {android::util::APP_SIZE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}},
         // Size of specific categories of files. Eg. Music.
         {android::util::CATEGORY_SIZE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
         // Number of fingerprints registered to each user.
         {android::util::NUM_FINGERPRINTS,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
         // ProcStats.
         {android::util::PROC_STATS,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}},
         // ProcStatsPkgProc.
         {android::util::PROC_STATS_PKG_PROC,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+         {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
         // Disk I/O stats per uid.
         {android::util::DISK_IO,
-         {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
-          3 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DISK_IO)}},
+         {.additiveFields = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
+          .coolDownNs = 3 * NS_PER_SEC,
+          .puller = new StatsCompanionServicePuller(android::util::DISK_IO)}},
         // PowerProfile constants for power model calculations.
         {android::util::POWER_PROFILE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
         // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
         {android::util::PROCESS_CPU_TIME,
-         {{} /* additive fields */,
-          5 * NS_PER_SEC /* min cool-down in seconds*/,
-          new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+         {.coolDownNs = 5 * NS_PER_SEC /* min cool-down in seconds*/,
+          .puller = new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
         {android::util::CPU_TIME_PER_THREAD_FREQ,
-         {{7, 9, 11, 13, 15, 17, 19, 21},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+         {.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
         // DeviceCalculatedPowerUse.
         {android::util::DEVICE_CALCULATED_POWER_USE,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
         // DeviceCalculatedPowerBlameUid.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
-         {{},  // BatteryStats already merged isolated with host ids so it's unnecessary here.
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+         {.puller = new StatsCompanionServicePuller(
+                  android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
         // DeviceCalculatedPowerBlameOther.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+         {.puller = new StatsCompanionServicePuller(
+                  android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
         // BuildInformation.
         {android::util::BUILD_INFORMATION,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
+         {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
 }
 
-bool StatsPullerManager::Pull(const int tagId, const int64_t timeNs,
-                                  vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
     VLOG("Initiating pulling %d", tagId);
 
     if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
-        bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(timeNs, data);
+        bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
         VLOG("pulled %d items", (int)data->size());
         return ret;
     } else {
@@ -333,8 +310,9 @@
     }
 }
 
-void StatsPullerManager::OnAlarmFired(const int64_t currentTimeNs) {
+void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
     AutoMutex _l(mLock);
+    int64_t wallClockNs = getWallClockNs();
 
     int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
 
@@ -344,7 +322,7 @@
         vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
         if (pair.second.size() != 0) {
             for (ReceiverInfo& receiverInfo : pair.second) {
-                if (receiverInfo.nextPullTimeNs <= currentTimeNs) {
+                if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
                     receivers.push_back(&receiverInfo);
                 } else {
                     if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
@@ -360,22 +338,38 @@
 
     for (const auto& pullInfo : needToPull) {
         vector<shared_ptr<LogEvent>> data;
-        if (Pull(pullInfo.first, currentTimeNs, &data)) {
-            for (const auto& receiverInfo : pullInfo.second) {
-                sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
-                if (receiverPtr != nullptr) {
-                    receiverPtr->onDataPulled(data);
-                    // we may have just come out of a coma, compute next pull time
-                    receiverInfo->nextPullTimeNs =
-                            (currentTimeNs - receiverInfo->nextPullTimeNs) /
-                                receiverInfo->intervalNs * receiverInfo->intervalNs +
-                            receiverInfo->intervalNs + receiverInfo->nextPullTimeNs;
-                    if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
-                        minNextPullTimeNs = receiverInfo->nextPullTimeNs;
-                    }
-                } else {
-                    VLOG("receiver already gone.");
+        if (!Pull(pullInfo.first, &data)) {
+            VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
+            continue;
+        }
+        StatsdStats::getInstance().notePullDelay(pullInfo.first,
+                                                 getElapsedRealtimeNs() - elapsedTimeNs);
+
+        // Convention is to mark pull atom timestamp at request time.
+        // If we pull at t0, puller starts at t1, finishes at t2, and send back
+        // at t3, we mark t0 as its timestamp, which should correspond to its
+        // triggering event, such as condition change at t0.
+        // Here the triggering event is alarm fired from AlarmManager.
+        // In ValueMetricProducer and GaugeMetricProducer we do same thing
+        // when pull on condition change, etc.
+        for (auto& event : data) {
+            event->setElapsedTimestampNs(elapsedTimeNs);
+            event->setLogdWallClockTimestampNs(wallClockNs);
+        }
+
+        for (const auto& receiverInfo : pullInfo.second) {
+            sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
+            if (receiverPtr != nullptr) {
+                receiverPtr->onDataPulled(data);
+                // we may have just come out of a coma, compute next pull time
+                int numBucketsAhead =
+                        (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs;
+                receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs;
+                if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
+                    minNextPullTimeNs = receiverInfo->nextPullTimeNs;
                 }
+            } else {
+                VLOG("receiver already gone.");
             }
         }
     }
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 3350736..807e4af 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -26,6 +26,7 @@
 #include <vector>
 #include "PullDataReceiver.h"
 #include "StatsPuller.h"
+#include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 
 namespace android {
@@ -36,11 +37,19 @@
     // The field numbers of the fields that need to be summed when merging
     // isolated uid with host uid.
     std::vector<int> additiveFields;
-    // How long should the puller wait before doing an actual pull again. Default
-    // 1 sec. Set this to 0 if this is handled elsewhere.
+    // Minimum time before this puller does actual pull again.
+    // Pullers can cause significant impact to system health and battery.
+    // So that we don't pull too frequently.
+    // If a pull request comes before cooldown, a cached version from previous pull
+    // will be returned.
     int64_t coolDownNs = 1 * NS_PER_SEC;
     // The actual puller
     sp<StatsPuller> puller;
+    // Max time allowed to pull this atom.
+    // We cannot reliably kill a pull thread. So we don't terminate the puller.
+    // The data is discarded if the pull takes longer than this and mHasGoodData
+    // marked as false.
+    int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs;
 } PullAtomInfo;
 
 class StatsPullerManager : public virtual RefBase {
@@ -61,13 +70,18 @@
     // Verify if we know how to pull for this matcher
     bool PullerForMatcherExists(int tagId) const;
 
-    void OnAlarmFired(const int64_t timeNs);
+    void OnAlarmFired(int64_t elapsedTimeNs);
 
-    // Use respective puller to pull the data. The returned data will have
-    // elapsedTimeNs set as timeNs and will have wallClockTimeNs set as current
-    // wall clock time.
-    virtual bool Pull(const int tagId, const int64_t timeNs,
-                      vector<std::shared_ptr<LogEvent>>* data);
+    // Pulls the most recent data.
+    // The data may be served from cache if consecutive pulls come within
+    // mCoolDownNs.
+    // Returns true if the pull was successful.
+    // Returns false when
+    //   1) the pull fails
+    //   2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+    // If the metric wants to make any change to the data, like timestamps, they
+    // should make a copy as this data may be shared with multiple metrics.
+    virtual bool Pull(int tagId, vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear pull data cache immediately.
     int ForceClearPullerCache();
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.h b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
index 17ce5b4cb..87f5f02 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.h
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
@@ -29,6 +29,8 @@
 class SubsystemSleepStatePuller : public StatsPuller {
 public:
     SubsystemSleepStatePuller();
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 3e5e82f..f4d0144 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -373,6 +373,16 @@
     mPulledAtomStats[pullAtomId].dataError++;
 }
 
+void StatsdStats::notePullTimeout(int pullAtomId) {
+    lock_guard<std::mutex> lock(mLock);
+    mPulledAtomStats[pullAtomId].pullTimeout++;
+}
+
+void StatsdStats::notePullExceedMaxDelay(int pullAtomId) {
+    lock_guard<std::mutex> lock(mLock);
+    mPulledAtomStats[pullAtomId].pullExceedMaxDelay++;
+}
+
 void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
     lock_guard<std::mutex> lock(mLock);
 
@@ -429,6 +439,8 @@
         pullStats.second.maxPullDelayNs = 0;
         pullStats.second.numPullDelay = 0;
         pullStats.second.dataError = 0;
+        pullStats.second.pullTimeout = 0;
+        pullStats.second.pullExceedMaxDelay = 0;
     }
 }
 
@@ -535,13 +547,16 @@
     dprintf(out, "********Pulled Atom stats***********\n");
     for (const auto& pair : mPulledAtomStats) {
         dprintf(out,
-                "Atom %d->(total pull)%ld, (pull from cache)%ld, (min pull interval)%ld, (average "
-                "pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay nanos)%lld, "
-                "(max pull delay nanos)%lld, (data error)%ld\n",
+                "Atom %d->(total pull)%ld, (pull from cache)%ld, (min pull interval)%ld \n"
+                "  (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
+                "nanos)%lld, "
+                "  (max pull delay nanos)%lld, (data error)%ld\n"
+                "  (pull timeout)%ld, (pull exceed max delay)%ld\n",
                 (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
                 (long)pair.second.minPullIntervalSec, (long long)pair.second.avgPullTimeNs,
                 (long long)pair.second.maxPullTimeNs, (long long)pair.second.avgPullDelayNs,
-                (long long)pair.second.maxPullDelayNs, pair.second.dataError);
+                (long long)pair.second.maxPullDelayNs, pair.second.dataError,
+                pair.second.pullTimeout, pair.second.pullExceedMaxDelay);
     }
 
     if (mAnomalyAlarmRegisteredStats > 0) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 3157037..dc647f8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -144,6 +144,8 @@
     // How long to try to clear puller cache from last time
     static const long kPullerCacheClearIntervalSec = 1;
 
+    // Max time to do a pull.
+    static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
     /**
      * Report a new config has been received and report the static stats about the config.
      *
@@ -296,6 +298,16 @@
     void notePullDelay(int pullAtomId, int64_t pullDelayNs);
 
     /*
+     * Records pull exceeds timeout for the puller.
+     */
+    void notePullTimeout(int pullAtomId);
+
+    /*
+     * Records pull exceeds max delay for a metric.
+     */
+    void notePullExceedMaxDelay(int pullAtomId);
+
+    /*
      * Records when system server restarts.
      */
     void noteSystemServerRestart(int32_t timeSec);
@@ -335,6 +347,8 @@
         int64_t maxPullDelayNs = 0;
         long numPullDelay = 0;
         long dataError = 0;
+        long pullTimeout = 0;
+        long pullExceedMaxDelay = 0;
     } PulledAtomStats;
 
 private:
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 8d61aba..2ff8aa1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,6 +41,14 @@
     }
 }
 
+LogEvent::LogEvent(const LogEvent& event) {
+    mTagId = event.mTagId;
+    mLogUid = event.mLogUid;
+    mElapsedTimestampNs = event.mElapsedTimestampNs;
+    mLogdTimestampNs = event.mLogdTimestampNs;
+    mValues = event.mValues;
+}
+
 LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
     mTagId = statsLogEventWrapper.getTagId();
     mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 5408d17..43e6e4f 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -207,12 +207,15 @@
         return &mValues;
     }
 
+    inline LogEvent makeCopy() {
+        return LogEvent(*this);
+    }
+
 private:
     /**
-     * Don't copy, it's slower. If we really need this we can add it but let's try to
-     * avoid it.
+     * Only use this if copy is absolutely needed.
      */
-    explicit LogEvent(const LogEvent&);
+    LogEvent(const LogEvent&);
 
     /**
      * Parses a log_msg into a LogEvent object.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index ec60244..67a1a47 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -68,15 +68,12 @@
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
 
-GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard,
-                                         const int whatMatcherIndex,
-                                         const sp<EventMatcherWizard>& matcherWizard,
-                                         const int pullTagId,
-                                         const int triggerAtomId, const int atomId,
-                                         const int64_t timeBaseNs, const int64_t startTimeNs,
-                                         const sp<StatsPullerManager>& pullerManager)
+GaugeMetricProducer::GaugeMetricProducer(
+        const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
+        const sp<ConditionWizard>& wizard, const int whatMatcherIndex,
+        const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
+        const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
+        const sp<StatsPullerManager>& pullerManager)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
@@ -86,6 +83,8 @@
       mAtomId(atomId),
       mIsPulled(pullTagId != -1),
       mMinBucketSizeNs(metric.min_bucket_size_nanos()),
+      mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
+                                                      : StatsdStats::kPullMaxDelayNs),
       mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
                                           StatsdStats::kAtomDimensionKeySizeLimitMap.end()
                                   ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
@@ -338,14 +337,24 @@
         return;
     }
     vector<std::shared_ptr<LogEvent>> allData;
-    if (!mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+    if (!mPullerManager->Pull(mPullTagId, &allData)) {
         ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
         return;
     }
+    const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+    if (pullDelayNs > mMaxPullDelayNs) {
+        ALOGE("Pull finish too late for atom %d", mPullTagId);
+        StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+        StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+        return;
+    }
+    StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
     for (const auto& data : allData) {
-        if (mEventMatcherWizard->matchLogEvent(
-                *data, mWhatMatcherIndex) == MatchingState::kMatched) {
-            onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+        LogEvent localCopy = data->makeCopy();
+        localCopy.setElapsedTimestampNs(timestampNs);
+        if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
+            MatchingState::kMatched) {
+            onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
         }
     }
 }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6e3530b..a1a5061 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -160,6 +160,8 @@
 
     GaugeMetric::SamplingType mSamplingType;
 
+    const int64_t mMaxPullDelayNs;
+
     // apply a whitelist on the original input
     std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event);
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index cf56e2d..9a8e3bd 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -103,7 +103,9 @@
       mValueDirection(metric.value_direction()),
       mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
       mUseZeroDefaultBase(metric.use_zero_default_base()),
-      mHasGlobalBase(false) {
+      mHasGlobalBase(false),
+      mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
+                                                      : StatsdStats::kPullMaxDelayNs) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
         bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -340,19 +342,32 @@
 
 void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
     vector<std::shared_ptr<LogEvent>> allData;
-    if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
-        for (const auto& data : allData) {
-            if (mEventMatcherWizard->matchLogEvent(
-                *data, mWhatMatcherIndex) == MatchingState::kMatched) {
-                onMatchedLogEventLocked(mWhatMatcherIndex, *data);
-            }
-        }
-        mHasGlobalBase = true;
-    } else {
-        // for pulled data, every pull is needed. So we reset the base if any
-        // pull fails.
+    if (!mPullerManager->Pull(mPullTagId, &allData)) {
+        ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
         resetBase();
+        return;
     }
+    const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+    if (pullDelayNs > mMaxPullDelayNs) {
+        ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
+              (long long)mMaxPullDelayNs);
+        StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+        StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+        resetBase();
+        return;
+    }
+    StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+
+    for (const auto& data : allData) {
+        // make a copy before doing and changes
+        LogEvent localCopy = data->makeCopy();
+        localCopy.setElapsedTimestampNs(timestampNs);
+        if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
+            MatchingState::kMatched) {
+            onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
+        }
+    }
+    mHasGlobalBase = true;
 }
 
 int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
@@ -381,10 +396,11 @@
             return;
         }
         for (const auto& data : allData) {
-            if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) ==
+            LogEvent localCopy = data->makeCopy();
+            if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
                 MatchingState::kMatched) {
-                data->setElapsedTimestampNs(bucketEndTime);
-                onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+                localCopy.setElapsedTimestampNs(bucketEndTime);
+                onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
             }
         }
         mHasGlobalBase = true;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4991af4..4865aee 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -183,6 +183,8 @@
     // diff against.
     bool mHasGlobalBase;
 
+    const int64_t mMaxPullDelayNs;
+
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
@@ -207,6 +209,8 @@
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
     FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFail);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index dffff7a..22883f3 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -131,8 +131,7 @@
                     VLOG("pull atom %d now", pullInfo.mPullerMatcher.atom_id());
 
                     vector<std::shared_ptr<LogEvent>> data;
-                    mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), nowMillis * 1000000L,
-                                     &data);
+                    mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), &data);
                     VLOG("pulled %zu atoms", data.size());
                     if (data.size() > 0) {
                         writeToOutputLocked(data, pullInfo.mPullerMatcher);
diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h
index 73e4d33..b8185d2 100644
--- a/cmds/statsd/src/socket/StatsSocketListener.h
+++ b/cmds/statsd/src/socket/StatsSocketListener.h
@@ -35,7 +35,7 @@
 
 class StatsSocketListener : public SocketListener, public virtual android::RefBase {
 public:
-    StatsSocketListener(const sp<LogListener>& listener);
+    explicit StatsSocketListener(const sp<LogListener>& listener);
 
     virtual ~StatsSocketListener();
 
@@ -51,4 +51,4 @@
 };
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 5a87e46..e8de875 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -401,6 +401,8 @@
         optional int64 average_pull_delay_nanos = 7;
         optional int64 max_pull_delay_nanos = 8;
         optional int64 data_error = 9;
+        optional int64 pull_timeout = 10;
+        optional int64 pull_exceed_max_delay = 11;
     }
     repeated PulledAtomStats pulled_atom_stats = 10;
 
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index f1310db..7de0bb3 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -64,6 +64,8 @@
 const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7;
 const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8;
 const int FIELD_ID_DATA_ERROR = 9;
+const int FIELD_ID_PULL_TIMEOUT = 10;
+const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
 
 namespace {
 
@@ -450,6 +452,10 @@
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS,
                        (long long)pair.second.maxPullDelayNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT,
+                       (long long)pair.second.pullTimeout);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY,
+                       (long long)pair.second.pullExceedMaxDelay);
     protoOutput->end(token);
 }
 
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index f485185..381ac32 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -240,7 +240,10 @@
   optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ;
 
   optional int64 min_bucket_size_nanos = 10;
+
   optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
+
+  optional int32 max_pull_delay_sec = 13 [default = 10];
 }
 
 message ValueMetric {
@@ -285,6 +288,8 @@
   optional ValueDirection value_direction = 13 [default = INCREASING];
 
   optional bool skip_zero_diff_output = 14 [default = true];
+
+  optional int32 max_pull_delay_sec = 16 [default = 10];
 }
 
 message Alert {
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 2d090e0..d5c358d 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -48,6 +48,7 @@
     *gaugeMetric->mutable_dimensions_in_what() =
         CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
     gaugeMetric->set_bucket(FIVE_MINUTES);
+    gaugeMetric->set_max_pull_delay_sec(INT_MAX);
     config.set_hash_strings_in_metric_report(false);
 
     return config;
@@ -57,7 +58,7 @@
 
 TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
     auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
@@ -202,7 +203,7 @@
 
 TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
     auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
@@ -303,7 +304,7 @@
 
 TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
     auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index abf1ab1..cab6eac 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -50,6 +50,7 @@
     valueMetric->set_bucket(FIVE_MINUTES);
     valueMetric->set_use_absolute_value_on_reset(true);
     valueMetric->set_skip_zero_diff_output(false);
+    valueMetric->set_max_pull_delay_sec(INT_MAX);
     return config;
 }
 
@@ -57,7 +58,7 @@
 
 TEST(ValueMetricE2eTest, TestPulledEvents) {
     auto config = CreateStatsdConfig();
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
@@ -163,7 +164,7 @@
 
 TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
     auto config = CreateStatsdConfig();
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
new file mode 100644
index 0000000..76e2097
--- /dev/null
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -0,0 +1,227 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <chrono>
+#include <thread>
+#include <vector>
+#include "../metrics/metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using std::this_thread::sleep_for;
+using testing::Contains;
+
+// cooldown time 1sec.
+int pullTagId = 10014;
+
+bool pullSuccess;
+vector<std::shared_ptr<LogEvent>> pullData;
+long pullDelayNs;
+
+class FakePuller : public StatsPuller {
+public:
+    FakePuller() : StatsPuller(pullTagId){};
+
+private:
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
+        (*data) = pullData;
+        sleep_for(std::chrono::nanoseconds(pullDelayNs));
+        return pullSuccess;
+    }
+};
+
+FakePuller puller;
+
+shared_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(pullTagId, eventTimeNs);
+    event->write(value);
+    event->init();
+    return event;
+}
+
+class StatsPullerTest : public ::testing::Test {
+public:
+    StatsPullerTest() {
+    }
+
+    void SetUp() override {
+        puller.ForceClearCache();
+        pullSuccess = false;
+        pullDelayNs = 0;
+        pullData.clear();
+    }
+};
+
+TEST_F(StatsPullerTest, PullSucces) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    sleep_for(std::chrono::seconds(1));
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailAfterSuccess) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    sleep_for(std::chrono::seconds(1));
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = false;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullSuccess = true;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown
+TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+    pullSuccess = true;
+    // timeout is 0.5
+    pullDelayNs = (long)(0.8 * NS_PER_SEC);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullFail) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = false;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTakeTooLong) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+    pullDelayNs = NS_PER_SEC;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTooFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    dataHolder.clear();
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailsAndTooFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = false;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 67a9f7f..2799107 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -90,6 +90,7 @@
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_gauge_fields_filter()->set_include_all(false);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(1);
@@ -106,9 +107,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(3);
@@ -266,6 +266,7 @@
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
@@ -281,10 +282,9 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Return(false))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
                 event->write("some value");
@@ -341,6 +341,7 @@
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
@@ -357,9 +358,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write("some value");
@@ -420,6 +420,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_gauge_fields_filter()->set_include_all(true);
     metric.set_condition(StringToId("APP_DIED"));
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto dim = metric.mutable_dimensions_in_what();
     dim->set_field(tagId);
     dim->add_child()->set_field(1);
@@ -454,9 +455,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(1000);
@@ -502,11 +502,12 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
 
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
@@ -591,6 +592,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
     metric.mutable_gauge_fields_filter()->set_include_all(false);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(1);
@@ -604,9 +606,8 @@
         new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(4);
@@ -614,8 +615,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
                 event->write(5);
@@ -664,6 +664,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
     metric.mutable_gauge_fields_filter()->set_include_all(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto dimensionMatcher = metric.mutable_dimensions_in_what();
     // use field 1 as dimension.
     dimensionMatcher->set_field(tagId);
@@ -678,9 +679,8 @@
         new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3);
                 event->write(3);
@@ -689,8 +689,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(4);
@@ -699,8 +698,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
                 event->write(4);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 5524503..67570fc 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -125,6 +125,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -136,9 +137,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(tagId);
@@ -218,6 +218,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -232,9 +233,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(3);
@@ -315,6 +315,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_use_absolute_value_on_reset(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -326,7 +327,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -393,6 +394,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -404,7 +406,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -469,6 +471,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -481,9 +484,8 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
@@ -492,8 +494,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
@@ -599,6 +600,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -610,10 +612,9 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Return(true))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
                 event->write(tagId);
@@ -661,6 +662,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -672,9 +674,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
                 event->write(tagId);
@@ -683,8 +684,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
                 event->write(tagId);
@@ -924,6 +924,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -935,7 +936,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -1012,6 +1013,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1024,10 +1026,9 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // condition becomes true
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
@@ -1037,8 +1038,7 @@
                 return true;
             }))
             // condition becomes false
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
@@ -1098,6 +1098,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1110,10 +1111,9 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // condition becomes true
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
@@ -1123,8 +1123,7 @@
                 return true;
             }))
             // condition becomes false
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
@@ -1134,8 +1133,7 @@
                 return true;
             }))
             // condition becomes true again
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
                 event->write(tagId);
@@ -1480,6 +1478,7 @@
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_use_zero_default_base(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1491,9 +1490,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(1);
@@ -1565,6 +1563,7 @@
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_use_zero_default_base(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1576,9 +1575,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(1);
@@ -1692,6 +1690,7 @@
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1703,9 +1702,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(1);
@@ -1804,6 +1802,128 @@
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
 }
 
+TEST(ValueMetricProducerTest, TestResetBaseOnPullFail) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+                event->write(tagId);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            .WillOnce(Return(false));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+                                      eventMatcherWizard, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval& curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(100, curInterval.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    valueProducer.onConditionChanged(false, bucketStartTimeNs + 20);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(0);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+                event->write(tagId);
+                event->write(120);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+                                      eventMatcherWizard, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    valueProducer.mCondition = true;
+    valueProducer.mHasGlobalBase = true;
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event->write(1);
+    event->write(110);
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval& curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(110, curInterval.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+
+    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 5afaba6..97c1072 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -38,8 +38,7 @@
     MOCK_METHOD4(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver,
                                         int64_t nextPulltimeNs, int64_t intervalNs));
     MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
-    MOCK_METHOD3(Pull, bool(const int pullCode, const int64_t timeNs,
-                            vector<std::shared_ptr<LogEvent>>* data));
+    MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
 };
 
 class MockUidMap : public UidMap {
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index dd00561..a184f56 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -189,23 +189,22 @@
     sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(10016, _, _))
-            .WillRepeatedly(
-                    Invoke([](int tagId, int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data) {
-                        data->clear();
-                        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, timeNs);
-                        event->write(kUid1);
-                        event->write(kCpuTime1);
-                        event->init();
-                        data->push_back(event);
-                        // another event
-                        event = make_shared<LogEvent>(tagId, timeNs);
-                        event->write(kUid2);
-                        event->write(kCpuTime2);
-                        event->init();
-                        data->push_back(event);
-                        return true;
-                    }));
+    EXPECT_CALL(*pullerManager, Pull(10016, _))
+            .WillRepeatedly(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, 1111L);
+                event->write(kUid1);
+                event->write(kCpuTime1);
+                event->init();
+                data->push_back(event);
+                // another event
+                event = make_shared<LogEvent>(tagId, 1111L);
+                event->write(kUid2);
+                event->write(kCpuTime2);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
 
     runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
                  getExpectedShellData());
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index a782ced..aad3253 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1638,46 +1638,6 @@
             return true;
         }
 
-        /**
-         * @hide
-         */
-        public static boolean areAnyScreenOffEffectsSuppressed(int effects) {
-            for (int i = 0; i < SCREEN_OFF_SUPPRESSED_EFFECTS.length; i++) {
-                final int effect = SCREEN_OFF_SUPPRESSED_EFFECTS[i];
-                if ((effects & effect) != 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        public static boolean areAnyScreenOnEffectsSuppressed(int effects) {
-            for (int i = 0; i < SCREEN_ON_SUPPRESSED_EFFECTS.length; i++) {
-                final int effect = SCREEN_ON_SUPPRESSED_EFFECTS[i];
-                if ((effects & effect) != 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        public static int toggleScreenOffEffectsSuppressed(int currentEffects, boolean suppress) {
-            return toggleEffects(currentEffects, SCREEN_OFF_SUPPRESSED_EFFECTS, suppress);
-        }
-
-        /**
-         * @hide
-         */
-        public static int toggleScreenOnEffectsSuppressed(int currentEffects, boolean suppress) {
-            return toggleEffects(currentEffects, SCREEN_ON_SUPPRESSED_EFFECTS, suppress);
-        }
-
         private static int toggleEffects(int currentEffects, int[] effects, boolean suppress) {
             for (int i = 0; i < effects.length; i++) {
                 final int effect = effects[i];
@@ -1837,6 +1797,41 @@
             return priorityMessageSenders;
         }
 
+        /** @hide **/
+        public boolean showFullScreenIntents() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0;
+        }
+
+        /** @hide **/
+        public boolean showLights() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0;
+        }
+
+        /** @hide **/
+        public boolean showPeeking() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0;
+        }
+
+        /** @hide **/
+        public boolean showStatusBarIcons() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_STATUS_BAR) == 0;
+        }
+
+        /** @hide **/
+        public boolean showAmbient() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0;
+        }
+
+        /** @hide **/
+        public boolean showBadges() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_BADGE) == 0;
+        }
+
+        /** @hide **/
+        public boolean showInNotificationList() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0;
+        }
+
         /**
          * returns a deep copy of this policy
          * @hide
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 001e328..6f12cad 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -691,7 +691,8 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
      */
-    public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(attrs);
     }
 
@@ -702,8 +703,9 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
      */
-    public final TypedArray obtainStyledAttributes(
-            @StyleRes int resid, @StyleableRes int[] attrs) throws Resources.NotFoundException {
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@StyleRes int resid,
+            @NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException {
         return getTheme().obtainStyledAttributes(resid, attrs);
     }
 
@@ -714,8 +716,9 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
+    @NonNull
     public final TypedArray obtainStyledAttributes(
-            AttributeSet set, @StyleableRes int[] attrs) {
+            @Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
     }
 
@@ -726,8 +729,9 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
-    public final TypedArray obtainStyledAttributes(
-            AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+            @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
             @StyleRes int defStyleRes) {
         return getTheme().obtainStyledAttributes(
             set, attrs, defStyleAttr, defStyleRes);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 623bdda..f06df3d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -145,6 +145,15 @@
     public static final String ACTION_SESSION_COMMITTED =
             "android.content.pm.action.SESSION_COMMITTED";
 
+    /**
+     * Broadcast Action: Send information about a staged install session when its state is updated.
+     * <p>
+     * The associated session information is defined in {@link #EXTRA_SESSION}.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SESSION_UPDATED =
+            "android.content.pm.action.SESSION_UPDATED";
+
     /** {@hide} */
     public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
 
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 88b1c88..365ceac 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1468,7 +1468,8 @@
          * @see #obtainStyledAttributes(int, int[])
          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
          */
-        public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+        @NonNull
+        public TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
             return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
         }
 
@@ -1493,7 +1494,9 @@
          * @see #obtainStyledAttributes(int[])
          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
          */
-        public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
+        @NonNull
+        public TypedArray obtainStyledAttributes(@StyleRes int resId,
+                @NonNull @StyleableRes int[] attrs)
                 throws NotFoundException {
             return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
         }
@@ -1547,8 +1550,10 @@
          * @see #obtainStyledAttributes(int[])
          * @see #obtainStyledAttributes(int, int[])
          */
-        public TypedArray obtainStyledAttributes(AttributeSet set,
-                @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+        @NonNull
+        public TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+                @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+                @StyleRes int defStyleRes) {
             return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
         }
 
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index f01d5b1..015146466 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -123,7 +123,7 @@
         mContext = context;
         mAudioManager = context.getSystemService(AudioManager.class);
         mNotificationManager = context.getSystemService(NotificationManager.class);
-        mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+        mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
         mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
                 .PRIORITY_CATEGORY_ALARMS) != 0;
         mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
@@ -485,7 +485,7 @@
                 mZenMode = mNotificationManager.getZenMode();
                 updateSlider();
             } else if (NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED.equals(action)) {
-                mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+                mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
                 mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
                         .PRIORITY_CATEGORY_ALARMS) != 0;
                 mAllowMedia = (mNotificationPolicy.priorityCategories
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 27df845..bad7ddd 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -163,14 +163,15 @@
             final int height = rect.bottom - rect.top;
             final int width = rect.right - rect.left;
             final WindowManager.LayoutParams windowParams = window.getAttributes();
-            windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
-            windowParams.y = rect.top - height;
+            windowParams.gravity = Gravity.TOP | Gravity.LEFT;
+            windowParams.y = rect.top + height;
             windowParams.height = height;
             windowParams.x = rect.left;
             windowParams.width = width;
 
             window.setAttributes(windowParams);
             window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            window.setBackgroundDrawableResource(android.R.color.transparent);
 
             mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
             final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 6792c69..bec654a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,7 +16,6 @@
 
 package android.service.notification;
 
-import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
@@ -126,6 +125,15 @@
     private static final String STATE_TAG = "state";
     private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
 
+    // zen policy visual effects attributes
+    private static final String SHOW_ATT_FULL_SCREEN_INTENT = "showFullScreenIntent";
+    private static final String SHOW_ATT_LIGHTS = "showLights";
+    private static final String SHOW_ATT_PEEK = "shoePeek";
+    private static final String SHOW_ATT_STATUS_BAR_ICONS = "showStatusBarIcons";
+    private static final String SHOW_ATT_BADGES = "showBadges";
+    private static final String SHOW_ATT_AMBIENT = "showAmbient";
+    private static final String SHOW_ATT_NOTIFICATION_LIST = "showNotificationList";
+
     private static final String CONDITION_ATT_ID = "id";
     private static final String CONDITION_ATT_SUMMARY = "summary";
     private static final String CONDITION_ATT_LINE1 = "line1";
@@ -134,6 +142,8 @@
     private static final String CONDITION_ATT_STATE = "state";
     private static final String CONDITION_ATT_FLAGS = "flags";
 
+    private static final String ZEN_POLICY_TAG = "zen_policy";
+
     private static final String MANUAL_TAG = "manual";
     private static final String AUTOMATIC_TAG = "automatic";
 
@@ -650,6 +660,7 @@
             rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         }
         rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
+        rt.zenPolicy = readZenPolicyXml(parser);
         return rt;
     }
 
@@ -676,6 +687,9 @@
         if (rule.condition != null) {
             writeConditionXml(rule.condition, out);
         }
+        if (rule.zenPolicy != null) {
+            writeZenPolicyXml(rule.zenPolicy, out);
+        }
         out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified));
     }
 
@@ -706,6 +720,141 @@
         out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
     }
 
+    /**
+     * Read the zen policy from xml
+     * Returns null if no zen policy exists
+     */
+    public static ZenPolicy readZenPolicyXml(XmlPullParser parser) {
+        boolean policySet = false;
+
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+        final int calls = safeInt(parser, ALLOW_ATT_CALLS_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
+        final int messages = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
+        final int repeatCallers = safeInt(parser, ALLOW_ATT_REPEAT_CALLERS, ZenPolicy.STATE_UNSET);
+        final int alarms = safeInt(parser, ALLOW_ATT_ALARMS, ZenPolicy.STATE_UNSET);
+        final int media = safeInt(parser, ALLOW_ATT_MEDIA, ZenPolicy.STATE_UNSET);
+        final int system = safeInt(parser, ALLOW_ATT_SYSTEM, ZenPolicy.STATE_UNSET);
+        final int events = safeInt(parser, ALLOW_ATT_EVENTS, ZenPolicy.STATE_UNSET);
+        final int reminders = safeInt(parser, ALLOW_ATT_REMINDERS, ZenPolicy.STATE_UNSET);
+
+        if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) {
+            builder.allowCalls(calls);
+            policySet = true;
+        }
+        if (messages != ZenPolicy.PEOPLE_TYPE_UNSET) {
+            builder.allowMessages(messages);
+            policySet = true;
+        }
+        if (repeatCallers != ZenPolicy.STATE_UNSET) {
+            builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (alarms != ZenPolicy.STATE_UNSET) {
+            builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (media != ZenPolicy.STATE_UNSET) {
+            builder.allowMedia(media == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (system != ZenPolicy.STATE_UNSET) {
+            builder.allowSystem(system == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (events != ZenPolicy.STATE_UNSET) {
+            builder.allowEvents(events == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (reminders != ZenPolicy.STATE_UNSET) {
+            builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+
+        final int fullScreenIntent = safeInt(parser, SHOW_ATT_FULL_SCREEN_INTENT,
+                ZenPolicy.STATE_UNSET);
+        final int lights = safeInt(parser, SHOW_ATT_LIGHTS, ZenPolicy.STATE_UNSET);
+        final int peek = safeInt(parser, SHOW_ATT_PEEK, ZenPolicy.STATE_UNSET);
+        final int statusBar = safeInt(parser, SHOW_ATT_STATUS_BAR_ICONS, ZenPolicy.STATE_UNSET);
+        final int badges = safeInt(parser, SHOW_ATT_BADGES, ZenPolicy.STATE_UNSET);
+        final int ambient = safeInt(parser, SHOW_ATT_AMBIENT, ZenPolicy.STATE_UNSET);
+        final int notificationList = safeInt(parser, SHOW_ATT_NOTIFICATION_LIST,
+                ZenPolicy.STATE_UNSET);
+
+        if (fullScreenIntent != ZenPolicy.STATE_UNSET) {
+            builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (lights != ZenPolicy.STATE_UNSET) {
+            builder.showLights(lights == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (peek != ZenPolicy.STATE_UNSET) {
+            builder.showPeeking(peek == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (statusBar != ZenPolicy.STATE_UNSET) {
+            builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (badges != ZenPolicy.STATE_UNSET) {
+            builder.showBadges(badges == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (ambient != ZenPolicy.STATE_UNSET) {
+            builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (notificationList != ZenPolicy.STATE_UNSET) {
+            builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+
+        if (policySet) {
+            return builder.build();
+        }
+        return null;
+    }
+
+    /**
+     * Writes zen policy to xml
+     */
+    public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out)
+            throws IOException {
+        writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out);
+        writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out);
+        writeZenPolicyState(ALLOW_ATT_REPEAT_CALLERS, policy.getPriorityCategoryRepeatCallers(),
+                out);
+        writeZenPolicyState(ALLOW_ATT_ALARMS, policy.getPriorityCategoryAlarms(), out);
+        writeZenPolicyState(ALLOW_ATT_MEDIA, policy.getPriorityCategoryMedia(), out);
+        writeZenPolicyState(ALLOW_ATT_SYSTEM, policy.getPriorityCategorySystem(), out);
+        writeZenPolicyState(ALLOW_ATT_REMINDERS, policy.getPriorityCategoryReminders(), out);
+        writeZenPolicyState(ALLOW_ATT_EVENTS, policy.getPriorityCategoryEvents(), out);
+
+        writeZenPolicyState(SHOW_ATT_FULL_SCREEN_INTENT, policy.getVisualEffectFullScreenIntent(),
+                out);
+        writeZenPolicyState(SHOW_ATT_LIGHTS, policy.getVisualEffectLights(), out);
+        writeZenPolicyState(SHOW_ATT_PEEK, policy.getVisualEffectPeek(), out);
+        writeZenPolicyState(SHOW_ATT_STATUS_BAR_ICONS, policy.getVisualEffectStatusBar(), out);
+        writeZenPolicyState(SHOW_ATT_BADGES, policy.getVisualEffectBadge(), out);
+        writeZenPolicyState(SHOW_ATT_AMBIENT, policy.getVisualEffectAmbient(), out);
+        writeZenPolicyState(SHOW_ATT_NOTIFICATION_LIST, policy.getVisualEffectNotificationList(),
+                out);
+    }
+
+    private static void writeZenPolicyState(String attr, int val, XmlSerializer out)
+            throws IOException {
+        if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM)
+                || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) {
+            if (val != ZenPolicy.PEOPLE_TYPE_UNSET) {
+                out.attribute(null, attr, Integer.toString(val));
+            }
+        } else {
+            if (val != ZenPolicy.STATE_UNSET) {
+                out.attribute(null, attr, Integer.toString(val));
+            }
+        }
+    }
+
     public static boolean isValidHour(int val) {
         return val >= 0 && val < 24;
     }
@@ -798,7 +947,7 @@
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
-            priorityCategories |= PRIORITY_CATEGORY_REMINDERS;
+            priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
         }
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS,
@@ -839,15 +988,30 @@
             priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
         }
 
-        if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
+        boolean suppressFullScreenIntent = !zenPolicy.isVisualEffectAllowed(
+                ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
-                        defaultPolicy))) {
+                        defaultPolicy));
+
+        boolean suppressLights = !zenPolicy.isVisualEffectAllowed(
+                ZenPolicy.VISUAL_EFFECT_LIGHTS,
+                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
+                        defaultPolicy));
+
+        boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
+                ZenPolicy.VISUAL_EFFECT_AMBIENT,
+                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
+                        defaultPolicy));
+
+        if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
+            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+        }
+
+        if (suppressFullScreenIntent) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
         }
 
-        if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_LIGHTS,
-                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
-                        defaultPolicy))) {
+        if (suppressLights) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
         }
 
@@ -855,6 +1019,7 @@
                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK,
                         defaultPolicy))) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
+            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
         }
 
         if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
@@ -869,9 +1034,7 @@
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
         }
 
-        if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_AMBIENT,
-                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
-                        defaultPolicy))) {
+        if (suppressAmbient) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
         }
 
@@ -898,14 +1061,31 @@
             case ZenPolicy.PEOPLE_TYPE_ANYONE:
                 return Policy.PRIORITY_SENDERS_ANY;
             case ZenPolicy.PEOPLE_TYPE_CONTACTS:
+
                 return Policy.PRIORITY_SENDERS_CONTACTS;
             case ZenPolicy.PEOPLE_TYPE_STARRED:
             default:
                 return Policy.PRIORITY_SENDERS_STARRED;
         }
-
     }
 
+
+    /**
+     * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType
+     */
+    public static @ZenPolicy.PeopleType int getZenPolicySenders(int senders) {
+        switch (senders) {
+            case Policy.PRIORITY_SENDERS_ANY:
+                return ZenPolicy.PEOPLE_TYPE_ANYONE;
+            case Policy.PRIORITY_SENDERS_CONTACTS:
+                return ZenPolicy.PEOPLE_TYPE_CONTACTS;
+            case Policy.PRIORITY_SENDERS_STARRED:
+            default:
+                return ZenPolicy.PEOPLE_TYPE_STARRED;
+        }
+    }
+
+
     public Policy toNotificationPolicy() {
         int priorityCategories = 0;
         int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 194147c..6392704 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -327,6 +327,32 @@
     }
 
     /**
+     * Whether this policy hides all visual effects
+     * @hide
+     */
+    public boolean shouldHideAllVisualEffects() {
+        for (int i = 0; i < mVisualEffects.size(); i++) {
+            if (mVisualEffects.get(i) != STATE_DISALLOW) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Whether this policy shows all visual effects
+     * @hide
+     */
+    public boolean shouldShowAllVisualEffects() {
+        for (int i = 0; i < mVisualEffects.size(); i++) {
+            if (mVisualEffects.get(i) != STATE_ALLOW) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Builder class for {@link ZenPolicy} objects.
      * Provides a convenient way to set the various fields of a {@link ZenPolicy}.  If a field
      * is not set, it is (@link STATE_UNSET} and will not change the current set policy.
@@ -339,6 +365,17 @@
         }
 
         /**
+         * @hide
+         */
+        public Builder(ZenPolicy policy) {
+            if (policy != null) {
+                mZenPolicy = policy.copy();
+            } else {
+                mZenPolicy = new ZenPolicy();
+            }
+        }
+
+        /**
          * Builds the current ZenPolicy.
          */
         public ZenPolicy build() {
@@ -533,6 +570,34 @@
         }
 
         /**
+         * Whether to allow {@link PriorityCategory} sounds to play when DND is active.
+         * @hide
+         */
+        public Builder allowCategory(@PriorityCategory int category, boolean allow) {
+            switch (category) {
+                case PRIORITY_CATEGORY_ALARMS:
+                    allowAlarms(allow);
+                    break;
+                case PRIORITY_CATEGORY_MEDIA:
+                    allowMedia(allow);
+                    break;
+                case PRIORITY_CATEGORY_SYSTEM:
+                    allowSystem(allow);
+                    break;
+                case PRIORITY_CATEGORY_REMINDERS:
+                    allowReminders(allow);
+                    break;
+                case PRIORITY_CATEGORY_EVENTS:
+                    allowEvents(allow);
+                    break;
+                case PRIORITY_CATEGORY_REPEAT_CALLERS:
+                    allowRepeatCallers(allow);
+                    break;
+            }
+            return this;
+        }
+
+        /**
          * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted
          * by DND are shown.
          */
@@ -601,6 +666,38 @@
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
         }
+
+        /**
+         * Whether notifications intercepted by DND are prevented from appearing for
+         * {@link VisualEffect}
+         * @hide
+         */
+        public Builder showVisualEffect(@VisualEffect int effect, boolean show) {
+            switch (effect) {
+                case VISUAL_EFFECT_FULL_SCREEN_INTENT:
+                    showFullScreenIntent(show);
+                    break;
+                case VISUAL_EFFECT_LIGHTS:
+                    showLights(show);
+                    break;
+                case VISUAL_EFFECT_PEEK:
+                    showPeeking(show);
+                    break;
+                case VISUAL_EFFECT_STATUS_BAR:
+                    showStatusBarIcons(show);
+                    break;
+                case VISUAL_EFFECT_BADGE:
+                    showBadges(show);
+                    break;
+                case VISUAL_EFFECT_AMBIENT:
+                    showInAmbientDisplay(show);
+                    break;
+                case VISUAL_EFFECT_NOTIFICATION_LIST:
+                    showInNotificationList(show);
+                    break;
+            }
+            return this;
+        }
     }
 
     @Override
@@ -640,8 +737,8 @@
                 .append('{')
                 .append("priorityCategories=[").append(priorityCategoriesToString())
                 .append("], visualEffects=[").append(visualEffectsToString())
-                .append(", priorityCalls=").append(stateToString(mPriorityCalls))
-                .append("], priorityMessages=").append(stateToString(mPriorityMessages))
+                .append("], priorityCalls=").append(peopleTypeToString(mPriorityCalls))
+                .append(", priorityMessages=").append(peopleTypeToString(mPriorityMessages))
                 .append('}')
                 .toString();
     }
@@ -726,7 +823,23 @@
             case STATE_ALLOW:
                 return "allow";
         }
-        return null;
+        return "invalidState{" + state + "}";
+    }
+
+    private String peopleTypeToString(@PeopleType int peopleType) {
+        switch (peopleType) {
+            case PEOPLE_TYPE_ANYONE:
+                return "anyone";
+            case PEOPLE_TYPE_CONTACTS:
+                return "contacts";
+            case PEOPLE_TYPE_NONE:
+                return "none";
+            case PEOPLE_TYPE_STARRED:
+                return "starred_contacts";
+            case STATE_UNSET:
+                return "unset";
+        }
+        return "invalidPeopleType{" + peopleType + "}";
     }
 
     @Override
@@ -859,27 +972,6 @@
     /**
      * @hide
      */
-    public boolean areValuesSet() {
-        return getPriorityCategoryReminders() != STATE_UNSET
-                || getPriorityCategoryEvents() != STATE_UNSET
-                || getPriorityCategoryMessages() != STATE_UNSET
-                || getPriorityCategoryCalls() != STATE_UNSET
-                || getPriorityCategoryRepeatCallers() != STATE_UNSET
-                || getPriorityCategoryAlarms() != STATE_UNSET
-                || getPriorityCategoryMedia() != STATE_UNSET
-                || getPriorityCategorySystem() != STATE_UNSET
-                || getVisualEffectFullScreenIntent() != STATE_UNSET
-                || getVisualEffectLights() != STATE_UNSET
-                || getVisualEffectPeek() != STATE_UNSET
-                || getVisualEffectStatusBar() != STATE_UNSET
-                || getVisualEffectBadge() != STATE_UNSET
-                || getVisualEffectAmbient() != STATE_UNSET
-                || getVisualEffectNotificationList() != STATE_UNSET;
-    }
-
-    /**
-     * @hide
-     */
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9dfd43c..330d72f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -107,12 +107,6 @@
       */
     void endProlongedAnimations();
 
-    // Re-evaluate the current orientation from the caller's state.
-    // If there is a change, the new Configuration is returned and the
-    // caller must call setNewConfiguration() sometime later.
-    Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId);
-
     void startFreezingScreen(int exitAnim, int enterAnim);
     void stopFreezingScreen();
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 699b34a..56f973e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -77,7 +77,6 @@
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-
 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
 
@@ -2966,12 +2965,13 @@
             if (afm == null) return null;
 
             final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
-            // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
-            // fix (for example, take system status bar height into account) logic below
+            final Rect windowVisibleDisplayFrame = new Rect();
+            view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
             final int[] location = new int[2];
             view.getLocationOnScreen(location);
-            final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
-                    location[1] + view.getHeight());
+            final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
+                    location[0] + view.getWidth(),
+                    location[1] - windowVisibleDisplayFrame.top + view.getHeight());
             if (sVerbose) {
                 Log.v(TAG, "Coordinates for " + id + ": " + rect);
             }
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 03ff0ca..8a1a0b5 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
     private final UserInfo mUserInfo;
     private final PackageInfo mPackageInfo;
 
-    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.P;
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.Q;
 
     public UserPackage(UserInfo user, PackageInfo packageInfo) {
         this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 46b1f6e..6d88530 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
     /** @hide */
     private static final String CHROMIUM_WEBVIEW_FACTORY =
-            "com.android.webview.chromium.WebViewChromiumFactoryProviderForP";
+            "com.android.webview.chromium.WebViewChromiumFactoryProviderForQ";
 
     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 299798b..c6343a8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -611,6 +611,7 @@
     <protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
 
     <!-- Added in Q -->
+    <protected-broadcast android:name="android.content.pm.action.SESSION_UPDATED" />
 
     <!-- For CarIdlenessTracker -->
     <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" />
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index fc813d2..4de25f9 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -932,10 +932,6 @@
     fDL->drawAnnotation(rect, key, val);
 }
 
-void RecordingCanvas::onDrawTextRSXform(const void* text, size_t bytes, const SkRSXform xform[],
-                                        const SkRect* cull, const SkPaint& paint) {
-    fDL->drawTextRSXform(text, bytes, xform, cull, paint);
-}
 void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                      const SkPaint& paint) {
     fDL->drawTextBlob(blob, x, y, paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 22b3a63..ae3c4f05 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -173,8 +173,6 @@
     void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
     void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
 
-    void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
-                           const SkPaint&) override;
     void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
 
     void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 2b5d580..0eb526a 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -82,11 +82,6 @@
         mOutput << mIdent << "drawDRRect" << std::endl;
     }
 
-    void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
-                           const SkPaint&) override {
-        mOutput << mIdent << "drawTextRSXform" << std::endl;
-    }
-
     void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override {
         mOutput << mIdent << "drawTextBlob" << std::endl;
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index f2906de..ff87313 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -30,7 +30,7 @@
 
 class SkiaPipeline : public renderthread::IRenderPipeline {
 public:
-    SkiaPipeline(renderthread::RenderThread& thread);
+    explicit SkiaPipeline(renderthread::RenderThread& thread);
     virtual ~SkiaPipeline();
 
     TaskManager* getTaskManager() override;
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
index daa4c18..dc8420f 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -23,7 +23,7 @@
 
 class SkiaProfileRenderer : public IProfileRenderer {
 public:
-    SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
+    explicit SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
 
     void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
     void drawRects(const float* rects, int count, const SkPaint& paint) override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 02874c7..53ffc44 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -25,7 +25,7 @@
 
 class SkiaVulkanPipeline : public SkiaPipeline {
 public:
-    SkiaVulkanPipeline(renderthread::RenderThread& thread);
+    explicit SkiaVulkanPipeline(renderthread::RenderThread& thread);
     virtual ~SkiaVulkanPipeline() {}
 
     renderthread::MakeCurrentResult makeCurrent() override;
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
index 74e48ce..5e892aa 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
@@ -62,8 +62,8 @@
 public:
     enum class StorageMode { allowSharedSurface, disallowSharedSurface };
 
-    VectorDrawableAtlas(size_t surfaceArea,
-                        StorageMode storageMode = StorageMode::allowSharedSurface);
+    explicit VectorDrawableAtlas(size_t surfaceArea,
+                                 StorageMode storageMode = StorageMode::allowSharedSurface);
 
     /**
      * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 146662b..1723c2e 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -30,10 +30,6 @@
     void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
         ADD_FAILURE() << "onDrawDRRect not expected in this test";
     }
-    void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
-                           const SkRect* cullRect, const SkPaint& paint) {
-        ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
-    }
     void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) {
         ADD_FAILURE() << "onDrawTextBlob not expected in this test";
     }
@@ -116,4 +112,4 @@
 
     int mDrawCounter = 0;  // counts how may draw calls of any kind were made to this canvas
 };
-}
\ No newline at end of file
+}
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index c56f689..ee1e33c 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -40,7 +40,7 @@
 class IncidentReportArgs : public Parcelable {
 public:
     IncidentReportArgs();
-    explicit IncidentReportArgs(const IncidentReportArgs& that);
+    IncidentReportArgs(const IncidentReportArgs& that);
     virtual ~IncidentReportArgs();
 
     virtual status_t writeToParcel(Parcel* out) const;
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index d400159..305862a 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -165,8 +165,12 @@
 
     private void bindChartDetailsView(PreferenceViewHolder holder) {
         final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
-        detailsView.setText(mDetailsId);
-        detailsView.setOnClickListener(mDetailsOnClickListener);
+        if (mDetailsId == 0) {
+            detailsView.setVisibility(View.GONE);
+        } else {
+            detailsView.setText(mDetailsId);
+            detailsView.setOnClickListener(mDetailsOnClickListener);
+        }
     }
 
     private void updateBarChart(PreferenceViewHolder holder) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
index d4e7481..375b45c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -41,6 +41,7 @@
     private BarView mBarView2;
     private BarView mBarView3;
     private BarView mBarView4;
+    private TextView mDetailsView;
     private PreferenceViewHolder mHolder;
     private BarChartPreference mPreference;
 
@@ -51,14 +52,13 @@
         mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView);
         mPreference = new BarChartPreference(mContext, null /* attrs */);
         mPreference.setBarChartTitle(R.string.debug_app);
-        mPreference.setBarChartDetails(R.string.debug_app);
-
 
         mIcon = mContext.getDrawable(R.drawable.ic_menu);
         mBarView1 = (BarView) mBarChartView.findViewById(R.id.bar_view1);
         mBarView2 = (BarView) mBarChartView.findViewById(R.id.bar_view2);
         mBarView3 = (BarView) mBarChartView.findViewById(R.id.bar_view3);
         mBarView4 = (BarView) mBarChartView.findViewById(R.id.bar_view4);
+        mDetailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
     }
 
     @Test
@@ -73,26 +73,31 @@
     }
 
     @Test
-    public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
-        final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+    public void onBindViewHolder_notSetDetailsRes_barChartDetailsViewIsGone() {
+        // We don't call BarChartPreference#setBarChartDetails
+        mPreference.onBindViewHolder(mHolder);
 
+        assertThat(mDetailsView.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
         mPreference.setBarChartDetails(R.string.debug_app);
         mPreference.onBindViewHolder(mHolder);
 
-        assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(detailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+        assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mDetailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
     }
 
     @Test
     public void setBarChartDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
-        final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
-
+        mPreference.setBarChartDetails(R.string.debug_app);
         mPreference.setBarChartDetailsClickListener(v -> {
         });
         mPreference.onBindViewHolder(mHolder);
 
-        assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(detailsView.hasOnClickListeners()).isTrue();
+        assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mDetailsView.hasOnClickListeners()).isTrue();
     }
 
     @Test
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 443e389..f51004a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -531,10 +531,18 @@
         sDependency.destroyDependency(cls, destroy);
     }
 
+    /**
+     * @deprecated see docs/dagger.md
+     */
+    @Deprecated
     public static <T> T get(Class<T> cls) {
         return sDependency.getDependency(cls);
     }
 
+    /**
+     * @deprecated see docs/dagger.md
+     */
+    @Deprecated
     public static <T> T get(DependencyKey<T> cls) {
         return sDependency.getDependency(cls);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 93130d4..bedba9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,7 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.app.Fragment;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -46,10 +46,11 @@
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.LifecycleFragment;
 
 import javax.inject.Inject;
 
-public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
+public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks {
     private static final String TAG = "QS";
     private static final boolean DEBUG = false;
     private static final String EXTRA_EXPANDED = "expanded";
@@ -81,9 +82,12 @@
 
     @Inject
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
-            InjectionInflationController injectionInflater) {
+            InjectionInflationController injectionInflater,
+            Context context) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mInjectionInflater = injectionInflater;
+        SysUiServiceProvider.getComponent(context, CommandQueue.class)
+                .observe(getLifecycle(), this);
     }
 
     @Override
@@ -118,13 +122,6 @@
                 mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
             }
         }
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
-    }
-
-    @Override
-    public void onDestroyView() {
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this);
-        super.onDestroyView();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 017cda7..017a9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -25,7 +25,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -44,6 +43,8 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import dagger.Lazy;
+
 /**
  * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
  * on their group structure. For example, if a notification becomes bundled with another,
@@ -60,20 +61,15 @@
             mTmpChildOrderMap = new HashMap<>();
 
     // Dependencies:
-    protected final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    protected final NotificationGroupManager mGroupManager =
-            Dependency.get(NotificationGroupManager.class);
-    protected final VisualStabilityManager mVisualStabilityManager =
-            Dependency.get(VisualStabilityManager.class);
-    private final StatusBarStateController mStatusBarStateController =
-            Dependency.get(StatusBarStateController.class);
-    private final NotificationEntryManager mEntryManager =
-            Dependency.get(NotificationEntryManager.class);
-    private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+    protected final NotificationLockscreenUserManager mLockscreenUserManager;
+    protected final NotificationGroupManager mGroupManager;
+    protected final VisualStabilityManager mVisualStabilityManager;
+    private final StatusBarStateController mStatusBarStateController;
+    private final NotificationEntryManager mEntryManager;
+    private final BubbleController mBubbleController;
 
     // Lazy
-    private ShadeController mShadeController;
+    private final Lazy<ShadeController> mShadeController;
 
     /**
      * {@code true} if notifications not part of a group should by default be rendered in their
@@ -120,20 +116,27 @@
         }
     }
 
-    private ShadeController getShadeController() {
-        if (mShadeController == null) {
-            mShadeController = Dependency.get(ShadeController.class);
-        }
-        return mShadeController;
-    }
-
     @Inject
-    public NotificationViewHierarchyManager(Context context) {
+    public NotificationViewHierarchyManager(Context context,
+            NotificationLockscreenUserManager notificationLockscreenUserManager,
+            NotificationGroupManager groupManager,
+            VisualStabilityManager visualStabilityManager,
+            StatusBarStateController statusBarStateController,
+            NotificationEntryManager notificationEntryManager,
+            BubbleController bubbleController,
+            Lazy<ShadeController> shadeController) {
+        mLockscreenUserManager = notificationLockscreenUserManager;
+        mGroupManager = groupManager;
+        mVisualStabilityManager = visualStabilityManager;
+        mStatusBarStateController = statusBarStateController;
+        mEntryManager = notificationEntryManager;
+        mBubbleController = bubbleController;
+        mShadeController = shadeController;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
         mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
-        Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -396,7 +399,7 @@
                         && !row.isLowPriority()));
             }
 
-            entry.getRow().setOnAmbient(getShadeController().isDozing());
+            entry.getRow().setOnAmbient(mShadeController.get().isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.isRowRemoved();
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 6a7983a..8c17922 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -276,7 +276,9 @@
 
         @Override
         public void onBackButtonVisibilityChanged(boolean visible) {
-            getBackButton().setVisibility(visible ? VISIBLE : GONE);
+            if (!inScreenPinning()) {
+                getBackButton().setVisibility(visible ? VISIBLE : GONE);
+            }
         }
 
         @Override
@@ -940,6 +942,9 @@
     public void showPinningEnterExitToast(boolean entering) {
         if (entering) {
             mScreenPinningNotify.showPinningStartToast();
+
+            // TODO(b/112934365): remove after prototype finished, only needed to escape from pin
+            getBackButton().setVisibility(VISIBLE);
         } else {
             mScreenPinningNotify.showPinningExitToast();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index 2305db0..fd030d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -19,7 +19,6 @@
 import android.os.Handler;
 import android.util.ArrayMap;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -49,12 +48,24 @@
     public static final int SORT_ORDER_DEFAULT = 4;
 
     private final Context mDefaultContext;
+    private final LeakDetector mLeakDetector;
+    private final PluginManager mPluginManager;
+    private final TunerService mTunerService;
+    private final ConfigurationController mConfigurationController;
 
     /**
      */
     @Inject
-    public ExtensionControllerImpl(Context context) {
+    public ExtensionControllerImpl(Context context,
+            LeakDetector leakDetector,
+            PluginManager pluginManager,
+            TunerService tunerService,
+            ConfigurationController configurationController) {
         mDefaultContext = context;
+        mLeakDetector = leakDetector;
+        mPluginManager = pluginManager;
+        mTunerService = tunerService;
+        mConfigurationController = configurationController;
     }
 
     @Override
@@ -168,14 +179,14 @@
         @Override
         public void clearItem(boolean isDestroyed) {
             if (isDestroyed && mItem != null) {
-                Dependency.get(LeakDetector.class).trackGarbage(mItem);
+                mLeakDetector.trackGarbage(mItem);
             }
             mItem = null;
         }
 
         private void notifyChanged() {
             if (mItem != null) {
-                Dependency.get(LeakDetector.class).trackGarbage(mItem);
+                mLeakDetector.trackGarbage(mItem);
             }
             mItem = null;
             for (int i = 0; i < mProducers.size(); i++) {
@@ -216,7 +227,7 @@
 
             public PluginItem(String action, Class<P> cls, PluginConverter<T, P> converter) {
                 mConverter = converter;
-                Dependency.get(PluginManager.class).addPluginListener(action, this, cls);
+                mPluginManager.addPluginListener(action, this, cls);
             }
 
             @Override
@@ -244,7 +255,7 @@
 
             @Override
             public void destroy() {
-                Dependency.get(PluginManager.class).removePluginListener(this);
+                mPluginManager.removePluginListener(this);
             }
 
             @Override
@@ -260,7 +271,7 @@
 
             public TunerItem(TunerFactory<T> factory, String... setting) {
                 mFactory = factory;
-                Dependency.get(TunerService.class).addTunable(this, setting);
+                mTunerService.addTunable(this, setting);
             }
 
             @Override
@@ -270,7 +281,7 @@
 
             @Override
             public void destroy() {
-                Dependency.get(TunerService.class).removeTunable(this);
+                mTunerService.removeTunable(this);
             }
 
             @Override
@@ -298,7 +309,7 @@
                 mSupplier = supplier;
                 mUiMode = mDefaultContext.getResources().getConfiguration().uiMode
                         & Configuration.UI_MODE_TYPE_MASK;
-                Dependency.get(ConfigurationController.class).addCallback(this);
+                mConfigurationController.addCallback(this);
             }
 
             @Override
@@ -318,7 +329,7 @@
 
             @Override
             public void destroy() {
-                Dependency.get(ConfigurationController.class).removeCallback(this);
+                mConfigurationController.removeCallback(this);
             }
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 7198165..29c42d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -117,8 +117,8 @@
     @Override
     public boolean areNotificationsHiddenInShade() {
         if (mZenMode != Global.ZEN_MODE_OFF) {
-            return (mConfig.suppressedVisualEffects & NotificationManager.Policy
-                    .SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+            return (mConsolidatedNotificationPolicy.suppressedVisualEffects
+                    & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
         }
         return false;
     }
@@ -260,7 +260,6 @@
         }
     }
 
-
     @VisibleForTesting
     protected void updateZenModeConfig() {
         final ZenModeConfig config = mNoMan.getZenModeConfig();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a86970b..32bc01c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -550,7 +550,8 @@
     }
 
     private boolean updateZenConfig() {
-        final NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
+        final NotificationManager.Policy policy =
+                mNotificationManager.getConsolidatedNotificationPolicy();
         boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy
                 .PRIORITY_CATEGORY_ALARMS) == 0;
         boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index ab508a2..45e49df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -125,6 +125,7 @@
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
         return new QSFragment(new RemoteInputQuickSettingsDisabler(context),
-                new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()));
+                new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+                context);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 520a927..8cf4b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -35,6 +36,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
@@ -90,7 +92,10 @@
 
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
-        mViewHierarchyManager = new NotificationViewHierarchyManager(mContext);
+        mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
+                mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+                mock(StatusBarStateController.class), mEntryManager, mock(BubbleController.class),
+                () -> mShadeController);
         Dependency.get(InitController.class).executePostInitTasks();
         mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index 1cceefa..2553ac1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -28,7 +28,6 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.Plugin;
@@ -39,6 +38,7 @@
 import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.LeakDetector;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,7 +62,8 @@
         mPluginManager = mDependency.injectMockDependency(PluginManager.class);
         mTunerService = mDependency.injectMockDependency(TunerService.class);
         mConfigurationController = mDependency.injectMockDependency(ConfigurationController.class);
-        mExtensionController = Dependency.get(ExtensionController.class);
+        mExtensionController = new ExtensionControllerImpl(mContext,
+                mock(LeakDetector.class), mPluginManager, mTunerService, mConfigurationController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 7437e834..8f5f072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -89,20 +89,22 @@
 
     @Test
     public void testAreNotificationsHiddenInShade_zenOnShadeNotSuppressed() {
-        mConfig.suppressedVisualEffects =
-                NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
+                NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR);
+        when(mNm.getConsolidatedNotificationPolicy()).thenReturn(policy);
+        mController.updateConsolidatedNotificationPolicy();
         mController.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mController.updateZenModeConfig();
 
         assertFalse(mController.areNotificationsHiddenInShade());
     }
 
     @Test
     public void testAreNotificationsHiddenInShade_zenOnShadeSuppressed() {
-        mConfig.suppressedVisualEffects =
-                NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
+                NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+        when(mNm.getConsolidatedNotificationPolicy()).thenReturn(policy);
+        mController.updateConsolidatedNotificationPolicy();
         mController.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mController.updateZenModeConfig();
 
         assertTrue(mController.areNotificationsHiddenInShade());
     }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 529d78f..3180b47 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6664,6 +6664,72 @@
     // OS: Q
     NOTIFICATION_ACTION_IS_SMART = 1601;
 
+    // FIELD: true if the associated ACTION_ZEN_ALLOW_* or ACTION_ZEN_BLOCK_* allows/blocks
+    // the effect/sound when DND is on.  false if set to disallow/show.
+    // OS: Q
+    FIELD_ZEN_TOGGLE_EXCEPTION = 1602;
+
+    // FIELD: rule id an ACTION_ZEN_ALLOW_* or ACTION_ZEN_BLOCK_* is associated with
+    // OS: Q
+    FIELD_ZEN_RULE_ID = 1603;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_SETTINGS = 1604;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_SOUND_SETTINGS = 1605;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_DEFAULT_SETTINGS = 1606;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_CUSTOM_SETTINGS = 1607;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_NOTIFICATION_RESTRICTIONS = 1608;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_VIS_EFFECTS = 1609;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom > Allow messages
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_MESSAGES = 1610;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom > Allow calls
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_CALLS = 1611;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Click footer link if custom settings applied
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_SETTINGS_DIALOG = 1612;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index ac17d87..121a830 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -87,6 +87,10 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.deviceidle.ConstraintController;
+import com.android.server.deviceidle.DeviceIdleConstraintTracker;
+import com.android.server.deviceidle.IDeviceIdleConstraint;
+import com.android.server.deviceidle.TvConstraintController;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -104,6 +108,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.stream.Collectors;
 
 /**
  * Keeps track of device idleness and drives low power mode based on that.
@@ -296,6 +301,17 @@
     private Location mLastGpsLocation;
     // Current locked state of the screen
     private boolean mScreenLocked;
+    private int mNumBlockingConstraints = 0;
+
+    /**
+     * Constraints are the "handbrakes" that stop the device from moving into a lower state until
+     * every one is released at the same time.
+     *
+     * @see #registerDeviceIdleConstraintInternal(IDeviceIdleConstraint, String, int)
+     */
+    private final ArrayMap<IDeviceIdleConstraint, DeviceIdleConstraintTracker>
+            mConstraints = new ArrayMap<>();
+    private ConstraintController mConstraintController;
 
     /** Device is currently active. */
     @VisibleForTesting
@@ -703,8 +719,7 @@
      * global Settings. Any access to this class or its fields should be done while
      * holding the DeviceIdleController lock.
      */
-    @VisibleForTesting
-    final class Constants extends ContentObserver {
+    public final class Constants extends ContentObserver {
         // Key names stored in the settings value.
         private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
                 = "light_after_inactive_to";
@@ -1228,6 +1243,7 @@
     private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
     private static final int MSG_FINISH_IDLE_OP = 8;
     private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
+    private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
 
     final class MyHandler extends Handler {
         MyHandler(Looper looper) {
@@ -1348,6 +1364,15 @@
                     final boolean added = (msg.arg2 == 1);
                     mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added);
                 } break;
+                case MSG_SEND_CONSTRAINT_MONITORING: {
+                    final IDeviceIdleConstraint constraint = (IDeviceIdleConstraint) msg.obj;
+                    final boolean monitoring = (msg.arg1 == 1);
+                    if (monitoring) {
+                        constraint.startMonitoring();
+                    } else {
+                        constraint.stopMonitoring();
+                    }
+                } break;
             }
         }
     }
@@ -1512,6 +1537,25 @@
     }
 
     public class LocalService {
+        public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) {
+            synchronized (DeviceIdleController.this) {
+                onConstraintStateChangedLocked(constraint, active);
+            }
+        }
+
+        public void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name,
+                @IDeviceIdleConstraint.MinimumState int minState) {
+            registerDeviceIdleConstraintInternal(constraint, name, minState);
+        }
+
+        public void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint) {
+            unregisterDeviceIdleConstraintInternal(constraint);
+        }
+
+        public void exitIdle(String reason) {
+            exitIdleInternal(reason);
+        }
+
         // duration in milliseconds
         public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
                 long duration, int userId, boolean sync, String reason) {
@@ -1612,6 +1656,23 @@
         PowerManager getPowerManager() {
             return mContext.getSystemService(PowerManager.class);
         }
+
+        SensorManager getSensorManager() {
+            return mContext.getSystemService(SensorManager.class);
+        }
+
+        ConstraintController getConstraintController(Handler handler, LocalService localService) {
+            if (mContext.getPackageManager()
+                    .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
+                return new TvConstraintController(mContext, handler);
+            }
+            return null;
+        }
+
+        boolean useMotionSensor() {
+            return mContext.getResources().getBoolean(
+                   com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
+        }
     }
 
     private final Injector mInjector;
@@ -1636,9 +1697,7 @@
         mHandler = mInjector.getHandler(this);
         mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
         LocalServices.addService(AppStateTracker.class, mAppStateTracker);
-
-        mUseMotionSensor = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
+        mUseMotionSensor = mInjector.useMotionSensor();
     }
 
     public DeviceIdleController(Context context) {
@@ -1738,7 +1797,7 @@
                 mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                         ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                 mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
-                mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
+                mSensorManager = mInjector.getSensorManager();
 
                 if (mUseMotionSensor) {
                     int sigMotionSensorId = getContext().getResources().getInteger(
@@ -1767,6 +1826,12 @@
                         .setNumUpdates(1);
                 }
 
+                mConstraintController = mInjector.getConstraintController(
+                        mHandler, getLocalService(LocalService.class));
+                if (mConstraintController != null) {
+                    mConstraintController.start();
+                }
+
                 float angleThreshold = getContext().getResources().getInteger(
                         com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
                 mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this,
@@ -1822,6 +1887,99 @@
         }
     }
 
+    @VisibleForTesting
+    boolean hasMotionSensor() {
+        return mUseMotionSensor && mMotionSensor != null;
+    }
+
+    private void registerDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint,
+            final String name, final int type) {
+        final int minState;
+        switch (type) {
+            case IDeviceIdleConstraint.ACTIVE:
+                minState = STATE_ACTIVE;
+                break;
+            case IDeviceIdleConstraint.SENSING_OR_ABOVE:
+                minState = STATE_SENSING;
+                break;
+            default:
+                Slog.wtf(TAG, "Registering device-idle constraint with invalid type: " + type);
+                return;
+        }
+        synchronized (this) {
+            if (mConstraints.containsKey(constraint)) {
+                Slog.e(TAG, "Re-registering device-idle constraint: " + constraint + ".");
+                return;
+            }
+            DeviceIdleConstraintTracker tracker = new DeviceIdleConstraintTracker(name, minState);
+            mConstraints.put(constraint, tracker);
+            updateActiveConstraintsLocked();
+        }
+    }
+
+    private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) {
+        synchronized (this) {
+            // Artifically force the constraint to inactive to unblock anything waiting for it.
+            onConstraintStateChangedLocked(constraint, /* active= */ false);
+
+            // Let the constraint know that we are not listening to it any more.
+            setConstraintMonitoringLocked(constraint, /* monitoring= */ false);
+            mConstraints.remove(constraint);
+        }
+    }
+
+    @GuardedBy("this")
+    private void onConstraintStateChangedLocked(IDeviceIdleConstraint constraint, boolean active) {
+        DeviceIdleConstraintTracker tracker = mConstraints.get(constraint);
+        if (tracker == null) {
+            Slog.e(TAG, "device-idle constraint " + constraint + " has not been registered.");
+            return;
+        }
+        if (active != tracker.active && tracker.monitoring) {
+            tracker.active = active;
+            mNumBlockingConstraints += (tracker.active ? +1 : -1);
+            if (mNumBlockingConstraints == 0) {
+                if (mState == STATE_ACTIVE) {
+                    becomeInactiveIfAppropriateLocked();
+                } else if (mNextAlarmTime == 0 || mNextAlarmTime < SystemClock.elapsedRealtime()) {
+                    stepIdleStateLocked("s:" + tracker.name);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("this")
+    private void setConstraintMonitoringLocked(IDeviceIdleConstraint constraint, boolean monitor) {
+        DeviceIdleConstraintTracker tracker = mConstraints.get(constraint);
+        if (tracker.monitoring != monitor) {
+            tracker.monitoring = monitor;
+            updateActiveConstraintsLocked();
+            // We send the callback on a separate thread instead of just relying on oneway as
+            // the client could be in the system server with us and cause re-entry problems.
+            mHandler.obtainMessage(MSG_SEND_CONSTRAINT_MONITORING,
+                    /* monitoring= */ monitor ? 1 : 0,
+                    /* <not used>= */ -1,
+                    /* constraint= */ constraint).sendToTarget();
+        }
+    }
+
+    @GuardedBy("this")
+    private void updateActiveConstraintsLocked() {
+        mNumBlockingConstraints = 0;
+        for (int i = 0; i < mConstraints.size(); i++) {
+            final IDeviceIdleConstraint constraint = mConstraints.keyAt(i);
+            final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i);
+            final boolean monitoring = (tracker.minState == mState);
+            if (monitoring != tracker.monitoring) {
+                setConstraintMonitoringLocked(constraint, monitoring);
+                tracker.active = monitoring;
+            }
+            if (tracker.monitoring && tracker.active) {
+                mNumBlockingConstraints++;
+            }
+        }
+    }
+
     public boolean addPowerSaveWhitelistAppInternal(String name) {
         synchronized (this) {
             try {
@@ -2452,6 +2610,7 @@
         cancelLocatingLocked();
         stopMonitoringMotionLocked();
         mAnyMotionDetector.stop();
+        updateActiveConstraintsLocked();
     }
 
     private void resetLightIdleManagementLocked() {
@@ -2587,40 +2746,50 @@
             return;
         }
 
+        if (mNumBlockingConstraints != 0 && !mForceIdle) {
+            // We have some constraints from other parts of the system server preventing
+            // us from moving to the next state.
+            if (DEBUG) {
+                Slog.i(TAG, "Cannot step idle state. Blocked by: " + mConstraints.values().stream()
+                        .filter(x -> x.active)
+                        .map(x -> x.name)
+                        .collect(Collectors.joining(",")));
+            }
+            return;
+        }
+
         switch (mState) {
             case STATE_INACTIVE:
                 // We have now been inactive long enough, it is time to start looking
                 // for motion and sleep some more while doing so.
                 startMonitoringMotionLocked();
                 scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
-                mState = STATE_IDLE_PENDING;
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_IDLE_PENDING, reason);
                 break;
             case STATE_IDLE_PENDING:
-                mState = STATE_SENSING;
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_SENSING, reason);
                 cancelLocatingLocked();
                 mLocated = false;
                 mLastGenericLocation = null;
                 mLastGpsLocation = null;
+                updateActiveConstraintsLocked();
 
-                // If we have an accelerometer, wait to find out whether we are moving.
+                // Wait for open constraints and an accelerometer reading before moving on.
                 if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {
                     scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
                     mNotMoving = false;
                     mAnyMotionDetector.checkForAnyMotion();
                     break;
+                } else if (mNumBlockingConstraints != 0) {
+                    cancelAlarmLocked();
+                    break;
                 }
 
                 mNotMoving = true;
                 // Otherwise, fall through and check this off the list of requirements.
             case STATE_SENSING:
                 cancelSensingTimeoutAlarmLocked();
-                mState = STATE_LOCATING;
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_LOCATING, reason);
                 scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
                 LocationManager locationManager = mInjector.getLocationManager();
                 if (locationManager != null
@@ -2669,12 +2838,11 @@
                 if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
                     mNextIdleDelay = mConstants.IDLE_TIMEOUT;
                 }
-                mState = STATE_IDLE;
+                moveToStateLocked(STATE_IDLE, reason);
                 if (mLightState != LIGHT_STATE_OVERRIDE) {
                     mLightState = LIGHT_STATE_OVERRIDE;
                     cancelLightAlarmLocked();
                 }
-                EventLogTags.writeDeviceIdle(mState, reason);
                 addEvent(EVENT_DEEP_IDLE, null);
                 mGoingIdleWakeLock.acquire();
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
@@ -2692,14 +2860,24 @@
                 if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
                     mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
                 }
-                mState = STATE_IDLE_MAINTENANCE;
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
                 addEvent(EVENT_DEEP_MAINTENANCE, null);
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                 break;
         }
     }
 
+    private void moveToStateLocked(int state, String reason) {
+        final int oldState = mState;
+        mState = state;
+        if (DEBUG) {
+            Slog.d(TAG, String.format("Moved from STATE_%s to STATE_%s.",
+                    stateToString(oldState), stateToString(mState)));
+        }
+        EventLogTags.writeDeviceIdle(mState, reason);
+        updateActiveConstraintsLocked();
+    }
+
     void incActiveIdleOps() {
         synchronized (this) {
             mActiveIdleOpCount++;
@@ -2822,6 +3000,7 @@
             mMaintenanceStartTime = 0;
             EventLogTags.writeDeviceIdle(mState, type);
             becomeInactive = true;
+            updateActiveConstraintsLocked();
         }
         if (mLightState == LIGHT_STATE_OVERRIDE) {
             // We went out of light idle mode because we had started deep idle mode...  let's
@@ -3798,8 +3977,22 @@
             pw.print("  mScreenLocked="); pw.println(mScreenLocked);
             pw.print("  mNetworkConnected="); pw.println(mNetworkConnected);
             pw.print("  mCharging="); pw.println(mCharging);
-            pw.print("  mMotionActive="); pw.println(mMotionListener.active);
+            if (mConstraints.size() != 0) {
+                pw.println("  mConstraints={");
+                for (int i = 0; i < mConstraints.size(); i++) {
+                    final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i);
+                    pw.print("    \""); pw.print(tracker.name); pw.print("\"=");
+                    if (tracker.minState == mState) {
+                        pw.println(tracker.active);
+                    } else {
+                        pw.print("ignored <mMinState="); pw.print(stateToString(tracker.minState));
+                        pw.println(">");
+                    }
+                }
+                pw.println("  }");
+            }
             if (mUseMotionSensor) {
+                pw.print("  mMotionActive="); pw.println(mMotionListener.active);
                 pw.print("  mNotMoving="); pw.println(mNotMoving);
             }
             pw.print("  mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
diff --git a/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
new file mode 100644
index 0000000..cc319bf
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.DeviceIdleController;
+
+/**
+ * Track whether there are any active Bluetooth devices connected.
+ */
+public class BluetoothConstraint implements IDeviceIdleConstraint {
+    private static final String TAG = BluetoothConstraint.class.getSimpleName();
+    private static final long INACTIVITY_TIMEOUT_MS = 20 * 60 * 1000L;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final DeviceIdleController.LocalService mLocalService;
+    private final BluetoothManager mBluetoothManager;
+
+    private volatile boolean mConnected = true;
+    private volatile boolean mMonitoring = false;
+
+    public BluetoothConstraint(
+            Context context, Handler handler, DeviceIdleController.LocalService localService) {
+        mContext = context;
+        mHandler = handler;
+        mLocalService = localService;
+        mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
+    }
+
+    @Override
+    public synchronized void startMonitoring() {
+        // Start by assuming we have a connected bluetooth device.
+        mConnected = true;
+        mMonitoring = true;
+
+        // Register a receiver to get updates on bluetooth devices disconnecting or the
+        // adapter state changing.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+
+        // Some devices will try to stay connected indefinitely. Set a timeout to ignore them.
+        mHandler.sendMessageDelayed(
+                Message.obtain(mHandler, mTimeoutCallback), INACTIVITY_TIMEOUT_MS);
+
+        // Now we have the receiver registered, make a direct check for connected devices.
+        updateAndReportActiveLocked();
+    }
+
+    @Override
+    public synchronized void stopMonitoring() {
+        mContext.unregisterReceiver(mReceiver);
+        mHandler.removeCallbacks(mTimeoutCallback);
+        mMonitoring = false;
+    }
+
+    private synchronized void cancelMonitoringDueToTimeout() {
+        if (mMonitoring) {
+            mMonitoring = false;
+            mLocalService.onConstraintStateChanged(this, /* active= */ false);
+        }
+    }
+
+    /**
+     * Check the latest data from BluetoothManager and let DeviceIdleController know whether we
+     * have connected devices (for example TV remotes / gamepads) and thus want to stay awake.
+     */
+    @GuardedBy("this")
+    private void updateAndReportActiveLocked() {
+        final boolean connected = isBluetoothConnected(mBluetoothManager);
+        if (connected != mConnected) {
+            mConnected = connected;
+            // If we lost all of our connections, we are on track to going into idle state.
+            mLocalService.onConstraintStateChanged(this, /* active= */ mConnected);
+        }
+    }
+
+    /**
+     * True if the bluetooth adapter exists, is enabled, and has at least one GATT device connected.
+     */
+    @VisibleForTesting
+    static boolean isBluetoothConnected(BluetoothManager bluetoothManager) {
+        BluetoothAdapter adapter = bluetoothManager.getAdapter();
+        if (adapter != null && adapter.isEnabled()) {
+            return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT).size() > 0;
+        }
+        return false;
+    }
+
+    /**
+     * Registered in {@link #startMonitoring()}, unregistered in {@link #stopMonitoring()}.
+     */
+    @VisibleForTesting
+    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(intent.getAction())) {
+                mLocalService.exitIdle("bluetooth");
+            } else {
+                updateAndReportActiveLocked();
+            }
+        }
+    };
+
+    private final Runnable mTimeoutCallback = () -> cancelMonitoringDueToTimeout();
+}
diff --git a/services/core/java/com/android/server/deviceidle/ConstraintController.java b/services/core/java/com/android/server/deviceidle/ConstraintController.java
new file mode 100644
index 0000000..6d52f71
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/ConstraintController.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+/**
+ * Device idle constraints for a specific form factor or use-case.
+ */
+public interface ConstraintController {
+    /**
+     * Begin any general continuing work and register all constraints.
+     */
+    void start();
+
+    /**
+     * Unregister all constraints and stop any general work.
+     */
+    void stop();
+}
diff --git a/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java
new file mode 100644
index 0000000..4d5760e
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+/**
+ * Current state of an {@link IDeviceIdleConstraint}.
+ *
+ * If the current doze state is between leastActive and mostActive, then startMonitoring() will
+ * be the most recent call. Otherwise, stopMonitoring() is the most recent call.
+ */
+public class DeviceIdleConstraintTracker {
+
+    /**
+     * Appears in "dumpsys deviceidle".
+     */
+    public final String name;
+
+    /**
+     * Whenever a constraint is active, it will keep the device at or above
+     * minState (provided the rule is currently in effect).
+     *
+     */
+    public final int minState;
+
+    /**
+     * Whether this constraint currently prevents going below {@link #minState}.
+     *
+     * When the state is set to exactly minState, active is automatically
+     * overwritten with {@code true}.
+     */
+    public boolean active = false;
+
+    /**
+     * Internal tracking for whether the {@link IDeviceIdleConstraint} on the other
+     * side has been told it needs to send updates.
+     */
+    public boolean monitoring = false;
+
+    public DeviceIdleConstraintTracker(final String name, int minState) {
+        this.name = name;
+        this.minState = minState;
+    }
+}
diff --git a/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java
new file mode 100644
index 0000000..f1f95730
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Implemented by OEM and/or Form Factor. System ones are built into the
+ * image regardless of build flavour but may still be switched off at run time.
+ * Individual feature flags at build time control which are used. We may
+ * also explore a local override for quick testing.
+ */
+public interface IDeviceIdleConstraint {
+
+    /**
+     * A state for this constraint to block descent from.
+     *
+     * <p>These states are a subset of the states in DeviceIdleController that make sense for
+     * constraints to be able to block on. For example, {@link #SENSING_OR_ABOVE} clearly has
+     * defined "above" and "below" states. However, a hypothetical {@code QUICK_DOZE_OR_ABOVE}
+     * state would not have clear semantics as to what transitions should be blocked and which
+     * should be allowed.
+     */
+    @IntDef(flag = false, value = {
+            ACTIVE,
+            SENSING_OR_ABOVE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface MinimumState {}
+
+    int ACTIVE = 0;
+    int SENSING_OR_ABOVE = 1;
+
+    /**
+     * Begin tracking events for this constraint.
+     *
+     * <p>The device idle controller has reached a point where it is waiting for the all-clear
+     * from this tracker (possibly among others) in order to continue with progression into
+     * idle state. It will not proceed until one of the following happens:
+     * <ul>
+     *   <li>The constraint reports inactive with {@code .setActive(false)}.</li>
+     *   <li>The constraint is unregistered with {@code .unregisterDeviceIdleConstraint(this)}.</li>
+     *   <li>A transition timeout in DeviceIdleController fires.
+     * </ul>
+     */
+    void startMonitoring();
+
+    /** Stop checking for new events and do not call into LocalService with updates any more. */
+    void stopMonitoring();
+}
diff --git a/services/core/java/com/android/server/deviceidle/TvConstraintController.java b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
new file mode 100644
index 0000000..2d472de6
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
+
+/**
+ * Device idle constraints for television devices.
+ *
+ * <p>Televisions are devices with {@code FEATURE_LEANBACK_ONLY}. Other devices might support
+ * some kind of leanback mode but they should not follow the same rules for idle state.
+ */
+public class TvConstraintController implements ConstraintController {
+    private final Context mContext;
+    private final Handler mHandler;
+    private final DeviceIdleController.LocalService mDeviceIdleService;
+
+    @Nullable
+    private final BluetoothConstraint mBluetoothConstraint;
+
+    public TvConstraintController(Context context, Handler handler) {
+        mContext = context;
+        mHandler = handler;
+        mDeviceIdleService = LocalServices.getService(DeviceIdleController.LocalService.class);
+
+        final PackageManager pm = context.getPackageManager();
+        mBluetoothConstraint = pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                ? new BluetoothConstraint(mContext, mHandler, mDeviceIdleService)
+                : null;
+    }
+
+    @Override
+    public void start() {
+        if (mBluetoothConstraint != null) {
+            mDeviceIdleService.registerDeviceIdleConstraint(
+                    mBluetoothConstraint, "bluetooth", IDeviceIdleConstraint.SENSING_OR_ABOVE);
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (mBluetoothConstraint != null) {
+            mDeviceIdleService.unregisterDeviceIdleConstraint(mBluetoothConstraint);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 8e3f351..918dc07 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -38,11 +38,11 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.textservice.SpellCheckerInfo;
-import android.view.textservice.TextServicesManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.StartInputFlags;
+import com.android.server.textservices.TextServicesManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -641,7 +641,7 @@
         }
         // Only the current spell checker should be treated as an enabled one.
         final SpellCheckerInfo currentSpellChecker =
-                TextServicesManager.getInstance().getCurrentSpellChecker();
+                TextServicesManagerInternal.get().getCurrentSpellCheckerForUser(userId);
         for (final String packageName : systemImesDisabledUntilUsed) {
             if (DEBUG) {
                 Slog.d(TAG, "check " + packageName);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f01d343..e5b1878 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -558,9 +558,7 @@
         rule.conditionId = automaticZenRule.getConditionId();
         rule.enabled = automaticZenRule.isEnabled();
         rule.modified = automaticZenRule.isModified();
-        if (automaticZenRule.getZenPolicy() != null) {
-            rule.zenPolicy = automaticZenRule.getZenPolicy();
-        }
+        rule.zenPolicy = automaticZenRule.getZenPolicy();
         rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                 automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0d0db94..35ffe8d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1065,6 +1065,7 @@
         if (isStaged()) {
             // STOPSHIP: implement staged sessions
             mStagedSessionReady = true;
+            mPm.sendSessionUpdatedBroadcast(generateInfo(), userId);
             dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
             return;
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9f1b922..af12633 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -19629,6 +19629,17 @@
         return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
     }
 
+    /**
+     * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing
+     * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}.
+     */
+    public void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo,
+            int userId) {
+        Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED)
+                .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo);
+        mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
+    }
+
     public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
         UserManagerService ums = UserManagerService.getInstance();
         if (ums != null) {
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
new file mode 100644
index 0000000..56bcdd9
--- /dev/null
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.textservices;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.view.textservice.SpellCheckerInfo;
+
+import com.android.server.LocalServices;
+
+/**
+ * Local interface of {@link TextServicesManagerService} inside system server process.
+ */
+public abstract class TextServicesManagerInternal {
+    /**
+     * Returns the list of installed input methods for the specified user.
+     *
+     * <p>CAVEAT: This method is not fully implemented yet. This may return an empty list if
+     * {@code userId} for a background user is specified. Check the implementation before starting
+     * this method.</p>
+     *
+     * @param userId The user ID to be queried.
+     * @return {@link SpellCheckerInfo} that is currently selected {@code userId}.
+     */
+    @Nullable
+    public abstract SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId);
+
+    /**
+     * Fake implementation of {@link TextServicesManagerInternal}.  All the methods do nothing.
+     */
+    private static final TextServicesManagerInternal NOP =
+            new TextServicesManagerInternal() {
+                @Override
+                public SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
+                    return null;
+                }
+            };
+
+    /**
+     * @return Global instance if exists.  Otherwise, a dummy no-op instance.
+     */
+    @NonNull
+    public static TextServicesManagerInternal get() {
+        final TextServicesManagerInternal instance =
+                LocalServices.getService(TextServicesManagerInternal.class);
+        return instance != null ? instance : NOP;
+    }
+}
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
index 97f238a..65d5b10 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -56,6 +56,7 @@
 import com.android.internal.textservice.ITextServicesManager;
 import com.android.internal.textservice.ITextServicesSessionListener;
 import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -278,6 +279,14 @@
 
         @Override
         public void onStart() {
+            LocalServices.addService(TextServicesManagerInternal.class,
+                    new TextServicesManagerInternal() {
+                        @Override
+                        public SpellCheckerInfo getCurrentSpellCheckerForUser(
+                                @UserIdInt int userId) {
+                            return mService.getCurrentSpellCheckerForUser(userId);
+                        }
+                    });
             publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService);
         }
 
@@ -493,6 +502,15 @@
         return spellCheckerList.get(0);
     }
 
+    @Nullable
+    private SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(userId);
+            final TextServicesData data = mUserData.get(spellCheckerOwnerUserId);
+            return data != null ? data.getCurrentSpellChecker() : null;
+        }
+    }
+
     // TODO: Save SpellCheckerService by supported languages. Currently only one spell
     // checker is saved.
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4d8440a8..2cd0168 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2486,36 +2486,20 @@
     }
 
     void setRequestedOrientation(int requestedOrientation) {
-        final int displayId = getDisplayId();
-        final Configuration displayConfig =
-                mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
-
-        final Configuration config = setOrientation(requestedOrientation,
-                displayId, displayConfig, mayFreezeScreenLocked(app));
-        if (config != null) {
-            frozenBeforeDestroy = true;
-            if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this,
-                    false /* deferResume */, displayId)) {
-                mRootActivityContainer.resumeFocusedStacksTopActivities();
-            }
-        }
+        setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
         mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                 task.taskId, requestedOrientation);
     }
 
-    Configuration setOrientation(int requestedOrientation, int displayId,
-            Configuration displayConfig, boolean freezeScreenIfNeeded) {
+    private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
         if (mAppWindowToken == null) {
             Slog.w(TAG_WM,
                     "Attempted to set orientation of non-existing app token: " + appToken);
-            return null;
+            return;
         }
 
-        mAppWindowToken.setOrientation(requestedOrientation);
-
         final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
-        return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
-                displayId);
+        mAppWindowToken.setOrientation(requestedOrientation, binder, this);
     }
 
     int getOrientation() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1943efc..a5ceee2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -397,10 +397,11 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final Region mTmpRegion = new Region();
 
-
     /** Used for handing back size of display */
     private final Rect mTmpBounds = new Rect();
 
+    private final Configuration mTmpConfiguration = new Configuration();
+
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
@@ -1156,6 +1157,36 @@
         mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
     }
 
+    @Override
+    boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+            ConfigurationContainer requestingContainer) {
+        final Configuration config = updateOrientationFromAppTokens(
+                getRequestedOverrideConfiguration(), freezeDisplayToken, false);
+        // If display rotation class tells us that it doesn't consider app requested orientation,
+        // this display won't rotate just because of an app changes its requested orientation. Thus
+        // it indicates that this display chooses not to handle this request.
+        final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+        if (config == null) {
+            return handled;
+        }
+
+        if (handled && requestingContainer instanceof ActivityRecord) {
+            final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
+            final boolean kept = mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+                    config, activityRecord, false /* deferResume */, getDisplayId());
+            activityRecord.frozenBeforeDestroy = true;
+            if (!kept) {
+                mWmService.mAtmService.mRootActivityContainer.resumeFocusedStacksTopActivities();
+            }
+        } else {
+            // We have a new configuration to push so we need to update ATMS for now.
+            // TODO: Clean up display configuration push between ATMS and WMS after unification.
+            mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+                    config, null /* starting */, false /* deferResume */, getDisplayId());
+        }
+        return handled;
+    }
+
     /**
      * Determine the new desired orientation of this display.
      *
@@ -1169,7 +1200,56 @@
         return updateOrientationFromAppTokens(false /* forceUpdate */);
     }
 
-    boolean updateOrientationFromAppTokens(boolean forceUpdate) {
+    /**
+     * Update orientation of the target display, returning a non-null new Configuration if it has
+     * changed from the current orientation. If a non-null configuration is returned, someone must
+     * call {@link WindowManagerService#setNewDisplayOverrideConfiguration(Configuration,
+     * DisplayContent)} to tell the window manager it can unfreeze the screen. This will typically
+     * be done by calling {@link WindowManagerService#sendNewConfiguration(int)}.
+     */
+    Configuration updateOrientationFromAppTokens(Configuration currentConfig,
+            IBinder freezeDisplayToken, boolean forceUpdate) {
+        if (!mDisplayReady) {
+            return null;
+        }
+
+        Configuration config = null;
+        if (updateOrientationFromAppTokens(forceUpdate)) {
+            // If we changed the orientation but mOrientationChangeComplete is already true,
+            // we used seamless rotation, and we don't need to freeze the screen.
+            if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) {
+                final AppWindowToken atoken = getAppWindowToken(freezeDisplayToken);
+                if (atoken != null) {
+                    atoken.startFreezingScreen();
+                }
+            }
+            config = new Configuration();
+            computeScreenConfiguration(config);
+        } else if (currentConfig != null) {
+            // No obvious action we need to take, but if our current state mismatches the
+            // activity manager's, update it, disregarding font scale, which should remain set
+            // to the value of the previous configuration.
+            // Here we're calling Configuration#unset() instead of setToDefaults() because we
+            // need to keep override configs clear of non-empty values (e.g. fontSize).
+            mTmpConfiguration.unset();
+            mTmpConfiguration.updateFrom(currentConfig);
+            computeScreenConfiguration(mTmpConfiguration);
+            if (currentConfig.diff(mTmpConfiguration) != 0) {
+                mWaitingForConfig = true;
+                setLayoutNeeded();
+                int[] anim = new int[2];
+                getDisplayPolicy().selectRotationAnimationLw(anim);
+
+                mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
+                config = new Configuration(mTmpConfiguration);
+            }
+        }
+
+        return config;
+    }
+
+
+    private boolean updateOrientationFromAppTokens(boolean forceUpdate) {
         final int req = getOrientation();
         if (req != mLastOrientation || forceUpdate) {
             mLastOrientation = req;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 7aabc15..bcc7be4 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -329,6 +329,15 @@
         return mFixedToUserRotation;
     }
 
+    /**
+     * Returns {@code true} if this display rotation takes app requested orientation into
+     * consideration; {@code false} otherwise. For the time being the only case where this is {@code
+     * false} is when {@link #isFixedToUserRotation()} is {@code true}.
+     */
+    boolean respectAppRequestedOrientation() {
+        return !mFixedToUserRotation;
+    }
+
     public int getLandscapeRotation() {
         return mLandscapeRotation;
     }
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d0937e9..6f92e64 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -597,11 +597,15 @@
 
         // Force-update the orientation from the WindowManager, since we need the true configuration
         // to send to the client now.
-        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                getDisplayOverrideConfiguration(displayId),
-                starting != null && starting.mayFreezeScreenLocked(starting.app)
-                        ? starting.appToken : null,
-                displayId, true /* forceUpdate */);
+        final DisplayContent displayContent = mRootWindowContainer.getDisplayContent(displayId);
+        Configuration config = null;
+        if (displayContent != null) {
+            config = displayContent.updateOrientationFromAppTokens(
+                    getDisplayOverrideConfiguration(displayId),
+                    starting != null && starting.mayFreezeScreenLocked(starting.app)
+                            ? starting.appToken : null,
+                    true /* forceUpdate */);
+        }
         if (starting != null && markFrozenIfConfigChanged && config != null) {
             starting.frozenBeforeDestroy = true;
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b10fd31..d334bd2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -44,6 +44,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -328,6 +329,23 @@
         return boundsChange;
     }
 
+    @Override
+    public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+            ConfigurationContainer requestingContainer) {
+        if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
+            return true;
+        }
+
+        // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill
+        // it if possible.
+        // TODO: Move to TaskRecord after unification is done.
+        if (mTaskRecord != null) {
+            mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration());
+            return true;
+        }
+        return false;
+    }
+
     void resize(boolean relayout, boolean forced) {
         if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
             getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 651089d..32c5a3b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -31,10 +31,12 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.Pools;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -698,8 +700,58 @@
         }
     }
 
+    /**
+     * Called when this container or one of its descendants changed its requested orientation, and
+     * wants this container to handle it or pass it to its parent.
+     *
+     * @param freezeDisplayToken freeze this app window token if display needs to freeze
+     * @param requestingContainer the container which orientation request has changed
+     * @return {@code true} if handled; {@code false} otherwise.
+     */
+    boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
+            @Nullable ConfigurationContainer requestingContainer) {
+        final WindowContainer parent = getParent();
+        if (parent == null) {
+            return false;
+        }
+        return parent.onDescendantOrientationChanged(freezeDisplayToken,
+                requestingContainer);
+    }
+
+    /**
+     * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
+     * parameters.
+     *
+     * @param orientation the specified orientation.
+     */
     void setOrientation(int orientation) {
+        setOrientation(orientation, null /* freezeDisplayToken */,
+                null /* ActivityRecord */);
+    }
+
+    /**
+     * Sets the specified orientation of this container. It percolates this change upward along the
+     * hierarchy to let each level of the hierarchy a chance to respond to it.
+     *
+     * @param orientation the specified orientation. Needs to be one of {@link
+     *      android.content.pm.ActivityInfo.ScreenOrientation}.
+     * @param freezeDisplayToken uses this token to freeze display if orientation change is not
+     *                           done. Display will not be frozen if this is {@code null}, which
+     *                           should only happen in tests.
+     * @param requestingContainer the container which orientation request has changed. Mostly used
+     *                            to ensure it gets correct configuration.
+     */
+    void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
+            @Nullable ConfigurationContainer requestingContainer) {
+        final boolean changed = mOrientation != orientation;
         mOrientation = orientation;
+        if (!changed) {
+            return;
+        }
+        final WindowContainer parent = getParent();
+        if (parent != null) {
+            onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+        }
     }
 
     int getOrientation() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e3ced83..b6a4a51 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2383,85 +2383,6 @@
         }
     }
 
-    @Override
-    public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId) {
-        return updateOrientationFromAppTokens(currentConfig, freezeThisOneIfNeeded, displayId,
-                false /* forceUpdate */);
-    }
-
-    public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
-        if (!checkCallingPermission(MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) {
-            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
-        }
-
-        final Configuration config;
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
-                        displayId, forceUpdate);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-
-        return config;
-    }
-
-    /**
-     * Update orientation of the target display, returning a non-null new Configuration if it has
-     * changed from the current orientation. If a non-null configuration is returned, someone must
-     * call {@link #setNewDisplayOverrideConfiguration(Configuration, int)} to tell the window
-     * manager it can unfreeze the screen. This will typically be done by calling
-     * {@link #sendNewConfiguration(int)}.
-     *
-     * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
-     */
-    private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
-        if (!mDisplayReady) {
-            return null;
-        }
-        Configuration config = null;
-
-        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-        if (dc != null && dc.updateOrientationFromAppTokens(forceUpdate)) {
-            // If we changed the orientation but mOrientationChangeComplete is already true,
-            // we used seamless rotation, and we don't need to freeze the screen.
-            if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
-                final AppWindowToken atoken = mRoot.getAppWindowToken(freezeThisOneIfNeeded);
-                if (atoken != null) {
-                    atoken.startFreezingScreen();
-                }
-            }
-            config = computeNewConfigurationLocked(displayId);
-
-        } else if (currentConfig != null) {
-            // No obvious action we need to take, but if our current state mismatches the activity
-            // manager's, update it, disregarding font scale, which should remain set to the value
-            // of the previous configuration.
-            // Here we're calling Configuration#unset() instead of setToDefaults() because we need
-            // to keep override configs clear of non-empty values (e.g. fontSize).
-            mTempConfiguration.unset();
-            mTempConfiguration.updateFrom(currentConfig);
-            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            displayContent.computeScreenConfiguration(mTempConfiguration);
-            if (currentConfig.diff(mTempConfiguration) != 0) {
-                displayContent.mWaitingForConfig = true;
-                displayContent.setLayoutNeeded();
-                int anim[] = new int[2];
-                displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
-
-                startFreezingDisplayLocked(anim[0], anim[1], displayContent);
-                config = new Configuration(mTempConfiguration);
-            }
-        }
-
-        return config;
-    }
-
     void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
             @NonNull DisplayContent dc) {
         if (dc.mWaitingForConfig) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 7ff10bf..1a16e56 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -50,6 +50,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
@@ -57,6 +58,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.location.LocationManager;
 import android.location.LocationProvider;
@@ -71,6 +73,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.deviceidle.ConstraintController;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -78,6 +81,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
@@ -110,10 +114,13 @@
     private PowerManager.WakeLock mWakeLock;
     @Mock
     private PowerManagerInternal mPowerManagerInternal;
+    @Mock
+    private SensorManager mSensorManager;
 
     class InjectorForTest extends DeviceIdleController.Injector {
         ConnectivityService connectivityService;
         LocationManager locationManager;
+        ConstraintController constraintController;
 
         InjectorForTest(Context ctx) {
             super(ctx);
@@ -147,20 +154,36 @@
 
         @Override
         DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
-            return mock(DeviceIdleController.MyHandler.class);
+            return mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
         }
 
         @Override
         PowerManager getPowerManager() {
             return mPowerManager;
         }
+
+        @Override
+        SensorManager getSensorManager() {
+            return mSensorManager;
+        }
+
+        @Override
+        ConstraintController getConstraintController(
+                Handler handler, DeviceIdleController.LocalService localService) {
+            return constraintController;
+        }
+
+        @Override
+        boolean useMotionSensor() {
+            return true;
+        }
     }
 
     private class AnyMotionDetectorForTest extends AnyMotionDetector {
         boolean isMonitoring = false;
 
         AnyMotionDetectorForTest() {
-            super(mPowerManager, mock(Handler.class), mock(SensorManager.class),
+            super(mPowerManager, mock(Handler.class), mSensorManager,
                     mock(DeviceIdleCallback.class), 0.5f);
         }
 
@@ -201,7 +224,7 @@
         mMockingSession = mockitoSession()
                 .initMocks(this)
                 .strictness(Strictness.LENIENT)
-                .mockStatic(LocalServices.class)
+                .spyStatic(LocalServices.class)
                 .startMocking();
         spyOn(getContext());
         doReturn(null).when(getContext()).registerReceiver(any(), any());
@@ -218,6 +241,9 @@
         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
         doNothing().when(mWakeLock).acquire();
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
+        doReturn(mock(Sensor.class)).when(mSensorManager)
+                .getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true));
+        doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
         mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
         mAnyMotionDetector = new AnyMotionDetectorForTest();
         mInjector = new InjectorForTest(getContext());
@@ -240,9 +266,10 @@
         if (mMockingSession != null) {
             mMockingSession.finishMocking();
         }
-        // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove
-        // it after each test, otherwise, subsequent tests will fail.
+        // DeviceIdleController adds these to LocalServices in the constructor, so we have to remove
+        // them after each test, otherwise, subsequent tests will fail.
         LocalServices.removeServiceForTest(AppStateTracker.class);
+        LocalServices.removeServiceForTest(DeviceIdleController.LocalService.class);
     }
 
     @Test
@@ -1486,27 +1513,36 @@
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_IDLE_PENDING:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mDeviceIdleController.mMotionListener.isActive());
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_SENSING:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
-                assertTrue(mAnyMotionDetector.isMonitoring);
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mDeviceIdleController.mMotionListener.isActive());
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_LOCATING:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
-                assertTrue(mAnyMotionDetector.isMonitoring);
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mDeviceIdleController.mMotionListener.isActive());
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_IDLE:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive()
+                if (mDeviceIdleController.hasMotionSensor()) {
+                    assertTrue(mDeviceIdleController.mMotionListener.isActive()
                         // If quick doze is enabled, the motion listener should NOT be active.
                         || mDeviceIdleController.isQuickDozeEnabled());
+                }
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
@@ -1514,9 +1550,11 @@
                 verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
                 break;
             case STATE_IDLE_MAINTENANCE:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive()
+                if (mDeviceIdleController.hasMotionSensor()) {
+                    assertTrue(mDeviceIdleController.mMotionListener.isActive()
                         // If quick doze is enabled, the motion listener should NOT be active.
                         || mDeviceIdleController.isQuickDozeEnabled());
+                }
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
diff --git a/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
new file mode 100644
index 0000000..f74ac1f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.deviceidle;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.DeviceIdleController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link com.android.server.deviceidle.BluetoothConstraint}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BluetoothConstraintTest {
+
+    private MockitoSession mMockingSession;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Handler mHandler;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private BluetoothManager mBluetoothManager;
+
+    @Mock
+    private DeviceIdleController.LocalService mDeviceIdleService;
+
+    private BluetoothConstraint mConstraint;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        doReturn(mBluetoothManager)
+                .when(mContext).getSystemService(BluetoothManager.class);
+        mConstraint = new BluetoothConstraint(mContext, mHandler, mDeviceIdleService);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    @Test
+    public void testIsConnected_noBluetoothAdapter() {
+        doReturn(null).when(mBluetoothManager).getAdapter();
+        assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+    }
+
+    @Test
+    public void testIsConnected_noConnectedDevice() {
+        enableBluetooth(true);
+        assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+    }
+
+    @Test
+    public void testIsConnected_twoConnectedDevices() {
+        enableBluetooth(true, mock(BluetoothDevice.class), mock(BluetoothDevice.class));
+        assertTrue(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+    }
+
+    @Test
+    public void testStartMonitoring_updatesActiveAtCorrectTimes() {
+        // First setup -> no callbacks should fire.
+        BluetoothConstraint constraint = mConstraint;
+        verify(mDeviceIdleService, never()).onConstraintStateChanged(any(), anyBoolean());
+        verify(mContext, never()).registerReceiver(eq(constraint.mReceiver), any());
+
+        InOrder order = inOrder(mDeviceIdleService);
+
+        // No devices -> active=false should be triggered.
+        enableBluetooth(true);
+        constraint.startMonitoring();
+        order.verify(mDeviceIdleService, times(1)).onConstraintStateChanged(any(), eq(false));
+
+        // One device -> active=true should be triggered.
+        enableBluetooth(true, mock(BluetoothDevice.class));
+        constraint.mReceiver.onReceive(
+                mContext, new Intent(BluetoothDevice.ACTION_ACL_CONNECTED));
+        constraint.startMonitoring();
+        order.verify(mDeviceIdleService, times(1)).exitIdle(eq("bluetooth"));
+
+        // Stop monitoring -> broadcast receiver should be unregistered.
+        constraint.stopMonitoring();
+        verify(mContext, times(1)).unregisterReceiver(eq(constraint.mReceiver));
+        order.verifyNoMoreInteractions();
+
+    }
+
+    private void enableBluetooth(boolean enabled, BluetoothDevice... devices) {
+        when(mBluetoothManager.getAdapter().isEnabled()).thenReturn(enabled);
+        when(mBluetoothManager.getConnectedDevices(eq(BluetoothProfile.GATT)))
+                .thenReturn(Arrays.asList(devices));
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index dc3287e..a459b0a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -61,6 +61,7 @@
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.service.notification.ZenPolicy;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -683,6 +684,92 @@
     }
 
     @Test
+    public void testWriteXmlWithZenPolicy() throws Exception {
+        final String ruleId = "customRule";
+        setupZenConfig();
+
+        // one enabled automatic rule with zen policy
+        ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo customRuleInfo = new ScheduleInfo();
+        customRule.enabled = true;
+        customRule.creationTime = 0;
+        customRule.id = "customRule";
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+        customRule.configurationActivity =
+                new ComponentName("android", "ScheduleConditionProvider");
+        customRule.pkg = customRule.configurationActivity.getPackageName();
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowAlarms(false)
+                .allowMedia(false)
+                .allowRepeatCallers(false)
+                .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS)
+                .allowEvents(true)
+                .allowReminders(false)
+                .build();
+        automaticRules.put("customRule", customRule);
+        mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+        ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, null);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, false);
+
+        ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
+        ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId);
+
+        assertEquals("Automatic rules mismatch", original, current);
+    }
+
+    @Test
+    public void testReadXmlRestoreWithZenPolicy() throws Exception {
+        final String ruleId = "customRule";
+        setupZenConfig();
+
+        // one enabled automatic rule with zen policy
+        ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo customRuleInfo = new ScheduleInfo();
+        customRule.enabled = true;
+        customRule.creationTime = 0;
+        customRule.id = ruleId;
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+        customRule.configurationActivity =
+                new ComponentName("android", "ScheduleConditionProvider");
+        customRule.pkg = customRule.configurationActivity.getPackageName();
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowSystem(true)
+                .allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .allowReminders(true)
+                .build();
+        automaticRules.put(ruleId, customRule);
+        mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+        ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, null);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, true);
+
+        ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
+        ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId);
+
+        assertEquals("Automatic rules mismatch", original, current);
+    }
+
+    @Test
     public void testReadXmlRulesNotOverriden() throws Exception {
         setupZenConfig();
 
@@ -896,6 +983,10 @@
         customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowReminders(true)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .build();
         automaticRules.put("customRule", customRule);
 
         ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -943,6 +1034,10 @@
         customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowReminders(true)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .build();
         automaticRules.put("customRule", customRule);
 
         ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -953,6 +1048,10 @@
         defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultScheduleRuleInfo);
         defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
+        defaultScheduleRule.zenPolicy = new ZenPolicy.Builder()
+                .allowEvents(true)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .build();
         automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule);
 
         ZenModeConfig.ZenRule defaultEventRule = new ZenModeConfig.ZenRule();
@@ -963,6 +1062,11 @@
         defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultEventRuleInfo);
         defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
+        defaultScheduleRule.zenPolicy = new ZenPolicy.Builder()
+                .allowAlarms(false)
+                .allowMedia(false)
+                .allowRepeatCallers(false)
+                .build();
         automaticRules.put(ZenModeConfig.EVENTS_DEFAULT_RULE_ID, defaultEventRule);
 
         mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 92b4dbb..bc62de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -155,15 +155,17 @@
 
         // Set initial orientation and update.
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
-                null, mDisplayContent.getDisplayId());
+        mDisplayContent.updateOrientationFromAppTokens(
+                mDisplayContent.getRequestedOverrideConfiguration(),
+                null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.mResizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
         mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
-        mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
-                null, mDisplayContent.getDisplayId());
+        mDisplayContent.updateOrientationFromAppTokens(
+                mDisplayContent.getRequestedOverrideConfiguration(),
+                null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
         mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
         assertTrue(appWindow.mResizeReported);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 8430616..3826fac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -31,7 +31,13 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -60,9 +66,11 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -475,6 +483,13 @@
     @SuppressLint("InlinedApi")
     public void testOrientationDefinedByKeyguard() {
         final DisplayContent dc = createNewDisplay();
+
+        // When display content is created its configuration is not yet initialized, which could
+        // cause unnecessary configuration propagation, so initialize it here.
+        final Configuration config = new Configuration();
+        dc.computeScreenConfiguration(config);
+        dc.onRequestedOverrideConfigurationChanged(config);
+
         // Create a window that requests landscape orientation. It will define device orientation
         // by default.
         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
@@ -567,6 +582,52 @@
         assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
     }
 
+    @Test
+    public void testOnDescendantOrientationRequestChanged() {
+        final DisplayContent dc = createNewDisplay();
+        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+                ? SCREEN_ORIENTATION_PORTRAIT
+                : SCREEN_ORIENTATION_LANDSCAPE;
+
+        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+        window.mAppToken.setOrientation(newOrientation);
+
+        ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+        assertTrue("Display should rotate to handle orientation request by default.",
+                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+
+        final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
+        verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
+                same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+        final Configuration newDisplayConfig = captor.getValue();
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+    }
+
+    @Test
+    public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
+        final DisplayContent dc = createNewDisplay();
+        dc.getDisplayRotation().setFixedToUserRotation(true);
+        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+                ? SCREEN_ORIENTATION_PORTRAIT
+                : SCREEN_ORIENTATION_LANDSCAPE;
+
+        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+        window.mAppToken.setOrientation(newOrientation);
+
+        ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+        assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
+                        + " user rotation.",
+                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+        verify(mWm.mAtmService, never()).updateDisplayOverrideConfigurationLocked(any(),
+                eq(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index bf4b52e..6b31e6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -33,6 +33,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.ContentResolver;
@@ -234,7 +235,7 @@
     }
 
     @Test
-    public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+    public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()
             throws Exception {
         mBuilder.build();
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
@@ -604,6 +605,26 @@
                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
     }
 
+    // ========================
+    // Non-rotation API Tests
+    // ========================
+    @Test
+    public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+        mBuilder.build();
+
+        assertTrue("Display rotation should respect app requested orientation by"
+                + " default.", mTarget.respectAppRequestedOrientation());
+    }
+
+    @Test
+    public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+        mBuilder.build();
+        mTarget.setFixedToUserRotation(true);
+
+        assertFalse("Display rotation shouldn't respect app requested orientation if"
+                + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+    }
+
     /**
      * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
      * according to given parameters.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 60f957f..e156143 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -38,8 +39,10 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -559,8 +562,7 @@
         builder.setLayer(2).setIsVisible(true);
         final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent =
                 visibleUnspecifiedRootChild.addChildWindow(builder);
-        visibleUnspecifiedRootChildChildFillsParent.setOrientation(
-                SCREEN_ORIENTATION_PORTRAIT);
+        visibleUnspecifiedRootChildChildFillsParent.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(SCREEN_ORIENTATION_PORTRAIT,
                 visibleUnspecifiedRootChildChildFillsParent.getOrientation());
         assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
@@ -724,6 +726,19 @@
         verify(grandChild, times(1)).onParentResize();
     }
 
+    @Test
+    public void testOnDescendantOrientationRequestChangedPropagation() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+        final TestWindowContainer root = spy(builder.build());
+
+        final IBinder binder = mock(IBinder.class);
+        final ActivityRecord activityRecord = mock(ActivityRecord.class);
+        final TestWindowContainer child = root.addChildWindow();
+
+        child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord);
+        verify(root).onDescendantOrientationChanged(binder, activityRecord);
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 369a002..4c2a984 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -16,16 +16,16 @@
 
 package com.android.framework.permission.tests;
 
-import android.content.res.Configuration;
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.IWindowManager;
-import junit.framework.TestCase;
 
-import static android.view.Display.DEFAULT_DISPLAY;
+import junit.framework.TestCase;
 
 /**
  * TODO: Remove this. This is only a placeholder, need to implement this.
@@ -73,17 +73,6 @@
         }
 
         try {
-            mWm.updateOrientationFromAppTokens(new Configuration(),
-                    null /* freezeThisOneIfNeeded */, DEFAULT_DISPLAY);
-            fail("IWindowManager.updateOrientationFromAppTokens did not throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        } catch (RemoteException e) {
-            fail("Unexpected remote exception");
-        }
-
-        try {
             mWm.prepareAppTransition(0, false);
             fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
                     + " expected");
diff --git a/tools/incident_report/printer.h b/tools/incident_report/printer.h
index ed93fa1..63e276b 100644
--- a/tools/incident_report/printer.h
+++ b/tools/incident_report/printer.h
@@ -22,7 +22,7 @@
 class Out
 {
 public:
-    Out(int fd);
+    explicit Out(int fd);
     ~Out();
 
     void printf(const char* format, ...);